event-target.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. 'use strict';
  2. const { kForOnEventAttribute, kListener } = require('./constants');
  3. const kCode = Symbol('kCode');
  4. const kData = Symbol('kData');
  5. const kError = Symbol('kError');
  6. const kMessage = Symbol('kMessage');
  7. const kReason = Symbol('kReason');
  8. const kTarget = Symbol('kTarget');
  9. const kType = Symbol('kType');
  10. const kWasClean = Symbol('kWasClean');
  11. /**
  12. * Class representing an event.
  13. */
  14. class Event {
  15. /**
  16. * Create a new `Event`.
  17. *
  18. * @param {String} type The name of the event
  19. * @throws {TypeError} If the `type` argument is not specified
  20. */
  21. constructor(type) {
  22. this[kTarget] = null;
  23. this[kType] = type;
  24. }
  25. /**
  26. * @type {*}
  27. */
  28. get target() {
  29. return this[kTarget];
  30. }
  31. /**
  32. * @type {String}
  33. */
  34. get type() {
  35. return this[kType];
  36. }
  37. }
  38. Object.defineProperty(Event.prototype, 'target', { enumerable: true });
  39. Object.defineProperty(Event.prototype, 'type', { enumerable: true });
  40. /**
  41. * Class representing a close event.
  42. *
  43. * @extends Event
  44. */
  45. class CloseEvent extends Event {
  46. /**
  47. * Create a new `CloseEvent`.
  48. *
  49. * @param {String} type The name of the event
  50. * @param {Object} [options] A dictionary object that allows for setting
  51. * attributes via object members of the same name
  52. * @param {Number} [options.code=0] The status code explaining why the
  53. * connection was closed
  54. * @param {String} [options.reason=''] A human-readable string explaining why
  55. * the connection was closed
  56. * @param {Boolean} [options.wasClean=false] Indicates whether or not the
  57. * connection was cleanly closed
  58. */
  59. constructor(type, options = {}) {
  60. super(type);
  61. this[kCode] = options.code === undefined ? 0 : options.code;
  62. this[kReason] = options.reason === undefined ? '' : options.reason;
  63. this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;
  64. }
  65. /**
  66. * @type {Number}
  67. */
  68. get code() {
  69. return this[kCode];
  70. }
  71. /**
  72. * @type {String}
  73. */
  74. get reason() {
  75. return this[kReason];
  76. }
  77. /**
  78. * @type {Boolean}
  79. */
  80. get wasClean() {
  81. return this[kWasClean];
  82. }
  83. }
  84. Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });
  85. Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });
  86. Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });
  87. /**
  88. * Class representing an error event.
  89. *
  90. * @extends Event
  91. */
  92. class ErrorEvent extends Event {
  93. /**
  94. * Create a new `ErrorEvent`.
  95. *
  96. * @param {String} type The name of the event
  97. * @param {Object} [options] A dictionary object that allows for setting
  98. * attributes via object members of the same name
  99. * @param {*} [options.error=null] The error that generated this event
  100. * @param {String} [options.message=''] The error message
  101. */
  102. constructor(type, options = {}) {
  103. super(type);
  104. this[kError] = options.error === undefined ? null : options.error;
  105. this[kMessage] = options.message === undefined ? '' : options.message;
  106. }
  107. /**
  108. * @type {*}
  109. */
  110. get error() {
  111. return this[kError];
  112. }
  113. /**
  114. * @type {String}
  115. */
  116. get message() {
  117. return this[kMessage];
  118. }
  119. }
  120. Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });
  121. Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });
  122. /**
  123. * Class representing a message event.
  124. *
  125. * @extends Event
  126. */
  127. class MessageEvent extends Event {
  128. /**
  129. * Create a new `MessageEvent`.
  130. *
  131. * @param {String} type The name of the event
  132. * @param {Object} [options] A dictionary object that allows for setting
  133. * attributes via object members of the same name
  134. * @param {*} [options.data=null] The message content
  135. */
  136. constructor(type, options = {}) {
  137. super(type);
  138. this[kData] = options.data === undefined ? null : options.data;
  139. }
  140. /**
  141. * @type {*}
  142. */
  143. get data() {
  144. return this[kData];
  145. }
  146. }
  147. Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });
  148. /**
  149. * This provides methods for emulating the `EventTarget` interface. It's not
  150. * meant to be used directly.
  151. *
  152. * @mixin
  153. */
  154. const EventTarget = {
  155. /**
  156. * Register an event listener.
  157. *
  158. * @param {String} type A string representing the event type to listen for
  159. * @param {Function} listener The listener to add
  160. * @param {Object} [options] An options object specifies characteristics about
  161. * the event listener
  162. * @param {Boolean} [options.once=false] A `Boolean` indicating that the
  163. * listener should be invoked at most once after being added. If `true`,
  164. * the listener would be automatically removed when invoked.
  165. * @public
  166. */
  167. addEventListener(type, listener, options = {}) {
  168. let wrapper;
  169. if (type === 'message') {
  170. wrapper = function onMessage(data, isBinary) {
  171. const event = new MessageEvent('message', {
  172. data: isBinary ? data : data.toString()
  173. });
  174. event[kTarget] = this;
  175. listener.call(this, event);
  176. };
  177. } else if (type === 'close') {
  178. wrapper = function onClose(code, message) {
  179. const event = new CloseEvent('close', {
  180. code,
  181. reason: message.toString(),
  182. wasClean: this._closeFrameReceived && this._closeFrameSent
  183. });
  184. event[kTarget] = this;
  185. listener.call(this, event);
  186. };
  187. } else if (type === 'error') {
  188. wrapper = function onError(error) {
  189. const event = new ErrorEvent('error', {
  190. error,
  191. message: error.message
  192. });
  193. event[kTarget] = this;
  194. listener.call(this, event);
  195. };
  196. } else if (type === 'open') {
  197. wrapper = function onOpen() {
  198. const event = new Event('open');
  199. event[kTarget] = this;
  200. listener.call(this, event);
  201. };
  202. } else {
  203. return;
  204. }
  205. wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
  206. wrapper[kListener] = listener;
  207. if (options.once) {
  208. this.once(type, wrapper);
  209. } else {
  210. this.on(type, wrapper);
  211. }
  212. },
  213. /**
  214. * Remove an event listener.
  215. *
  216. * @param {String} type A string representing the event type to remove
  217. * @param {Function} handler The listener to remove
  218. * @public
  219. */
  220. removeEventListener(type, handler) {
  221. for (const listener of this.listeners(type)) {
  222. if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
  223. this.removeListener(type, listener);
  224. break;
  225. }
  226. }
  227. }
  228. };
  229. module.exports = {
  230. CloseEvent,
  231. ErrorEvent,
  232. Event,
  233. EventTarget,
  234. MessageEvent
  235. };