code.gitea.io/gitea@v1.22.3/web_src/js/features/eventsource.sharedworker.js (about)

     1  const sourcesByUrl = {};
     2  const sourcesByPort = {};
     3  
     4  class Source {
     5    constructor(url) {
     6      this.url = url;
     7      this.eventSource = new EventSource(url);
     8      this.listening = {};
     9      this.clients = [];
    10      this.listen('open');
    11      this.listen('close');
    12      this.listen('logout');
    13      this.listen('notification-count');
    14      this.listen('stopwatches');
    15      this.listen('error');
    16    }
    17  
    18    register(port) {
    19      if (this.clients.includes(port)) return;
    20  
    21      this.clients.push(port);
    22  
    23      port.postMessage({
    24        type: 'status',
    25        message: `registered to ${this.url}`,
    26      });
    27    }
    28  
    29    deregister(port) {
    30      const portIdx = this.clients.indexOf(port);
    31      if (portIdx < 0) {
    32        return this.clients.length;
    33      }
    34      this.clients.splice(portIdx, 1);
    35      return this.clients.length;
    36    }
    37  
    38    close() {
    39      if (!this.eventSource) return;
    40  
    41      this.eventSource.close();
    42      this.eventSource = null;
    43    }
    44  
    45    listen(eventType) {
    46      if (this.listening[eventType]) return;
    47      this.listening[eventType] = true;
    48      this.eventSource.addEventListener(eventType, (event) => {
    49        this.notifyClients({
    50          type: eventType,
    51          data: event.data,
    52        });
    53      });
    54    }
    55  
    56    notifyClients(event) {
    57      for (const client of this.clients) {
    58        client.postMessage(event);
    59      }
    60    }
    61  
    62    status(port) {
    63      port.postMessage({
    64        type: 'status',
    65        message: `url: ${this.url} readyState: ${this.eventSource.readyState}`,
    66      });
    67    }
    68  }
    69  
    70  self.addEventListener('connect', (e) => {
    71    for (const port of e.ports) {
    72      port.addEventListener('message', (event) => {
    73        if (!self.EventSource) {
    74          // some browsers (like PaleMoon, Firefox<53) don't support EventSource in SharedWorkerGlobalScope.
    75          // this event handler needs EventSource when doing "new Source(url)", so just post a message back to the caller,
    76          // in case the caller would like to use a fallback method to do its work.
    77          port.postMessage({type: 'no-event-source'});
    78          return;
    79        }
    80        if (event.data.type === 'start') {
    81          const url = event.data.url;
    82          if (sourcesByUrl[url]) {
    83            // we have a Source registered to this url
    84            const source = sourcesByUrl[url];
    85            source.register(port);
    86            sourcesByPort[port] = source;
    87            return;
    88          }
    89          let source = sourcesByPort[port];
    90          if (source) {
    91            if (source.eventSource && source.url === url) return;
    92  
    93            // How this has happened I don't understand...
    94            // deregister from that source
    95            const count = source.deregister(port);
    96            // Clean-up
    97            if (count === 0) {
    98              source.close();
    99              sourcesByUrl[source.url] = null;
   100            }
   101          }
   102          // Create a new Source
   103          source = new Source(url);
   104          source.register(port);
   105          sourcesByUrl[url] = source;
   106          sourcesByPort[port] = source;
   107        } else if (event.data.type === 'listen') {
   108          const source = sourcesByPort[port];
   109          source.listen(event.data.eventType);
   110        } else if (event.data.type === 'close') {
   111          const source = sourcesByPort[port];
   112  
   113          if (!source) return;
   114  
   115          const count = source.deregister(port);
   116          if (count === 0) {
   117            source.close();
   118            sourcesByUrl[source.url] = null;
   119            sourcesByPort[port] = null;
   120          }
   121        } else if (event.data.type === 'status') {
   122          const source = sourcesByPort[port];
   123          if (!source) {
   124            port.postMessage({
   125              type: 'status',
   126              message: 'not connected',
   127            });
   128            return;
   129          }
   130          source.status(port);
   131        } else {
   132          // just send it back
   133          port.postMessage({
   134            type: 'error',
   135            message: `received but don't know how to handle: ${event.data}`,
   136          });
   137        }
   138      });
   139      port.start();
   140    }
   141  });