web.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. // Event state
  2. const BUBBLES = 0x1
  3. const CANCELABLE = 0x2
  4. const COMPOSED = 0x4
  5. const CANCELED = 0x8
  6. const DISPATCH = 0x10
  7. const STOP = 0x20
  8. // EventTarget state
  9. const CAPTURE = 0x1
  10. const PASSIVE = 0x2
  11. const ONCE = 0x4
  12. // https://dom.spec.whatwg.org/#event
  13. class Event {
  14. // https://dom.spec.whatwg.org/#dom-event-event
  15. constructor(type, options = {}) {
  16. const { bubbles = false, cancelable = false, composed = false } = options
  17. this._type = type
  18. this._target = null
  19. this._state = 0
  20. if (bubbles) this._state |= BUBBLES
  21. if (cancelable) this._state |= CANCELABLE
  22. if (composed) this._state |= COMPOSED
  23. }
  24. // https://dom.spec.whatwg.org/#dom-event-type
  25. get type() {
  26. return this._type
  27. }
  28. // https://dom.spec.whatwg.org/#dom-event-target
  29. get target() {
  30. return this._target
  31. }
  32. // https://dom.spec.whatwg.org/#dom-event-currenttarget
  33. get currentTarget() {
  34. return null
  35. }
  36. // https://dom.spec.whatwg.org/#dom-event-bubbles
  37. get bubbles() {
  38. return (this._state & BUBBLES) !== 0
  39. }
  40. // https://dom.spec.whatwg.org/#dom-event-cancelable
  41. get cancelable() {
  42. return (this._state & CANCELABLE) !== 0
  43. }
  44. // https://dom.spec.whatwg.org/#dom-event-composed
  45. get composed() {
  46. return (this._state & COMPOSED) !== 0
  47. }
  48. // https://dom.spec.whatwg.org/#dom-event-defaultprevented
  49. get defaultPrevented() {
  50. return (this._state & CANCELED) !== 0
  51. }
  52. // https://dom.spec.whatwg.org/#dom-event-istrusted
  53. get isTrusted() {
  54. return false
  55. }
  56. // https://dom.spec.whatwg.org/#dom-event-preventdefault
  57. preventDefault() {
  58. if (this._state & CANCELABLE) this._state |= CANCELED
  59. }
  60. // https://dom.spec.whatwg.org/#dom-event-stoppropagation
  61. stopPropagation() {}
  62. // https://dom.spec.whatwg.org/#dom-event-stopimmediatepropagation
  63. stopImmediatePropagation() {
  64. this._state |= STOP
  65. }
  66. toJSON() {
  67. return {
  68. type: this.type,
  69. target: this.target,
  70. bubbles: this.bubbles,
  71. cancelable: this.cancelable,
  72. composed: this.composed,
  73. defaultPrevented: this.defaultPrevented,
  74. isTrusted: this.isTrusted
  75. }
  76. }
  77. [Symbol.for('bare.inspect')]() {
  78. return {
  79. __proto__: { constructor: Event },
  80. type: this.type,
  81. target: this.target,
  82. bubbles: this.bubbles,
  83. cancelable: this.cancelable,
  84. composed: this.composed,
  85. defaultPrevented: this.defaultPrevented,
  86. isTrusted: this.isTrusted
  87. }
  88. }
  89. }
  90. exports.Event = Event
  91. // https://dom.spec.whatwg.org/#customevent
  92. exports.CustomEvent = class CustomEvent extends Event {
  93. constructor(type, options = {}) {
  94. super(type, options)
  95. const { detail = null } = options
  96. this._detail = detail
  97. }
  98. // https://dom.spec.whatwg.org/#dom-customevent-detail
  99. get detail() {
  100. return this._detail
  101. }
  102. }
  103. // https://dom.spec.whatwg.org/#eventtarget
  104. exports.EventTarget = class EventTarget {
  105. // https://dom.spec.whatwg.org/#dom-eventtarget-eventtarget
  106. constructor() {
  107. this._listeners = new Map()
  108. }
  109. // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
  110. addEventListener(type, callback = null, options = {}) {
  111. if (typeof options === 'boolean') options = { capture: options }
  112. const { capture = false, passive = false, once = false, signal = null } = options
  113. if (signal !== null && signal.aborted) return
  114. if (callback === null) return
  115. const listener = new EventListener(type, callback, capture, passive, once, signal)
  116. const listeners = this._listeners.get(type)
  117. if (listeners === undefined) this._listeners.set(type, listener)
  118. else {
  119. for (const existing of listeners) {
  120. if (callback === existing.callback && capture === existing.capture) {
  121. return // Duplicate listener
  122. }
  123. }
  124. listener.link(listeners)
  125. if (signal !== null) {
  126. signal.addEventListener('abort', onabort)
  127. function onabort() {
  128. listener.unlink()
  129. }
  130. }
  131. }
  132. }
  133. // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
  134. removeEventListener(type, callback = null, options = {}) {
  135. if (typeof options === 'boolean') options = { capture: options }
  136. const { capture = false } = options
  137. const listeners = this._listeners.get(type)
  138. if (listeners === undefined) return
  139. for (const existing of listeners) {
  140. if (callback === existing.callback && capture === existing.capture) {
  141. const next = existing.unlink()
  142. if (listeners === existing) this._listeners.set(type, next)
  143. return
  144. }
  145. }
  146. }
  147. // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
  148. dispatchEvent(event) {
  149. event._target = this
  150. event._state |= DISPATCH
  151. const listeners = this._listeners.get(event.type)
  152. try {
  153. if (listeners === undefined) return true
  154. for (const listener of listeners) {
  155. // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
  156. if (listener.once) listener.unlink()
  157. let callback = listener.callback
  158. let context = this
  159. if (typeof callback === 'object') {
  160. context = callback
  161. callback = callback.handleEvent
  162. }
  163. Reflect.apply(callback, context, [event])
  164. if (event._state & STOP) break
  165. }
  166. return (event._state & CANCELED) === 0
  167. } finally {
  168. event._state &= ~DISPATCH
  169. event._state &= ~STOP
  170. }
  171. }
  172. toJSON() {
  173. return {}
  174. }
  175. [Symbol.for('bare.inspect')]() {
  176. return {
  177. __proto__: { constructor: EventTarget }
  178. }
  179. }
  180. }
  181. // https://dom.spec.whatwg.org/#concept-event-listener
  182. class EventListener {
  183. constructor(type, callback, capture, passive, once, signal) {
  184. this._type = type
  185. this._callback = callback
  186. this._signal = signal
  187. this._state = 0
  188. if (capture) this._state |= CAPTURE
  189. if (passive) this._state |= PASSIVE
  190. if (once) this._state |= ONCE
  191. this._previous = this
  192. this._next = this
  193. }
  194. get type() {
  195. return this._type
  196. }
  197. get callback() {
  198. return this._callback
  199. }
  200. get capture() {
  201. return (this._state & CAPTURE) !== 0
  202. }
  203. get passive() {
  204. return (this._state & PASSIVE) !== 0
  205. }
  206. get once() {
  207. return (this._state & ONCE) !== 0
  208. }
  209. get removed() {
  210. return this._previous === this && this._next === this
  211. }
  212. link(listener) {
  213. const next = this._next
  214. const previous = listener._previous
  215. this._next = listener
  216. listener._previous = this
  217. previous._next = next
  218. next._previous = previous
  219. return listener
  220. }
  221. unlink() {
  222. if (this.removed) return this
  223. const next = this._next
  224. const previous = this._previous
  225. this._next = this
  226. this._previous = this
  227. previous._next = next
  228. next._previous = previous
  229. return next
  230. }
  231. *[Symbol.iterator]() {
  232. let current = this
  233. while (true) {
  234. const next = current._next
  235. yield current
  236. if (next === this) break
  237. current = next
  238. }
  239. }
  240. toJSON() {
  241. return {
  242. type: this.type,
  243. capture: this.capture,
  244. passive: this.passive,
  245. once: this.once,
  246. removed: this.removed
  247. }
  248. }
  249. [Symbol.for('bare.inspect')]() {
  250. return {
  251. __proto__: { constructor: EventListener },
  252. type: this.type,
  253. callback: this.callback,
  254. capture: this.capture,
  255. passive: this.passive,
  256. once: this.once,
  257. removed: this.removed
  258. }
  259. }
  260. }