Source: lib/util/fake_event_target.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.util.FakeEventTarget');
  7. goog.require('goog.asserts');
  8. goog.require('shaka.log');
  9. goog.require('shaka.util.FakeEvent');
  10. goog.require('shaka.util.IReleasable');
  11. goog.require('shaka.util.MultiMap');
  12. /**
  13. * @summary A work-alike for EventTarget. Only DOM elements may be true
  14. * EventTargets, but this can be used as a base class to provide event dispatch
  15. * to non-DOM classes. Only FakeEvents should be dispatched.
  16. *
  17. * @implements {EventTarget}
  18. * @implements {shaka.util.IReleasable}
  19. * @exportInterface
  20. */
  21. shaka.util.FakeEventTarget = class {
  22. /** */
  23. constructor() {
  24. /**
  25. * @private {shaka.util.MultiMap<shaka.util.FakeEventTarget.ListenerType>}
  26. */
  27. this.listeners_ = new shaka.util.MultiMap();
  28. /**
  29. * The target of all dispatched events. Defaults to |this|.
  30. * @type {EventTarget}
  31. */
  32. this.dispatchTarget = this;
  33. }
  34. /**
  35. * Add an event listener to this object.
  36. *
  37. * @param {string} type The event type to listen for.
  38. * @param {shaka.util.FakeEventTarget.ListenerType} listener The callback or
  39. * listener object to invoke.
  40. * @param {(!AddEventListenerOptions|boolean)=} options Ignored.
  41. * @override
  42. * @exportInterface
  43. */
  44. addEventListener(type, listener, options) {
  45. if (!this.listeners_) {
  46. return;
  47. }
  48. this.listeners_.push(type, listener);
  49. }
  50. /**
  51. * Add an event listener to this object that is invoked for all events types
  52. * the object fires.
  53. *
  54. * @param {shaka.util.FakeEventTarget.ListenerType} listener The callback or
  55. * listener object to invoke.
  56. * @exportInterface
  57. */
  58. listenToAllEvents(listener) {
  59. this.addEventListener(shaka.util.FakeEventTarget.ALL_EVENTS_, listener);
  60. }
  61. /**
  62. * Remove an event listener from this object.
  63. *
  64. * @param {string} type The event type for which you wish to remove a
  65. * listener.
  66. * @param {shaka.util.FakeEventTarget.ListenerType} listener The callback or
  67. * listener object to remove.
  68. * @param {(EventListenerOptions|boolean)=} options Ignored.
  69. * @override
  70. * @exportInterface
  71. */
  72. removeEventListener(type, listener, options) {
  73. if (!this.listeners_) {
  74. return;
  75. }
  76. this.listeners_.remove(type, listener);
  77. }
  78. /**
  79. * Dispatch an event from this object.
  80. *
  81. * @param {!Event} event The event to be dispatched from this object.
  82. * @return {boolean} True if the default action was prevented.
  83. * @override
  84. * @exportInterface
  85. */
  86. dispatchEvent(event) {
  87. // In many browsers, it is complex to overwrite properties of actual Events.
  88. // Here we expect only to dispatch FakeEvents, which are simpler.
  89. goog.asserts.assert(event instanceof shaka.util.FakeEvent,
  90. 'FakeEventTarget can only dispatch FakeEvents!');
  91. if (!this.listeners_) {
  92. return true;
  93. }
  94. let listeners = this.listeners_.get(event.type) || [];
  95. const universalListeners =
  96. this.listeners_.get(shaka.util.FakeEventTarget.ALL_EVENTS_);
  97. if (universalListeners) {
  98. listeners = listeners.concat(universalListeners);
  99. }
  100. // Execute this event on listeners until the event has been stopped or we
  101. // run out of listeners.
  102. for (const listener of listeners) {
  103. // Do this every time, since events can be re-dispatched from handlers.
  104. event.target = this.dispatchTarget;
  105. event.currentTarget = this.dispatchTarget;
  106. try {
  107. // Check for the |handleEvent| member to test if this is a
  108. // |EventListener| instance or a basic function.
  109. if (listener.handleEvent) {
  110. listener.handleEvent(event);
  111. } else {
  112. // eslint-disable-next-line no-restricted-syntax
  113. listener.call(this, event);
  114. }
  115. } catch (exception) {
  116. // Exceptions during event handlers should not affect the caller,
  117. // but should appear on the console as uncaught, according to MDN:
  118. // https://mzl.la/2JXgwRo
  119. shaka.log.error('Uncaught exception in event handler', exception,
  120. exception ? exception.message : null,
  121. exception ? exception.stack : null);
  122. }
  123. if (event.stopped) {
  124. break;
  125. }
  126. }
  127. return event.defaultPrevented;
  128. }
  129. /**
  130. * @override
  131. * @exportInterface
  132. */
  133. release() {
  134. this.listeners_ = null;
  135. }
  136. };
  137. /**
  138. * These are the listener types defined in the closure extern for EventTarget.
  139. * @typedef {EventListener|function(!Event):*}
  140. * @exportInterface
  141. */
  142. shaka.util.FakeEventTarget.ListenerType;
  143. /**
  144. * @const {string}
  145. * @private
  146. */
  147. shaka.util.FakeEventTarget.ALL_EVENTS_ = 'All';