Source: lib/util/fake_event_target.js

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