event-target.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. "use strict";
  2. function flattenOptions(options) {
  3. if (options !== Object(options)) {
  4. return {
  5. capture: Boolean(options),
  6. once: false,
  7. passive: false
  8. };
  9. }
  10. return {
  11. capture: Boolean(options.capture),
  12. once: Boolean(options.once),
  13. passive: Boolean(options.passive)
  14. };
  15. }
  16. function not(fn) {
  17. return function () {
  18. return !fn.apply(this, arguments);
  19. };
  20. }
  21. function hasListenerFilter(listener, capture) {
  22. return function (listenerSpec) {
  23. return listenerSpec.capture === capture
  24. && listenerSpec.listener === listener;
  25. };
  26. }
  27. var EventTarget = {
  28. // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
  29. addEventListener: function addEventListener(event, listener, providedOptions) {
  30. // 3. Let capture, passive, and once be the result of flattening more options.
  31. // Flatten property before executing step 2,
  32. // feture detection is usually based on registering handler with options object,
  33. // that has getter defined
  34. // addEventListener("load", () => {}, {
  35. // get once() { supportsOnce = true; }
  36. // });
  37. var options = flattenOptions(providedOptions);
  38. // 2. If callback is null, then return.
  39. if (listener == null) {
  40. return;
  41. }
  42. this.eventListeners = this.eventListeners || {};
  43. this.eventListeners[event] = this.eventListeners[event] || [];
  44. // 4. If context object’s associated list of event listener
  45. // does not contain an event listener whose type is type,
  46. // callback is callback, and capture is capture, then append
  47. // a new event listener to it, whose type is type, callback is
  48. // callback, capture is capture, passive is passive, and once is once.
  49. if (!this.eventListeners[event].some(hasListenerFilter(listener, options.capture))) {
  50. this.eventListeners[event].push({
  51. listener: listener,
  52. capture: options.capture,
  53. once: options.once
  54. });
  55. }
  56. },
  57. // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
  58. removeEventListener: function removeEventListener(event, listener, providedOptions) {
  59. if (!this.eventListeners || !this.eventListeners[event]) {
  60. return;
  61. }
  62. // 2. Let capture be the result of flattening options.
  63. var options = flattenOptions(providedOptions);
  64. // 3. If there is an event listener in the associated list of
  65. // event listeners whose type is type, callback is callback,
  66. // and capture is capture, then set that event listener’s
  67. // removed to true and remove it from the associated list of event listeners.
  68. this.eventListeners[event] = this.eventListeners[event]
  69. .filter(not(hasListenerFilter(listener, options.capture)));
  70. },
  71. dispatchEvent: function dispatchEvent(event) {
  72. if (!this.eventListeners || !this.eventListeners[event.type]) {
  73. return Boolean(event.defaultPrevented);
  74. }
  75. var self = this;
  76. var type = event.type;
  77. var listeners = self.eventListeners[type];
  78. // Remove listeners, that should be dispatched once
  79. // before running dispatch loop to avoid nested dispatch issues
  80. self.eventListeners[type] = listeners.filter(function (listenerSpec) {
  81. return !listenerSpec.once;
  82. });
  83. listeners.forEach(function (listenerSpec) {
  84. var listener = listenerSpec.listener;
  85. if (typeof listener === "function") {
  86. listener.call(self, event);
  87. } else {
  88. listener.handleEvent(event);
  89. }
  90. });
  91. return Boolean(event.defaultPrevented);
  92. }
  93. };
  94. module.exports = EventTarget;