dommy 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +31 -13
- data/lib/dommy/animation.rb +288 -0
- data/lib/dommy/attr.rb +23 -11
- data/lib/dommy/backend/nokogiri_adapter.rb +51 -0
- data/lib/dommy/backend/nokolexbor_adapter.rb +80 -0
- data/lib/dommy/backend.rb +129 -0
- data/lib/dommy/blob.rb +2 -2
- data/lib/dommy/compression_streams.rb +147 -0
- data/lib/dommy/cookie_store.rb +128 -0
- data/lib/dommy/crypto.rb +396 -0
- data/lib/dommy/css.rb +7 -7
- data/lib/dommy/custom_elements.rb +6 -6
- data/lib/dommy/document.rb +190 -32
- data/lib/dommy/dom_parser.rb +5 -4
- data/lib/dommy/element.rb +356 -53
- data/lib/dommy/event.rb +431 -25
- data/lib/dommy/event_source.rb +131 -0
- data/lib/dommy/fetch.rb +76 -6
- data/lib/dommy/file_reader.rb +176 -0
- data/lib/dommy/form_data.rb +1 -3
- data/lib/dommy/history.rb +82 -0
- data/lib/dommy/html_collection.rb +4 -4
- data/lib/dommy/html_elements.rb +130 -67
- data/lib/dommy/internal/cookie_jar.rb +2 -0
- data/lib/dommy/internal/css_pseudo_handlers.rb +28 -0
- data/lib/dommy/internal/dom_matching.rb +4 -4
- data/lib/dommy/internal/idna.rb +443 -0
- data/lib/dommy/internal/idna_data.rb +10379 -0
- data/lib/dommy/internal/ipv4_parser.rb +78 -0
- data/lib/dommy/internal/node_traversal.rb +1 -1
- data/lib/dommy/internal/node_wrapper_cache.rb +23 -12
- data/lib/dommy/internal/observable_callback.rb +25 -0
- data/lib/dommy/internal/punycode.rb +202 -0
- data/lib/dommy/internal/range_text_serializer.rb +72 -0
- data/lib/dommy/internal/reflected_attributes.rb +45 -0
- data/lib/dommy/internal/template_content_registry.rb +6 -6
- data/lib/dommy/intersection_observer.rb +82 -0
- data/lib/dommy/{router.rb → location.rb} +8 -142
- data/lib/dommy/media_query_list.rb +118 -0
- data/lib/dommy/message_channel.rb +249 -0
- data/lib/dommy/{observer.rb → mutation_observer.rb} +21 -11
- data/lib/dommy/navigator.rb +365 -5
- data/lib/dommy/node.rb +12 -0
- data/lib/dommy/notification.rb +89 -0
- data/lib/dommy/parser.rb +13 -13
- data/lib/dommy/performance.rb +146 -0
- data/lib/dommy/performance_observer.rb +55 -0
- data/lib/dommy/range.rb +597 -0
- data/lib/dommy/resize_observer.rb +53 -0
- data/lib/dommy/shadow_root.rb +10 -8
- data/lib/dommy/streams.rb +386 -0
- data/lib/dommy/svg_elements.rb +3863 -0
- data/lib/dommy/text_codec.rb +175 -0
- data/lib/dommy/tree_walker.rb +21 -21
- data/lib/dommy/url.rb +274 -29
- data/lib/dommy/url_pattern.rb +144 -0
- data/lib/dommy/version.rb +1 -1
- data/lib/dommy/web_socket.rb +209 -0
- data/lib/dommy/window.rb +369 -0
- data/lib/dommy/worker.rb +143 -0
- data/lib/dommy/xml_http_request.rb +438 -0
- data/lib/dommy.rb +43 -5
- metadata +44 -29
- data/lib/dommy/world.rb +0 -209
data/lib/dommy/event.rb
CHANGED
|
@@ -54,24 +54,30 @@ module Dommy
|
|
|
54
54
|
# Per spec, dispatchEvent must receive an Event instance.
|
|
55
55
|
raise TypeError, "dispatchEvent requires an Event, got #{event.class}" unless event.is_a?(Event)
|
|
56
56
|
|
|
57
|
-
event.
|
|
57
|
+
event.__internal_prepare_for_dispatch__(self)
|
|
58
58
|
path = if event.bubbles?
|
|
59
59
|
event.__js_get__("composed") ? composed_bubble_path(event) : event_bubble_path
|
|
60
60
|
else
|
|
61
61
|
[self]
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
event.
|
|
64
|
+
event.__internal_record_path__(path) if event.respond_to?(:__internal_record_path__)
|
|
65
65
|
path.each do |target|
|
|
66
|
-
event.
|
|
67
|
-
target.
|
|
66
|
+
event.__internal_set_current_target__(target)
|
|
67
|
+
target.__internal_deliver_event__(event)
|
|
68
68
|
break if event.propagation_stopped?
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
+
# WHATWG: after dispatch completes, currentTarget reverts to
|
|
72
|
+
# null and eventPhase reverts to NONE. Dommy derives
|
|
73
|
+
# eventPhase from currentTarget, so clearing it here covers
|
|
74
|
+
# both spec requirements.
|
|
75
|
+
event.__internal_set_current_target__(nil)
|
|
76
|
+
|
|
71
77
|
!event.default_prevented?
|
|
72
78
|
end
|
|
73
79
|
|
|
74
|
-
def
|
|
80
|
+
def __internal_deliver_event__(event)
|
|
75
81
|
listeners = listeners_for(event.type).dup
|
|
76
82
|
listeners.each do |entry|
|
|
77
83
|
invoke_listener(entry.listener, event)
|
|
@@ -106,7 +112,7 @@ module Dommy
|
|
|
106
112
|
def event_bubble_path
|
|
107
113
|
path = [self]
|
|
108
114
|
current = self
|
|
109
|
-
while (current = current.send(:
|
|
115
|
+
while (current = current.send(:__internal_event_parent__))
|
|
110
116
|
path << current
|
|
111
117
|
end
|
|
112
118
|
|
|
@@ -116,12 +122,12 @@ module Dommy
|
|
|
116
122
|
# Build the propagation path with optional shadow-boundary
|
|
117
123
|
# crossing. When the in-flight event has `composed: true`, the
|
|
118
124
|
# walk continues from a ShadowRoot to its host; otherwise it
|
|
119
|
-
# stops at the shadow boundary (nil from `
|
|
125
|
+
# stops at the shadow boundary (nil from `__internal_event_parent__`).
|
|
120
126
|
def composed_bubble_path(event)
|
|
121
127
|
path = [self]
|
|
122
128
|
current = self
|
|
123
129
|
loop do
|
|
124
|
-
nxt = current.send(:
|
|
130
|
+
nxt = current.send(:__internal_event_parent__)
|
|
125
131
|
if nxt.nil? && event.respond_to?(:__js_get__) && event.__js_get__("composed")
|
|
126
132
|
# Try to cross a shadow boundary
|
|
127
133
|
if current.is_a?(ShadowRoot)
|
|
@@ -148,12 +154,12 @@ module Dommy
|
|
|
148
154
|
private
|
|
149
155
|
|
|
150
156
|
def enclosing_shadow_root_of(target)
|
|
151
|
-
return nil unless target.respond_to?(:
|
|
157
|
+
return nil unless target.respond_to?(:__dommy_backend_node__)
|
|
152
158
|
|
|
153
159
|
doc = target.instance_variable_get(:@document)
|
|
154
|
-
return nil unless doc && doc.respond_to?(:
|
|
160
|
+
return nil unless doc && doc.respond_to?(:__internal_shadow_root_containing__)
|
|
155
161
|
|
|
156
|
-
doc.
|
|
162
|
+
doc.__internal_shadow_root_containing__(target.__dommy_backend_node__)
|
|
157
163
|
end
|
|
158
164
|
|
|
159
165
|
public
|
|
@@ -178,6 +184,12 @@ module Dommy
|
|
|
178
184
|
class StandaloneEventTarget
|
|
179
185
|
include EventTarget
|
|
180
186
|
|
|
187
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
188
|
+
JS_METHOD_NAMES = %w[addEventListener removeEventListener dispatchEvent].freeze
|
|
189
|
+
def __js_method_names__
|
|
190
|
+
JS_METHOD_NAMES
|
|
191
|
+
end
|
|
192
|
+
|
|
181
193
|
def __js_call__(method, args)
|
|
182
194
|
case method
|
|
183
195
|
when "addEventListener"
|
|
@@ -191,7 +203,7 @@ module Dommy
|
|
|
191
203
|
end
|
|
192
204
|
end
|
|
193
205
|
|
|
194
|
-
def
|
|
206
|
+
def __internal_event_parent__
|
|
195
207
|
nil
|
|
196
208
|
end
|
|
197
209
|
end
|
|
@@ -232,11 +244,11 @@ module Dommy
|
|
|
232
244
|
@immediate_propagation_stopped
|
|
233
245
|
end
|
|
234
246
|
|
|
235
|
-
def
|
|
247
|
+
def __internal_prepare_for_dispatch__(target)
|
|
236
248
|
@target ||= target
|
|
237
249
|
end
|
|
238
250
|
|
|
239
|
-
def
|
|
251
|
+
def __internal_set_current_target__(target)
|
|
240
252
|
@current_target = target
|
|
241
253
|
end
|
|
242
254
|
|
|
@@ -276,6 +288,12 @@ module Dommy
|
|
|
276
288
|
nil
|
|
277
289
|
end
|
|
278
290
|
|
|
291
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
292
|
+
JS_METHOD_NAMES = %w[preventDefault stopPropagation stopImmediatePropagation composedPath initEvent].freeze
|
|
293
|
+
def __js_method_names__
|
|
294
|
+
JS_METHOD_NAMES
|
|
295
|
+
end
|
|
296
|
+
|
|
279
297
|
def __js_call__(method, args)
|
|
280
298
|
case method
|
|
281
299
|
when "preventDefault"
|
|
@@ -314,7 +332,7 @@ module Dommy
|
|
|
314
332
|
# Per spec, `load` events do not propagate to the Window when
|
|
315
333
|
# composed paths are computed (resource-finished signal stays at
|
|
316
334
|
# the target).
|
|
317
|
-
def
|
|
335
|
+
def __internal_record_path__(targets)
|
|
318
336
|
@composed_path = if @type == "load"
|
|
319
337
|
targets.reject { |t| t.is_a?(Window) }
|
|
320
338
|
else
|
|
@@ -348,6 +366,8 @@ module Dommy
|
|
|
348
366
|
end
|
|
349
367
|
|
|
350
368
|
class CustomEvent < Event
|
|
369
|
+
attr_reader :detail
|
|
370
|
+
|
|
351
371
|
def initialize(type, init = nil)
|
|
352
372
|
super
|
|
353
373
|
@detail = read_init(init, "detail")
|
|
@@ -443,6 +463,380 @@ module Dommy
|
|
|
443
463
|
end
|
|
444
464
|
end
|
|
445
465
|
|
|
466
|
+
# `InputEvent` — fired by `input` / `beforeinput`. Carries `data`
|
|
467
|
+
# (the inserted text, if any) and `inputType` (insertText,
|
|
468
|
+
# deleteContentBackward, etc.).
|
|
469
|
+
class InputEvent < Event
|
|
470
|
+
def initialize(type, init = nil)
|
|
471
|
+
super
|
|
472
|
+
@data = read_init(init, "data")
|
|
473
|
+
@input_type = (read_init(init, "inputType") || "").to_s
|
|
474
|
+
@is_composing = !!read_init(init, "isComposing")
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def __js_get__(key)
|
|
478
|
+
case key
|
|
479
|
+
when "data"
|
|
480
|
+
@data
|
|
481
|
+
when "inputType"
|
|
482
|
+
@input_type
|
|
483
|
+
when "isComposing"
|
|
484
|
+
@is_composing
|
|
485
|
+
else
|
|
486
|
+
super
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
# `PointerEvent` — pointer (mouse / touch / pen) unified events.
|
|
492
|
+
# Inherits from MouseEvent for coords / modifier keys; adds
|
|
493
|
+
# pointerId, pointerType, pressure, width, height, etc.
|
|
494
|
+
class PointerEvent < MouseEvent
|
|
495
|
+
def initialize(type, init = nil)
|
|
496
|
+
super
|
|
497
|
+
@pointer_id = (read_init(init, "pointerId") || 0).to_i
|
|
498
|
+
@pointer_type = (read_init(init, "pointerType") || "mouse").to_s
|
|
499
|
+
@pressure = (read_init(init, "pressure") || 0).to_f
|
|
500
|
+
@tangential_pressure = (read_init(init, "tangentialPressure") || 0).to_f
|
|
501
|
+
@width = (read_init(init, "width") || 1).to_f
|
|
502
|
+
@height = (read_init(init, "height") || 1).to_f
|
|
503
|
+
@tilt_x = (read_init(init, "tiltX") || 0).to_i
|
|
504
|
+
@tilt_y = (read_init(init, "tiltY") || 0).to_i
|
|
505
|
+
@twist = (read_init(init, "twist") || 0).to_i
|
|
506
|
+
@is_primary = !!read_init(init, "isPrimary")
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def __js_get__(key)
|
|
510
|
+
case key
|
|
511
|
+
when "pointerId"
|
|
512
|
+
@pointer_id
|
|
513
|
+
when "pointerType"
|
|
514
|
+
@pointer_type
|
|
515
|
+
when "pressure"
|
|
516
|
+
@pressure
|
|
517
|
+
when "tangentialPressure"
|
|
518
|
+
@tangential_pressure
|
|
519
|
+
when "width"
|
|
520
|
+
@width
|
|
521
|
+
when "height"
|
|
522
|
+
@height
|
|
523
|
+
when "tiltX"
|
|
524
|
+
@tilt_x
|
|
525
|
+
when "tiltY"
|
|
526
|
+
@tilt_y
|
|
527
|
+
when "twist"
|
|
528
|
+
@twist
|
|
529
|
+
when "isPrimary"
|
|
530
|
+
@is_primary
|
|
531
|
+
else
|
|
532
|
+
super
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
# `ProgressEvent` — fired during long-running operations (XHR,
|
|
538
|
+
# fetch upload/download progress). Carries `loaded` / `total` /
|
|
539
|
+
# `lengthComputable`.
|
|
540
|
+
class ProgressEvent < Event
|
|
541
|
+
def initialize(type, init = nil)
|
|
542
|
+
super
|
|
543
|
+
@loaded = (read_init(init, "loaded") || 0).to_i
|
|
544
|
+
@total = (read_init(init, "total") || 0).to_i
|
|
545
|
+
@length_computable = !!read_init(init, "lengthComputable")
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def __js_get__(key)
|
|
549
|
+
case key
|
|
550
|
+
when "loaded"
|
|
551
|
+
@loaded
|
|
552
|
+
when "total"
|
|
553
|
+
@total
|
|
554
|
+
when "lengthComputable"
|
|
555
|
+
@length_computable
|
|
556
|
+
else
|
|
557
|
+
super
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# `Touch` — a single touch point on a touch-capable surface.
|
|
563
|
+
# Constructed by tests; tied to a target element and coordinate set.
|
|
564
|
+
#
|
|
565
|
+
# Spec: https://w3c.github.io/touch-events/#touch-interface
|
|
566
|
+
class Touch
|
|
567
|
+
attr_reader(
|
|
568
|
+
:identifier,
|
|
569
|
+
:target,
|
|
570
|
+
:client_x,
|
|
571
|
+
:client_y,
|
|
572
|
+
:page_x,
|
|
573
|
+
:page_y,
|
|
574
|
+
:screen_x,
|
|
575
|
+
:screen_y,
|
|
576
|
+
:radius_x,
|
|
577
|
+
:radius_y,
|
|
578
|
+
:rotation_angle,
|
|
579
|
+
:force
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
def initialize(init = {})
|
|
583
|
+
@identifier = (init["identifier"] || init[:identifier] || 0).to_i
|
|
584
|
+
@target = init["target"] || init[:target]
|
|
585
|
+
@client_x = (init["clientX"] || init[:clientX] || 0).to_f
|
|
586
|
+
@client_y = (init["clientY"] || init[:clientY] || 0).to_f
|
|
587
|
+
@page_x = (init["pageX"] || init[:pageX] || @client_x).to_f
|
|
588
|
+
@page_y = (init["pageY"] || init[:pageY] || @client_y).to_f
|
|
589
|
+
@screen_x = (init["screenX"] || init[:screenX] || @client_x).to_f
|
|
590
|
+
@screen_y = (init["screenY"] || init[:screenY] || @client_y).to_f
|
|
591
|
+
@radius_x = (init["radiusX"] || init[:radiusX] || 0).to_f
|
|
592
|
+
@radius_y = (init["radiusY"] || init[:radiusY] || 0).to_f
|
|
593
|
+
@rotation_angle = (init["rotationAngle"] || init[:rotationAngle] || 0).to_f
|
|
594
|
+
@force = (init["force"] || init[:force] || 0).to_f
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def __js_get__(key)
|
|
598
|
+
case key
|
|
599
|
+
when "identifier"
|
|
600
|
+
@identifier
|
|
601
|
+
when "target"
|
|
602
|
+
@target
|
|
603
|
+
when "clientX"
|
|
604
|
+
@client_x
|
|
605
|
+
when "clientY"
|
|
606
|
+
@client_y
|
|
607
|
+
when "pageX"
|
|
608
|
+
@page_x
|
|
609
|
+
when "pageY"
|
|
610
|
+
@page_y
|
|
611
|
+
when "screenX"
|
|
612
|
+
@screen_x
|
|
613
|
+
when "screenY"
|
|
614
|
+
@screen_y
|
|
615
|
+
when "radiusX"
|
|
616
|
+
@radius_x
|
|
617
|
+
when "radiusY"
|
|
618
|
+
@radius_y
|
|
619
|
+
when "rotationAngle"
|
|
620
|
+
@rotation_angle
|
|
621
|
+
when "force"
|
|
622
|
+
@force
|
|
623
|
+
end
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
# `TouchList` — immutable, indexed collection of Touch points.
|
|
628
|
+
class TouchList
|
|
629
|
+
include Enumerable
|
|
630
|
+
|
|
631
|
+
def initialize(touches = [])
|
|
632
|
+
@touches = touches.to_a.freeze
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def length
|
|
636
|
+
@touches.length
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
def item(index)
|
|
640
|
+
@touches[index.to_i]
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def [](index)
|
|
644
|
+
item(index)
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
def each(&block)
|
|
648
|
+
@touches.each(&block)
|
|
649
|
+
self
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
def __js_get__(key)
|
|
653
|
+
case key
|
|
654
|
+
when "length"
|
|
655
|
+
length
|
|
656
|
+
else
|
|
657
|
+
item(key.to_i) if key.is_a?(Integer) || key.to_s.match?(/\A-?\d+\z/)
|
|
658
|
+
end
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
662
|
+
JS_METHOD_NAMES = %w[item].freeze
|
|
663
|
+
def __js_method_names__
|
|
664
|
+
JS_METHOD_NAMES
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
def __js_call__(method, args)
|
|
668
|
+
case method
|
|
669
|
+
when "item"
|
|
670
|
+
item(args[0])
|
|
671
|
+
end
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
# `TouchEvent` — fires for touchstart / touchmove / touchend /
|
|
676
|
+
# touchcancel. Carries three TouchLists plus modifier keys.
|
|
677
|
+
#
|
|
678
|
+
# Spec: https://w3c.github.io/touch-events/#touchevent-interface
|
|
679
|
+
class TouchEvent < Event
|
|
680
|
+
def initialize(type, init = nil)
|
|
681
|
+
super
|
|
682
|
+
@touches = TouchList.new(Array(read_init(init, "touches") || []))
|
|
683
|
+
@target_touches = TouchList.new(Array(read_init(init, "targetTouches") || []))
|
|
684
|
+
@changed_touches = TouchList.new(Array(read_init(init, "changedTouches") || []))
|
|
685
|
+
@alt_key = !!read_init(init, "altKey")
|
|
686
|
+
@ctrl_key = !!read_init(init, "ctrlKey")
|
|
687
|
+
@shift_key = !!read_init(init, "shiftKey")
|
|
688
|
+
@meta_key = !!read_init(init, "metaKey")
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
attr_reader :touches, :target_touches, :changed_touches
|
|
692
|
+
|
|
693
|
+
def __js_get__(key)
|
|
694
|
+
case key
|
|
695
|
+
when "touches"
|
|
696
|
+
@touches
|
|
697
|
+
when "targetTouches"
|
|
698
|
+
@target_touches
|
|
699
|
+
when "changedTouches"
|
|
700
|
+
@changed_touches
|
|
701
|
+
when "altKey"
|
|
702
|
+
@alt_key
|
|
703
|
+
when "ctrlKey"
|
|
704
|
+
@ctrl_key
|
|
705
|
+
when "shiftKey"
|
|
706
|
+
@shift_key
|
|
707
|
+
when "metaKey"
|
|
708
|
+
@meta_key
|
|
709
|
+
else
|
|
710
|
+
super
|
|
711
|
+
end
|
|
712
|
+
end
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
# `ClipboardEvent` — fires for copy / cut / paste. Carries the
|
|
716
|
+
# `clipboardData` payload as a DataTransfer.
|
|
717
|
+
#
|
|
718
|
+
# Spec: https://w3c.github.io/clipboard-apis/#clipboard-event-interface
|
|
719
|
+
class ClipboardEvent < Event
|
|
720
|
+
def initialize(type, init = nil)
|
|
721
|
+
super
|
|
722
|
+
@clipboard_data = read_init(init, "clipboardData")
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
attr_reader :clipboard_data
|
|
726
|
+
|
|
727
|
+
def __js_get__(key)
|
|
728
|
+
case key
|
|
729
|
+
when "clipboardData"
|
|
730
|
+
@clipboard_data
|
|
731
|
+
else
|
|
732
|
+
super
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
# `CompositionEvent` — IME composition events (compositionstart /
|
|
738
|
+
# compositionupdate / compositionend). `data` holds the composing text.
|
|
739
|
+
class CompositionEvent < Event
|
|
740
|
+
def initialize(type, init = nil)
|
|
741
|
+
super
|
|
742
|
+
@data = (read_init(init, "data") || "").to_s
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
attr_reader :data
|
|
746
|
+
|
|
747
|
+
def __js_get__(key)
|
|
748
|
+
case key
|
|
749
|
+
when "data"
|
|
750
|
+
@data
|
|
751
|
+
else
|
|
752
|
+
super
|
|
753
|
+
end
|
|
754
|
+
end
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
# `WheelEvent` — wheel-scroll events. Inherits MouseEvent (coords +
|
|
758
|
+
# modifier keys) and adds delta values + a delta mode.
|
|
759
|
+
class WheelEvent < MouseEvent
|
|
760
|
+
DOM_DELTA_PIXEL = 0
|
|
761
|
+
DOM_DELTA_LINE = 1
|
|
762
|
+
DOM_DELTA_PAGE = 2
|
|
763
|
+
|
|
764
|
+
def initialize(type, init = nil)
|
|
765
|
+
super
|
|
766
|
+
@delta_x = (read_init(init, "deltaX") || 0).to_f
|
|
767
|
+
@delta_y = (read_init(init, "deltaY") || 0).to_f
|
|
768
|
+
@delta_z = (read_init(init, "deltaZ") || 0).to_f
|
|
769
|
+
@delta_mode = (read_init(init, "deltaMode") || 0).to_i
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
attr_reader :delta_x, :delta_y, :delta_z, :delta_mode
|
|
773
|
+
|
|
774
|
+
def __js_get__(key)
|
|
775
|
+
case key
|
|
776
|
+
when "deltaX"
|
|
777
|
+
@delta_x
|
|
778
|
+
when "deltaY"
|
|
779
|
+
@delta_y
|
|
780
|
+
when "deltaZ"
|
|
781
|
+
@delta_z
|
|
782
|
+
when "deltaMode"
|
|
783
|
+
@delta_mode
|
|
784
|
+
else
|
|
785
|
+
super
|
|
786
|
+
end
|
|
787
|
+
end
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
# `FocusEvent` — focus / blur / focusin / focusout. `relatedTarget`
|
|
791
|
+
# is the element gaining/losing focus on the other side.
|
|
792
|
+
class FocusEvent < Event
|
|
793
|
+
def initialize(type, init = nil)
|
|
794
|
+
super
|
|
795
|
+
@related_target = read_init(init, "relatedTarget")
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
attr_reader :related_target
|
|
799
|
+
|
|
800
|
+
def __js_get__(key)
|
|
801
|
+
case key
|
|
802
|
+
when "relatedTarget"
|
|
803
|
+
@related_target
|
|
804
|
+
else
|
|
805
|
+
super
|
|
806
|
+
end
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
# `BeforeUnloadEvent` — `beforeunload` event. Setting `return_value`
|
|
811
|
+
# to a non-empty string (or calling `preventDefault`) signals the
|
|
812
|
+
# browser to prompt the user before navigating away.
|
|
813
|
+
class BeforeUnloadEvent < Event
|
|
814
|
+
def initialize(type = "beforeunload", init = nil)
|
|
815
|
+
super
|
|
816
|
+
@return_value = (read_init(init, "returnValue") || "").to_s
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
attr_accessor :return_value
|
|
820
|
+
|
|
821
|
+
def __js_get__(key)
|
|
822
|
+
case key
|
|
823
|
+
when "returnValue"
|
|
824
|
+
@return_value
|
|
825
|
+
else
|
|
826
|
+
super
|
|
827
|
+
end
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
def __js_set__(key, value)
|
|
831
|
+
case key
|
|
832
|
+
when "returnValue"
|
|
833
|
+
@return_value = value.to_s
|
|
834
|
+
else
|
|
835
|
+
super
|
|
836
|
+
end
|
|
837
|
+
end
|
|
838
|
+
end
|
|
839
|
+
|
|
446
840
|
# `AbortController` + `AbortSignal` subset. Signal fires an
|
|
447
841
|
# "abort" event and flips `[:aborted]` to true when the controller's
|
|
448
842
|
# `abort()` is called; otherwise it stays inert.
|
|
@@ -453,7 +847,7 @@ module Dommy
|
|
|
453
847
|
# signal. Convenient for APIs that need an already-cancelled token.
|
|
454
848
|
def self.abort(reason = nil)
|
|
455
849
|
signal = new
|
|
456
|
-
signal.
|
|
850
|
+
signal.__internal_mark_aborted__(reason)
|
|
457
851
|
signal
|
|
458
852
|
end
|
|
459
853
|
|
|
@@ -466,9 +860,9 @@ module Dommy
|
|
|
466
860
|
signal = new
|
|
467
861
|
reason = DOMException::TimeoutError.new("operation timed out")
|
|
468
862
|
if scheduler
|
|
469
|
-
scheduler.set_timeout(proc { signal.
|
|
863
|
+
scheduler.set_timeout(proc { signal.__internal_mark_aborted__(reason) }, ms.to_i)
|
|
470
864
|
else
|
|
471
|
-
signal.
|
|
865
|
+
signal.__internal_schedule_thread_timeout__(ms.to_i, reason)
|
|
472
866
|
end
|
|
473
867
|
|
|
474
868
|
signal
|
|
@@ -483,12 +877,12 @@ module Dommy
|
|
|
483
877
|
list = Array(signals).select { |s| s.is_a?(AbortSignal) }
|
|
484
878
|
already = list.find(&:aborted?)
|
|
485
879
|
if already
|
|
486
|
-
composite.
|
|
880
|
+
composite.__internal_mark_aborted__(already.reason)
|
|
487
881
|
return composite
|
|
488
882
|
end
|
|
489
883
|
|
|
490
884
|
list.each do |sig|
|
|
491
|
-
sig.add_event_listener("abort", proc { composite.
|
|
885
|
+
sig.add_event_listener("abort", proc { composite.__internal_mark_aborted__(sig.reason) })
|
|
492
886
|
end
|
|
493
887
|
|
|
494
888
|
composite
|
|
@@ -501,11 +895,11 @@ module Dommy
|
|
|
501
895
|
|
|
502
896
|
# Background-thread timeout used by `AbortSignal.timeout` when no
|
|
503
897
|
# scheduler is provided. Kept package-private; tests can also
|
|
504
|
-
# drive the abort manually via `
|
|
505
|
-
def
|
|
898
|
+
# drive the abort manually via `__internal_mark_aborted__`.
|
|
899
|
+
def __internal_schedule_thread_timeout__(ms, reason)
|
|
506
900
|
Thread.new do
|
|
507
901
|
sleep(ms.to_f / 1000.0)
|
|
508
|
-
|
|
902
|
+
__internal_mark_aborted__(reason)
|
|
509
903
|
end
|
|
510
904
|
|
|
511
905
|
nil
|
|
@@ -542,6 +936,12 @@ module Dommy
|
|
|
542
936
|
nil
|
|
543
937
|
end
|
|
544
938
|
|
|
939
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
940
|
+
JS_METHOD_NAMES = %w[addEventListener removeEventListener dispatchEvent throwIfAborted].freeze
|
|
941
|
+
def __js_method_names__
|
|
942
|
+
JS_METHOD_NAMES
|
|
943
|
+
end
|
|
944
|
+
|
|
545
945
|
def __js_call__(method, args)
|
|
546
946
|
case method
|
|
547
947
|
when "addEventListener"
|
|
@@ -555,7 +955,7 @@ module Dommy
|
|
|
555
955
|
end
|
|
556
956
|
end
|
|
557
957
|
|
|
558
|
-
def
|
|
958
|
+
def __internal_mark_aborted__(reason = nil)
|
|
559
959
|
return if @aborted
|
|
560
960
|
|
|
561
961
|
@aborted = true
|
|
@@ -579,10 +979,16 @@ module Dommy
|
|
|
579
979
|
nil
|
|
580
980
|
end
|
|
581
981
|
|
|
982
|
+
# Methods routed through __js_call__ (keep in sync with its when-arms).
|
|
983
|
+
JS_METHOD_NAMES = %w[abort].freeze
|
|
984
|
+
def __js_method_names__
|
|
985
|
+
JS_METHOD_NAMES
|
|
986
|
+
end
|
|
987
|
+
|
|
582
988
|
def __js_call__(method, args)
|
|
583
989
|
case method
|
|
584
990
|
when "abort"
|
|
585
|
-
@signal.
|
|
991
|
+
@signal.__internal_mark_aborted__(args[0])
|
|
586
992
|
end
|
|
587
993
|
end
|
|
588
994
|
end
|