Class: Dommy::Event

Inherits:
Object
  • Object
show all
Includes:
Bridge::Methods
Defined in:
lib/dommy/event.rb

Constant Summary collapse

NONE =
0
CAPTURING_PHASE =
1
AT_TARGET =
2
BUBBLING_PHASE =
3

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Bridge::Methods

included

Constructor Details

#initialize(type, init = nil) ⇒ Event

Returns a new instance of Event.



375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'lib/dommy/event.rb', line 375

def initialize(type, init = nil)
  @type = type.to_s
  @bubbles = !!read_init(init, "bubbles")
  @cancelable = !!read_init(init, "cancelable")
  @composed = !!read_init(init, "composed")
  @default_prevented = false
  @propagation_stopped = false
  @immediate_propagation_stopped = false
  # Set while a passive listener runs, so preventDefault() is a no-op (per
  # the passive listener flag in the DOM spec).
  @in_passive_listener = false
  @target = nil
  @current_target = nil
  @event_phase = NONE
  @composed_path = []
  # `timeStamp` is the high-resolution timestamp at construction
  # in ms (browser uses performance.now). We use monotonic time
  # for determinism across spec runs.
  @time_stamp = (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000.0)
  @trusted = false
end

Instance Attribute Details

#typeObject (readonly)

Returns the value of attribute type.



404
405
406
# File 'lib/dommy/event.rb', line 404

def type
  @type
end

Instance Method Details

#__internal_clear_propagation_flags__Object

End-of-dispatch cleanup: the dispatch algorithm unsets the stop-propagation and stop-immediate-propagation flags (but NOT the canceled flag), so the same event object can be dispatched again. A stopPropagation() issued before the next dispatch is still honored — only the post-dispatch state is cleared here.



441
442
443
444
445
# File 'lib/dommy/event.rb', line 441

def __internal_clear_propagation_flags__
  @propagation_stopped = false
  @immediate_propagation_stopped = false
  nil
end

#__internal_mark_trusted__Object

Mark this event as UA-generated (isTrusted === true). Used by the host for events it fires itself (e.g. AbortSignal's "abort").



399
400
401
402
# File 'lib/dommy/event.rb', line 399

def __internal_mark_trusted__
  @trusted = true
  self
end

#__internal_prepare_for_dispatch__(target) ⇒ Object



432
433
434
# File 'lib/dommy/event.rb', line 432

def __internal_prepare_for_dispatch__(target)
  @target ||= target
end

#__internal_record_path__(targets) ⇒ Object

Filled in by EventTarget#dispatch_event as the event walks the bubble path so composedPath() returns the right list.

Per spec, load events do not propagate to the Window when composed paths are computed (resource-finished signal stays at the target).



577
578
579
580
581
582
583
# File 'lib/dommy/event.rb', line 577

def __internal_record_path__(targets)
  @composed_path = if @type == "load"
    targets.reject { |t| t.is_a?(Window) }
  else
    targets
  end
end

#__internal_run_passive__Object

Run a block with the passive-listener flag set, so any preventDefault() inside it is neutralized. Restores the prior flag afterward.



424
425
426
427
428
429
430
# File 'lib/dommy/event.rb', line 424

def __internal_run_passive__
  previous = @in_passive_listener
  @in_passive_listener = true
  yield
ensure
  @in_passive_listener = previous
end

#__internal_set_current_target__(target) ⇒ Object



447
448
449
# File 'lib/dommy/event.rb', line 447

def __internal_set_current_target__(target)
  @current_target = target
end

#__internal_set_dispatch_flag__(flag) ⇒ Object

Set while the event is being dispatched, so initEvent() can short-circuit.



550
551
552
553
# File 'lib/dommy/event.rb', line 550

def __internal_set_dispatch_flag__(flag)
  @dispatch_flag = flag
  nil
end

#__internal_set_event_phase__(phase) ⇒ Object



451
452
453
# File 'lib/dommy/event.rb', line 451

def __internal_set_event_phase__(phase)
  @event_phase = phase
end

#__js_call__(method, args) ⇒ Object



522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
# File 'lib/dommy/event.rb', line 522

def __js_call__(method, args)
  case method
  when "preventDefault"
    # A passive listener's preventDefault() is a no-op (DOM "passive listener
    # flag"), so the default action proceeds — what Stimulus's :passive
    # action option relies on.
    @default_prevented = true if @cancelable && !@in_passive_listener
    nil
  when "stopPropagation"
    @propagation_stopped = true
    nil
  when "stopImmediatePropagation"
    @propagation_stopped = true
    @immediate_propagation_stopped = true
    nil
  when "composedPath"
    # composedPath() is the event path only while the event is being
    # dispatched; once dispatch finishes (currentTarget is null) it is empty.
    @dispatch_flag ? @composed_path.dup : []
  when "initEvent"
    # WebIDL: the `type` argument is mandatory.
    raise Bridge::TypeError, "initEvent requires a type argument" if args.empty?

    init_event(args[0], args[1], args[2])
  end
end

#__js_get__(key) ⇒ Object



455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
# File 'lib/dommy/event.rb', line 455

def __js_get__(key)
  case key
  when "type"
    @type
  when "bubbles"
    @bubbles
  when "cancelable"
    @cancelable
  when "composed"
    @composed
  when "defaultPrevented"
    @default_prevented
  when "returnValue"
    # Legacy alias: false once the default has been prevented, else true.
    !@default_prevented
  when "isTrusted"
    # Script-created events are untrusted; a UA-fired event (e.g. an
    # AbortSignal's "abort") is marked trusted via __internal_mark_trusted__.
    @trusted == true
  when "target", "srcElement"
    # srcElement is a legacy alias of target — null (not undefined) when unset.
    @target
  when "currentTarget"
    @current_target
  when "timeStamp"
    @time_stamp
  when "cancelBubble"
    @propagation_stopped
  when "immediatePropagationStopped"
    # Non-standard, but real frameworks feature-detect it: Stimulus's
    # extendEvent does `"immediatePropagationStopped" in event` and, when
    # present, reads the flag directly instead of installing its own
    # tracking shim. Expose the live flag so that path stays correct (it
    # also keeps `"immediatePropagationStopped" in event` from being a
    # present-but-undefined no-op).
    @immediate_propagation_stopped
  when "eventPhase"
    event_phase
  else
    # An unknown property is genuinely absent: JS `undefined` and
    # `"x" in event` false — e.g. a non-dictionary member passed to the
    # constructor (`new Event("x", {sweet: 1}).sweet`) is not reflected on the
    # event. Genuinely-null DOM attributes (target/currentTarget/…) are
    # explicit cases above and still return nil.
    Bridge::ABSENT
  end
end

#__js_set__(key, value) ⇒ Object



503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
# File 'lib/dommy/event.rb', line 503

def __js_set__(key, value)
  case key
  when "cancelBubble"
    # Setting to truthy stops propagation; spec quirk that
    # `cancelBubble = false` does NOT un-stop (browser observation).
    @propagation_stopped = true if value
  when "returnValue"
    # Legacy alias: returnValue = false cancels the event (like
    # preventDefault); a truthy value does not un-cancel.
    @default_prevented = true if !value && @cancelable
  else
    return Bridge::UNHANDLED
  end

  nil
end

#bubbles?Boolean

Returns:

  • (Boolean)


406
407
408
# File 'lib/dommy/event.rb', line 406

def bubbles?
  @bubbles
end

#default_prevented?Boolean

Returns:

  • (Boolean)


410
411
412
# File 'lib/dommy/event.rb', line 410

def default_prevented?
  @default_prevented
end

#immediate_propagation_stopped?Boolean

Returns:

  • (Boolean)


418
419
420
# File 'lib/dommy/event.rb', line 418

def immediate_propagation_stopped?
  @immediate_propagation_stopped
end

#init_event(type, bubbles = false, cancelable = false) ⇒ Object

Deprecated Event#initEvent(type, bubbles, cancelable) — older browsers used document.createEvent("Event").initEvent(...). Resets internal flags as a side effect.



558
559
560
561
562
563
564
565
566
567
568
569
# File 'lib/dommy/event.rb', line 558

def init_event(type, bubbles = false, cancelable = false)
  # Spec: initEvent is a no-op while the event is being dispatched.
  return nil if @dispatch_flag

  @type = type.to_s
  @bubbles = !!bubbles
  @cancelable = !!cancelable
  @default_prevented = false
  @propagation_stopped = false
  @immediate_propagation_stopped = false
  nil
end

#propagation_stopped?Boolean

Returns:

  • (Boolean)


414
415
416
# File 'lib/dommy/event.rb', line 414

def propagation_stopped?
  @propagation_stopped
end