966 lines
25 KiB
JavaScript
966 lines
25 KiB
JavaScript
/* eslint-disable */
|
|
// Improved version of https://github.com/Yaffle/EventSource/
|
|
// Available under MIT License (MIT)
|
|
// Only tries to support IE11 and nothing below
|
|
import fetch from 'next/dist/build/polyfills/unfetch'
|
|
|
|
var document = window.document
|
|
var Response = window.Response
|
|
var TextDecoder = window.TextDecoder
|
|
var TextEncoder = window.TextEncoder
|
|
var AbortController = window.AbortController
|
|
|
|
if (AbortController == undefined) {
|
|
AbortController = function() {
|
|
this.signal = null
|
|
this.abort = function() {}
|
|
}
|
|
}
|
|
|
|
function TextDecoderPolyfill() {
|
|
this.bitsNeeded = 0
|
|
this.codePoint = 0
|
|
}
|
|
|
|
TextDecoderPolyfill.prototype.decode = function(octets) {
|
|
function valid(codePoint, shift, octetsCount) {
|
|
if (octetsCount === 1) {
|
|
return codePoint >= 0x0080 >> shift && codePoint << shift <= 0x07ff
|
|
}
|
|
if (octetsCount === 2) {
|
|
return (
|
|
(codePoint >= 0x0800 >> shift && codePoint << shift <= 0xd7ff) ||
|
|
(codePoint >= 0xe000 >> shift && codePoint << shift <= 0xffff)
|
|
)
|
|
}
|
|
if (octetsCount === 3) {
|
|
return codePoint >= 0x010000 >> shift && codePoint << shift <= 0x10ffff
|
|
}
|
|
throw new Error()
|
|
}
|
|
function octetsCount(bitsNeeded, codePoint) {
|
|
if (bitsNeeded === 6 * 1) {
|
|
return codePoint >> 6 > 15 ? 3 : codePoint > 31 ? 2 : 1
|
|
}
|
|
if (bitsNeeded === 6 * 2) {
|
|
return codePoint > 15 ? 3 : 2
|
|
}
|
|
if (bitsNeeded === 6 * 3) {
|
|
return 3
|
|
}
|
|
throw new Error()
|
|
}
|
|
var REPLACER = 0xfffd
|
|
var string = ''
|
|
var bitsNeeded = this.bitsNeeded
|
|
var codePoint = this.codePoint
|
|
for (var i = 0; i < octets.length; i += 1) {
|
|
var octet = octets[i]
|
|
if (bitsNeeded !== 0) {
|
|
if (
|
|
octet < 128 ||
|
|
octet > 191 ||
|
|
!valid(
|
|
(codePoint << 6) | (octet & 63),
|
|
bitsNeeded - 6,
|
|
octetsCount(bitsNeeded, codePoint)
|
|
)
|
|
) {
|
|
bitsNeeded = 0
|
|
codePoint = REPLACER
|
|
string += String.fromCharCode(codePoint)
|
|
}
|
|
}
|
|
if (bitsNeeded === 0) {
|
|
if (octet >= 0 && octet <= 127) {
|
|
bitsNeeded = 0
|
|
codePoint = octet
|
|
} else if (octet >= 192 && octet <= 223) {
|
|
bitsNeeded = 6 * 1
|
|
codePoint = octet & 31
|
|
} else if (octet >= 224 && octet <= 239) {
|
|
bitsNeeded = 6 * 2
|
|
codePoint = octet & 15
|
|
} else if (octet >= 240 && octet <= 247) {
|
|
bitsNeeded = 6 * 3
|
|
codePoint = octet & 7
|
|
} else {
|
|
bitsNeeded = 0
|
|
codePoint = REPLACER
|
|
}
|
|
if (
|
|
bitsNeeded !== 0 &&
|
|
!valid(codePoint, bitsNeeded, octetsCount(bitsNeeded, codePoint))
|
|
) {
|
|
bitsNeeded = 0
|
|
codePoint = REPLACER
|
|
}
|
|
} else {
|
|
bitsNeeded -= 6
|
|
codePoint = (codePoint << 6) | (octet & 63)
|
|
}
|
|
if (bitsNeeded === 0) {
|
|
if (codePoint <= 0xffff) {
|
|
string += String.fromCharCode(codePoint)
|
|
} else {
|
|
string += String.fromCharCode(0xd800 + ((codePoint - 0xffff - 1) >> 10))
|
|
string += String.fromCharCode(
|
|
0xdc00 + ((codePoint - 0xffff - 1) & 0x3ff)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
this.bitsNeeded = bitsNeeded
|
|
this.codePoint = codePoint
|
|
return string
|
|
}
|
|
|
|
// Firefox < 38 throws an error with stream option
|
|
var supportsStreamOption = function() {
|
|
try {
|
|
return (
|
|
new TextDecoder().decode(new TextEncoder().encode('test'), {
|
|
stream: true,
|
|
}) === 'test'
|
|
)
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IE, Edge
|
|
if (
|
|
TextDecoder == undefined ||
|
|
TextEncoder == undefined ||
|
|
!supportsStreamOption()
|
|
) {
|
|
TextDecoder = TextDecoderPolyfill
|
|
}
|
|
|
|
var k = function() {}
|
|
|
|
function XHRWrapper(xhr) {
|
|
this.withCredentials = false
|
|
this.responseType = ''
|
|
this.readyState = 0
|
|
this.status = 0
|
|
this.statusText = ''
|
|
this.responseText = ''
|
|
this.onprogress = k
|
|
this.onreadystatechange = k
|
|
this._contentType = ''
|
|
this._xhr = xhr
|
|
this._sendTimeout = 0
|
|
this._abort = k
|
|
}
|
|
|
|
XHRWrapper.prototype.open = function(method, url) {
|
|
this._abort(true)
|
|
|
|
var that = this
|
|
var xhr = this._xhr
|
|
var state = 1
|
|
var timeout = 0
|
|
|
|
this._abort = function(silent) {
|
|
if (that._sendTimeout !== 0) {
|
|
clearTimeout(that._sendTimeout)
|
|
that._sendTimeout = 0
|
|
}
|
|
if (state === 1 || state === 2 || state === 3) {
|
|
state = 4
|
|
xhr.onload = k
|
|
xhr.onerror = k
|
|
xhr.onabort = k
|
|
xhr.onprogress = k
|
|
xhr.onreadystatechange = k
|
|
// IE 8 - 9: XDomainRequest#abort() does not fire any event
|
|
// Opera < 10: XMLHttpRequest#abort() does not fire any event
|
|
xhr.abort()
|
|
if (timeout !== 0) {
|
|
clearTimeout(timeout)
|
|
timeout = 0
|
|
}
|
|
if (!silent) {
|
|
that.readyState = 4
|
|
that.onreadystatechange()
|
|
}
|
|
}
|
|
state = 0
|
|
}
|
|
|
|
var onStart = function() {
|
|
if (state === 1) {
|
|
// state = 2;
|
|
var status = 0
|
|
var statusText = ''
|
|
var contentType = undefined
|
|
if (!('contentType' in xhr)) {
|
|
try {
|
|
status = xhr.status
|
|
statusText = xhr.statusText
|
|
contentType = xhr.getResponseHeader('Content-Type')
|
|
} catch (error) {
|
|
// IE < 10 throws exception for `xhr.status` when xhr.readyState === 2 || xhr.readyState === 3
|
|
// Opera < 11 throws exception for `xhr.status` when xhr.readyState === 2
|
|
// https://bugs.webkit.org/show_bug.cgi?id=29121
|
|
status = 0
|
|
statusText = ''
|
|
contentType = undefined
|
|
// Firefox < 14, Chrome ?, Safari ?
|
|
// https://bugs.webkit.org/show_bug.cgi?id=29658
|
|
// https://bugs.webkit.org/show_bug.cgi?id=77854
|
|
}
|
|
} else {
|
|
status = 200
|
|
statusText = 'OK'
|
|
contentType = xhr.contentType
|
|
}
|
|
if (status !== 0) {
|
|
state = 2
|
|
that.readyState = 2
|
|
that.status = status
|
|
that.statusText = statusText
|
|
that._contentType = contentType
|
|
that.onreadystatechange()
|
|
}
|
|
}
|
|
}
|
|
var onProgress = function() {
|
|
onStart()
|
|
if (state === 2 || state === 3) {
|
|
state = 3
|
|
var responseText = ''
|
|
try {
|
|
responseText = xhr.responseText
|
|
} catch (error) {
|
|
// IE 8 - 9 with XMLHttpRequest
|
|
}
|
|
that.readyState = 3
|
|
that.responseText = responseText
|
|
that.onprogress()
|
|
}
|
|
}
|
|
var onFinish = function() {
|
|
// Firefox 52 fires "readystatechange" (xhr.readyState === 4) without final "readystatechange" (xhr.readyState === 3)
|
|
// IE 8 fires "onload" without "onprogress"
|
|
onProgress()
|
|
if (state === 1 || state === 2 || state === 3) {
|
|
state = 4
|
|
if (timeout !== 0) {
|
|
clearTimeout(timeout)
|
|
timeout = 0
|
|
}
|
|
that.readyState = 4
|
|
that.onreadystatechange()
|
|
}
|
|
}
|
|
var onReadyStateChange = function() {
|
|
if (xhr != undefined) {
|
|
// Opera 12
|
|
if (xhr.readyState === 4) {
|
|
onFinish()
|
|
} else if (xhr.readyState === 3) {
|
|
onProgress()
|
|
} else if (xhr.readyState === 2) {
|
|
onStart()
|
|
}
|
|
}
|
|
}
|
|
var onTimeout = function() {
|
|
timeout = setTimeout(function() {
|
|
onTimeout()
|
|
}, 500)
|
|
if (xhr.readyState === 3) {
|
|
onProgress()
|
|
}
|
|
}
|
|
|
|
// XDomainRequest#abort removes onprogress, onerror, onload
|
|
xhr.onload = onFinish
|
|
xhr.onerror = onFinish
|
|
// improper fix to match Firefox behaviour, but it is better than just ignore abort
|
|
// see https://bugzilla.mozilla.org/show_bug.cgi?id=768596
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=880200
|
|
// https://code.google.com/p/chromium/issues/detail?id=153570
|
|
// IE 8 fires "onload" without "onprogress
|
|
xhr.onabort = onFinish
|
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=736723
|
|
if (
|
|
!('sendAsBinary' in XMLHttpRequest.prototype) &&
|
|
!('mozAnon' in XMLHttpRequest.prototype)
|
|
) {
|
|
xhr.onprogress = onProgress
|
|
}
|
|
|
|
// IE 8 - 9 (XMLHTTPRequest)
|
|
// Opera < 12
|
|
// Firefox < 3.5
|
|
// Firefox 3.5 - 3.6 - ? < 9.0
|
|
// onprogress is not fired sometimes or delayed
|
|
// see also #64
|
|
xhr.onreadystatechange = onReadyStateChange
|
|
|
|
if ('contentType' in xhr) {
|
|
url += (url.indexOf('?') === -1 ? '?' : '&') + 'padding=true'
|
|
}
|
|
xhr.open(method, url, true)
|
|
|
|
if ('readyState' in xhr) {
|
|
// workaround for Opera 12 issue with "progress" events
|
|
// #91
|
|
timeout = setTimeout(function() {
|
|
onTimeout()
|
|
}, 0)
|
|
}
|
|
}
|
|
XHRWrapper.prototype.abort = function() {
|
|
this._abort(false)
|
|
}
|
|
XHRWrapper.prototype.getResponseHeader = function(name) {
|
|
return this._contentType
|
|
}
|
|
XHRWrapper.prototype.setRequestHeader = function(name, value) {
|
|
var xhr = this._xhr
|
|
if ('setRequestHeader' in xhr) {
|
|
xhr.setRequestHeader(name, value)
|
|
}
|
|
}
|
|
XHRWrapper.prototype.getAllResponseHeaders = function() {
|
|
return this._xhr.getAllResponseHeaders != undefined
|
|
? this._xhr.getAllResponseHeaders()
|
|
: ''
|
|
}
|
|
XHRWrapper.prototype.send = function() {
|
|
// loading indicator in Safari < ? (6), Chrome < 14, Firefox
|
|
if (
|
|
!('ontimeout' in XMLHttpRequest.prototype) &&
|
|
document != undefined &&
|
|
document.readyState != undefined &&
|
|
document.readyState !== 'complete'
|
|
) {
|
|
var that = this
|
|
that._sendTimeout = setTimeout(function() {
|
|
that._sendTimeout = 0
|
|
that.send()
|
|
}, 4)
|
|
return
|
|
}
|
|
|
|
var xhr = this._xhr
|
|
// withCredentials should be set after "open" for Safari and Chrome (< 19 ?)
|
|
xhr.withCredentials = this.withCredentials
|
|
xhr.responseType = this.responseType
|
|
try {
|
|
// xhr.send(); throws "Not enough arguments" in Firefox 3.0
|
|
xhr.send(undefined)
|
|
} catch (error1) {
|
|
// Safari 5.1.7, Opera 12
|
|
throw error1
|
|
}
|
|
}
|
|
|
|
function toLowerCase(name) {
|
|
return name.replace(/[A-Z]/g, function(c) {
|
|
return String.fromCharCode(c.charCodeAt(0) + 0x20)
|
|
})
|
|
}
|
|
|
|
function HeadersPolyfill(all) {
|
|
// Get headers: implemented according to mozilla's example code: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#Example
|
|
var map = Object.create(null)
|
|
var array = all.split('\r\n')
|
|
for (var i = 0; i < array.length; i += 1) {
|
|
var line = array[i]
|
|
var parts = line.split(': ')
|
|
var name = parts.shift()
|
|
var value = parts.join(': ')
|
|
map[toLowerCase(name)] = value
|
|
}
|
|
this._map = map
|
|
}
|
|
HeadersPolyfill.prototype.get = function(name) {
|
|
return this._map[toLowerCase(name)]
|
|
}
|
|
|
|
function XHRTransport() {}
|
|
|
|
XHRTransport.prototype.open = function(
|
|
xhr,
|
|
onStartCallback,
|
|
onProgressCallback,
|
|
onFinishCallback,
|
|
url,
|
|
withCredentials,
|
|
headers
|
|
) {
|
|
xhr.open('GET', url)
|
|
var offset = 0
|
|
xhr.onprogress = function() {
|
|
var responseText = xhr.responseText
|
|
var chunk = responseText.slice(offset)
|
|
offset += chunk.length
|
|
onProgressCallback(chunk)
|
|
}
|
|
xhr.onreadystatechange = function() {
|
|
if (xhr.readyState === 2) {
|
|
var status = xhr.status
|
|
var statusText = xhr.statusText
|
|
var contentType = xhr.getResponseHeader('Content-Type')
|
|
var headers = xhr.getAllResponseHeaders()
|
|
onStartCallback(
|
|
status,
|
|
statusText,
|
|
contentType,
|
|
new HeadersPolyfill(headers),
|
|
function() {
|
|
xhr.abort()
|
|
}
|
|
)
|
|
} else if (xhr.readyState === 4) {
|
|
onFinishCallback()
|
|
}
|
|
}
|
|
xhr.withCredentials = withCredentials
|
|
xhr.responseType = 'text'
|
|
for (var name in headers) {
|
|
if (Object.prototype.hasOwnProperty.call(headers, name)) {
|
|
xhr.setRequestHeader(name, headers[name])
|
|
}
|
|
}
|
|
xhr.send()
|
|
}
|
|
|
|
function HeadersWrapper(headers) {
|
|
this._headers = headers
|
|
}
|
|
HeadersWrapper.prototype.get = function(name) {
|
|
return this._headers.get(name)
|
|
}
|
|
|
|
function FetchTransport() {}
|
|
|
|
FetchTransport.prototype.open = function(
|
|
xhr,
|
|
onStartCallback,
|
|
onProgressCallback,
|
|
onFinishCallback,
|
|
url,
|
|
withCredentials,
|
|
headers
|
|
) {
|
|
var controller = new AbortController()
|
|
var signal = controller.signal // see #120
|
|
var textDecoder = new TextDecoder()
|
|
fetch(url, {
|
|
headers: headers,
|
|
credentials: withCredentials ? 'include' : 'same-origin',
|
|
signal: signal,
|
|
cache: 'no-store',
|
|
})
|
|
.then(function(response) {
|
|
var reader = response.body.getReader()
|
|
onStartCallback(
|
|
response.status,
|
|
response.statusText,
|
|
response.headers.get('Content-Type'),
|
|
new HeadersWrapper(response.headers),
|
|
function() {
|
|
controller.abort()
|
|
reader.cancel()
|
|
}
|
|
)
|
|
return new Promise(function(resolve, reject) {
|
|
var readNextChunk = function() {
|
|
reader
|
|
.read()
|
|
.then(function(result) {
|
|
if (result.done) {
|
|
// Note: bytes in textDecoder are ignored
|
|
resolve(undefined)
|
|
} else {
|
|
var chunk = textDecoder.decode(result.value, { stream: true })
|
|
onProgressCallback(chunk)
|
|
readNextChunk()
|
|
}
|
|
})
|
|
['catch'](function(error) {
|
|
reject(error)
|
|
})
|
|
}
|
|
readNextChunk()
|
|
})
|
|
})
|
|
.then(
|
|
function(result) {
|
|
onFinishCallback()
|
|
return result
|
|
},
|
|
function(error) {
|
|
onFinishCallback()
|
|
return Promise.reject(error)
|
|
}
|
|
)
|
|
}
|
|
|
|
function EventTarget() {
|
|
this._listeners = Object.create(null)
|
|
}
|
|
|
|
function throwError(e) {
|
|
setTimeout(function() {
|
|
throw e
|
|
}, 0)
|
|
}
|
|
|
|
EventTarget.prototype.dispatchEvent = function(event) {
|
|
event.target = this
|
|
var typeListeners = this._listeners[event.type]
|
|
if (typeListeners != undefined) {
|
|
var length = typeListeners.length
|
|
for (var i = 0; i < length; i += 1) {
|
|
var listener = typeListeners[i]
|
|
try {
|
|
if (typeof listener.handleEvent === 'function') {
|
|
listener.handleEvent(event)
|
|
} else {
|
|
listener.call(this, event)
|
|
}
|
|
} catch (e) {
|
|
throwError(e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EventTarget.prototype.addEventListener = function(type, listener) {
|
|
type = String(type)
|
|
var listeners = this._listeners
|
|
var typeListeners = listeners[type]
|
|
if (typeListeners == undefined) {
|
|
typeListeners = []
|
|
listeners[type] = typeListeners
|
|
}
|
|
var found = false
|
|
for (var i = 0; i < typeListeners.length; i += 1) {
|
|
if (typeListeners[i] === listener) {
|
|
found = true
|
|
}
|
|
}
|
|
if (!found) {
|
|
typeListeners.push(listener)
|
|
}
|
|
}
|
|
EventTarget.prototype.removeEventListener = function(type, listener) {
|
|
type = String(type)
|
|
var listeners = this._listeners
|
|
var typeListeners = listeners[type]
|
|
if (typeListeners != undefined) {
|
|
var filtered = []
|
|
for (var i = 0; i < typeListeners.length; i += 1) {
|
|
if (typeListeners[i] !== listener) {
|
|
filtered.push(typeListeners[i])
|
|
}
|
|
}
|
|
if (filtered.length === 0) {
|
|
delete listeners[type]
|
|
} else {
|
|
listeners[type] = filtered
|
|
}
|
|
}
|
|
}
|
|
|
|
function Event(type) {
|
|
this.type = type
|
|
this.target = undefined
|
|
}
|
|
|
|
function MessageEvent(type, options) {
|
|
Event.call(this, type)
|
|
this.data = options.data
|
|
this.lastEventId = options.lastEventId
|
|
}
|
|
|
|
MessageEvent.prototype = Object.create(Event.prototype)
|
|
|
|
function ConnectionEvent(type, options) {
|
|
Event.call(this, type)
|
|
this.status = options.status
|
|
this.statusText = options.statusText
|
|
this.headers = options.headers
|
|
}
|
|
|
|
ConnectionEvent.prototype = Object.create(Event.prototype)
|
|
|
|
var WAITING = -1
|
|
var CONNECTING = 0
|
|
var OPEN = 1
|
|
var CLOSED = 2
|
|
|
|
var AFTER_CR = -1
|
|
var FIELD_START = 0
|
|
var FIELD = 1
|
|
var VALUE_START = 2
|
|
var VALUE = 3
|
|
|
|
var contentTypeRegExp = /^text\/event\-stream;?(\s*charset\=utf\-8)?$/i
|
|
|
|
var MINIMUM_DURATION = 1000
|
|
var MAXIMUM_DURATION = 18000000
|
|
|
|
var parseDuration = function(value, def) {
|
|
var n = parseInt(value, 10)
|
|
if (n !== n) {
|
|
n = def
|
|
}
|
|
return clampDuration(n)
|
|
}
|
|
var clampDuration = function(n) {
|
|
return Math.min(Math.max(n, MINIMUM_DURATION), MAXIMUM_DURATION)
|
|
}
|
|
|
|
var fire = function(that, f, event) {
|
|
try {
|
|
if (typeof f === 'function') {
|
|
f.call(that, event)
|
|
}
|
|
} catch (e) {
|
|
throwError(e)
|
|
}
|
|
}
|
|
|
|
function EventSourcePolyfill(url, options) {
|
|
EventTarget.call(this)
|
|
|
|
this.onopen = undefined
|
|
this.onmessage = undefined
|
|
this.onerror = undefined
|
|
|
|
this.url = undefined
|
|
this.readyState = undefined
|
|
this.withCredentials = undefined
|
|
|
|
this._close = undefined
|
|
|
|
start(this, url, options)
|
|
}
|
|
|
|
var isFetchSupported =
|
|
fetch != undefined && Response != undefined && 'body' in Response.prototype
|
|
|
|
function start(es, url, options) {
|
|
url = String(url)
|
|
var withCredentials = options != undefined && Boolean(options.withCredentials)
|
|
|
|
var initialRetry = clampDuration(1000)
|
|
var heartbeatTimeout =
|
|
options != undefined && options.heartbeatTimeout != undefined
|
|
? parseDuration(options.heartbeatTimeout, 45000)
|
|
: clampDuration(45000)
|
|
|
|
var lastEventId = ''
|
|
var retry = initialRetry
|
|
var wasActivity = false
|
|
var headers =
|
|
options != undefined && options.headers != undefined
|
|
? JSON.parse(JSON.stringify(options.headers))
|
|
: undefined
|
|
var CurrentTransport =
|
|
options != undefined && options.Transport != undefined
|
|
? options.Transport
|
|
: XMLHttpRequest
|
|
var xhr =
|
|
isFetchSupported &&
|
|
!(options != undefined && options.Transport != undefined)
|
|
? undefined
|
|
: new XHRWrapper(new CurrentTransport())
|
|
var transport = xhr == undefined ? new FetchTransport() : new XHRTransport()
|
|
var cancelFunction = undefined
|
|
var timeout = 0
|
|
var currentState = WAITING
|
|
var dataBuffer = ''
|
|
var lastEventIdBuffer = ''
|
|
var eventTypeBuffer = ''
|
|
|
|
var textBuffer = ''
|
|
var state = FIELD_START
|
|
var fieldStart = 0
|
|
var valueStart = 0
|
|
|
|
var onStart = function(status, statusText, contentType, headers, cancel) {
|
|
if (currentState === CONNECTING) {
|
|
cancelFunction = cancel
|
|
if (
|
|
status === 200 &&
|
|
contentType != undefined &&
|
|
contentTypeRegExp.test(contentType)
|
|
) {
|
|
currentState = OPEN
|
|
wasActivity = true
|
|
retry = initialRetry
|
|
es.readyState = OPEN
|
|
var event = new ConnectionEvent('open', {
|
|
status: status,
|
|
statusText: statusText,
|
|
headers: headers,
|
|
})
|
|
es.dispatchEvent(event)
|
|
fire(es, es.onopen, event)
|
|
} else {
|
|
var message = ''
|
|
if (status !== 200) {
|
|
if (statusText) {
|
|
statusText = statusText.replace(/\s+/g, ' ')
|
|
}
|
|
message =
|
|
"EventSource's response has a status " +
|
|
status +
|
|
' ' +
|
|
statusText +
|
|
' that is not 200. Aborting the connection.'
|
|
} else {
|
|
message =
|
|
"EventSource's response has a Content-Type specifying an unsupported type: " +
|
|
(contentType == undefined
|
|
? '-'
|
|
: contentType.replace(/\s+/g, ' ')) +
|
|
'. Aborting the connection.'
|
|
}
|
|
throwError(new Error(message))
|
|
close()
|
|
var event = new ConnectionEvent('error', {
|
|
status: status,
|
|
statusText: statusText,
|
|
headers: headers,
|
|
})
|
|
es.dispatchEvent(event)
|
|
fire(es, es.onerror, event)
|
|
}
|
|
}
|
|
}
|
|
|
|
var onProgress = function(textChunk) {
|
|
if (currentState === OPEN) {
|
|
var n = -1
|
|
for (var i = 0; i < textChunk.length; i += 1) {
|
|
var c = textChunk.charCodeAt(i)
|
|
if (c === '\n'.charCodeAt(0) || c === '\r'.charCodeAt(0)) {
|
|
n = i
|
|
}
|
|
}
|
|
var chunk = (n !== -1 ? textBuffer : '') + textChunk.slice(0, n + 1)
|
|
textBuffer = (n === -1 ? textBuffer : '') + textChunk.slice(n + 1)
|
|
if (chunk !== '') {
|
|
wasActivity = true
|
|
}
|
|
for (var position = 0; position < chunk.length; position += 1) {
|
|
var c = chunk.charCodeAt(position)
|
|
if (state === AFTER_CR && c === '\n'.charCodeAt(0)) {
|
|
state = FIELD_START
|
|
} else {
|
|
if (state === AFTER_CR) {
|
|
state = FIELD_START
|
|
}
|
|
if (c === '\r'.charCodeAt(0) || c === '\n'.charCodeAt(0)) {
|
|
if (state !== FIELD_START) {
|
|
if (state === FIELD) {
|
|
valueStart = position + 1
|
|
}
|
|
var field = chunk.slice(fieldStart, valueStart - 1)
|
|
var value = chunk.slice(
|
|
valueStart +
|
|
(valueStart < position &&
|
|
chunk.charCodeAt(valueStart) === ' '.charCodeAt(0)
|
|
? 1
|
|
: 0),
|
|
position
|
|
)
|
|
if (field === 'data') {
|
|
dataBuffer += '\n'
|
|
dataBuffer += value
|
|
} else if (field === 'id') {
|
|
lastEventIdBuffer = value
|
|
} else if (field === 'event') {
|
|
eventTypeBuffer = value
|
|
} else if (field === 'retry') {
|
|
initialRetry = parseDuration(value, initialRetry)
|
|
retry = initialRetry
|
|
} else if (field === 'heartbeatTimeout') {
|
|
heartbeatTimeout = parseDuration(value, heartbeatTimeout)
|
|
if (timeout !== 0) {
|
|
clearTimeout(timeout)
|
|
timeout = setTimeout(function() {
|
|
onTimeout()
|
|
}, heartbeatTimeout)
|
|
}
|
|
}
|
|
}
|
|
if (state === FIELD_START) {
|
|
if (dataBuffer !== '') {
|
|
lastEventId = lastEventIdBuffer
|
|
if (eventTypeBuffer === '') {
|
|
eventTypeBuffer = 'message'
|
|
}
|
|
var event = new MessageEvent(eventTypeBuffer, {
|
|
data: dataBuffer.slice(1),
|
|
lastEventId: lastEventIdBuffer,
|
|
})
|
|
es.dispatchEvent(event)
|
|
if (eventTypeBuffer === 'message') {
|
|
fire(es, es.onmessage, event)
|
|
}
|
|
if (currentState === CLOSED) {
|
|
return
|
|
}
|
|
}
|
|
dataBuffer = ''
|
|
eventTypeBuffer = ''
|
|
}
|
|
state = c === '\r'.charCodeAt(0) ? AFTER_CR : FIELD_START
|
|
} else {
|
|
if (state === FIELD_START) {
|
|
fieldStart = position
|
|
state = FIELD
|
|
}
|
|
if (state === FIELD) {
|
|
if (c === ':'.charCodeAt(0)) {
|
|
valueStart = position + 1
|
|
state = VALUE_START
|
|
}
|
|
} else if (state === VALUE_START) {
|
|
state = VALUE
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var onFinish = function() {
|
|
if (currentState === OPEN || currentState === CONNECTING) {
|
|
currentState = WAITING
|
|
if (timeout !== 0) {
|
|
clearTimeout(timeout)
|
|
timeout = 0
|
|
}
|
|
timeout = setTimeout(function() {
|
|
onTimeout()
|
|
}, retry)
|
|
retry = clampDuration(Math.min(initialRetry * 16, retry * 2))
|
|
|
|
es.readyState = CONNECTING
|
|
var event = new Event('error')
|
|
es.dispatchEvent(event)
|
|
fire(es, es.onerror, event)
|
|
}
|
|
}
|
|
|
|
var close = function() {
|
|
currentState = CLOSED
|
|
if (cancelFunction != undefined) {
|
|
cancelFunction()
|
|
cancelFunction = undefined
|
|
}
|
|
if (timeout !== 0) {
|
|
clearTimeout(timeout)
|
|
timeout = 0
|
|
}
|
|
es.readyState = CLOSED
|
|
}
|
|
|
|
var onTimeout = function() {
|
|
timeout = 0
|
|
|
|
if (currentState !== WAITING) {
|
|
if (!wasActivity && cancelFunction != undefined) {
|
|
throwError(
|
|
new Error(
|
|
'No activity within ' +
|
|
heartbeatTimeout +
|
|
' milliseconds. Reconnecting.'
|
|
)
|
|
)
|
|
cancelFunction()
|
|
cancelFunction = undefined
|
|
} else {
|
|
wasActivity = false
|
|
timeout = setTimeout(function() {
|
|
onTimeout()
|
|
}, heartbeatTimeout)
|
|
}
|
|
return
|
|
}
|
|
|
|
wasActivity = false
|
|
timeout = setTimeout(function() {
|
|
onTimeout()
|
|
}, heartbeatTimeout)
|
|
|
|
currentState = CONNECTING
|
|
dataBuffer = ''
|
|
eventTypeBuffer = ''
|
|
lastEventIdBuffer = lastEventId
|
|
textBuffer = ''
|
|
fieldStart = 0
|
|
valueStart = 0
|
|
state = FIELD_START
|
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=428916
|
|
// Request header field Last-Event-ID is not allowed by Access-Control-Allow-Headers.
|
|
var requestURL = url
|
|
if (url.slice(0, 5) !== 'data:' && url.slice(0, 5) !== 'blob:') {
|
|
if (lastEventId !== '') {
|
|
requestURL +=
|
|
(url.indexOf('?') === -1 ? '?' : '&') +
|
|
'lastEventId=' +
|
|
encodeURIComponent(lastEventId)
|
|
}
|
|
}
|
|
var requestHeaders = {}
|
|
requestHeaders['Accept'] = 'text/event-stream'
|
|
if (headers != undefined) {
|
|
for (var name in headers) {
|
|
if (Object.prototype.hasOwnProperty.call(headers, name)) {
|
|
requestHeaders[name] = headers[name]
|
|
}
|
|
}
|
|
}
|
|
try {
|
|
transport.open(
|
|
xhr,
|
|
onStart,
|
|
onProgress,
|
|
onFinish,
|
|
requestURL,
|
|
withCredentials,
|
|
requestHeaders
|
|
)
|
|
} catch (error) {
|
|
close()
|
|
throw error
|
|
}
|
|
}
|
|
|
|
es.url = url
|
|
es.readyState = CONNECTING
|
|
es.withCredentials = withCredentials
|
|
es._close = close
|
|
|
|
onTimeout()
|
|
}
|
|
|
|
EventSourcePolyfill.prototype = Object.create(EventTarget.prototype)
|
|
EventSourcePolyfill.prototype.CONNECTING = CONNECTING
|
|
EventSourcePolyfill.prototype.OPEN = OPEN
|
|
EventSourcePolyfill.prototype.CLOSED = CLOSED
|
|
EventSourcePolyfill.prototype.close = function() {
|
|
this._close()
|
|
}
|
|
|
|
EventSourcePolyfill.CONNECTING = CONNECTING
|
|
EventSourcePolyfill.OPEN = OPEN
|
|
EventSourcePolyfill.CLOSED = CLOSED
|
|
EventSourcePolyfill.prototype.withCredentials = undefined
|
|
|
|
export default EventSourcePolyfill
|