2020-07-03 12:55:36 +03:00
const sourcesByUrl = { } ;
const sourcesByPort = { } ;
class Source {
constructor ( url ) {
this . url = url ;
this . eventSource = new EventSource ( url ) ;
this . listening = { } ;
this . clients = [ ] ;
this . listen ( 'open' ) ;
2021-04-05 00:37:50 +03:00
this . listen ( 'close' ) ;
2020-07-03 12:55:36 +03:00
this . listen ( 'logout' ) ;
this . listen ( 'notification-count' ) ;
2021-02-19 13:05:35 +03:00
this . listen ( 'stopwatches' ) ;
2020-07-03 12:55:36 +03:00
this . listen ( 'error' ) ;
}
register ( port ) {
2020-07-05 01:04:00 +03:00
if ( this . clients . includes ( port ) ) return ;
2020-07-03 12:55:36 +03:00
this . clients . push ( port ) ;
port . postMessage ( {
type : 'status' ,
message : ` registered to ${ this . url } ` ,
} ) ;
}
deregister ( port ) {
const portIdx = this . clients . indexOf ( port ) ;
if ( portIdx < 0 ) {
return this . clients . length ;
}
this . clients . splice ( portIdx , 1 ) ;
return this . clients . length ;
}
close ( ) {
if ( ! this . eventSource ) return ;
this . eventSource . close ( ) ;
this . eventSource = null ;
}
listen ( eventType ) {
if ( this . listening [ eventType ] ) return ;
this . listening [ eventType ] = true ;
this . eventSource . addEventListener ( eventType , ( event ) => {
2021-03-22 07:04:19 +03:00
this . notifyClients ( {
2020-07-03 12:55:36 +03:00
type : eventType ,
data : event . data
} ) ;
} ) ;
}
notifyClients ( event ) {
for ( const client of this . clients ) {
client . postMessage ( event ) ;
}
}
status ( port ) {
port . postMessage ( {
type : 'status' ,
message : ` url: ${ this . url } readyState: ${ this . eventSource . readyState } ` ,
} ) ;
}
}
2021-08-17 08:32:48 +03:00
self . addEventListener ( 'connect' , ( e ) => {
2020-07-03 12:55:36 +03:00
for ( const port of e . ports ) {
port . addEventListener ( 'message' , ( event ) => {
2022-08-03 22:58:27 +03:00
if ( ! self . EventSource ) {
// some browsers (like PaleMoon, Firefox<53) don't support EventSource in SharedWorkerGlobalScope.
// this event handler needs EventSource when doing "new Source(url)", so just post a message back to the caller,
// in case the caller would like to use a fallback method to do its work.
port . postMessage ( { type : 'no-event-source' } ) ;
return ;
}
2020-07-03 12:55:36 +03:00
if ( event . data . type === 'start' ) {
const url = event . data . url ;
if ( sourcesByUrl [ url ] ) {
// we have a Source registered to this url
const source = sourcesByUrl [ url ] ;
source . register ( port ) ;
sourcesByPort [ port ] = source ;
return ;
}
let source = sourcesByPort [ port ] ;
if ( source ) {
if ( source . eventSource && source . url === url ) return ;
// How this has happened I don't understand...
// deregister from that source
const count = source . deregister ( port ) ;
2020-07-04 17:01:25 +03:00
// Clean-up
2020-07-03 12:55:36 +03:00
if ( count === 0 ) {
source . close ( ) ;
sourcesByUrl [ source . url ] = null ;
}
}
// Create a new Source
source = new Source ( url ) ;
source . register ( port ) ;
sourcesByUrl [ url ] = source ;
sourcesByPort [ port ] = source ;
} else if ( event . data . type === 'listen' ) {
const source = sourcesByPort [ port ] ;
source . listen ( event . data . eventType ) ;
} else if ( event . data . type === 'close' ) {
const source = sourcesByPort [ port ] ;
if ( ! source ) return ;
const count = source . deregister ( port ) ;
if ( count === 0 ) {
source . close ( ) ;
sourcesByUrl [ source . url ] = null ;
sourcesByPort [ port ] = null ;
}
} else if ( event . data . type === 'status' ) {
const source = sourcesByPort [ port ] ;
if ( ! source ) {
port . postMessage ( {
type : 'status' ,
message : 'not connected' ,
} ) ;
return ;
}
source . status ( port ) ;
} else {
// just send it back
port . postMessage ( {
type : 'error' ,
message : ` received but don't know how to handle: ${ event . data } ` ,
} ) ;
}
} ) ;
port . start ( ) ;
}
2021-08-17 08:32:48 +03:00
} ) ;