You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

eventsource.sharedworker.js 3.7KB

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