Module: Dommy::Internal::ChildNode

Included in:
CharacterDataNode, ParentNode
Defined in:
lib/dommy/internal/child_node.rb

Overview

Shared ChildNode surface (WHATWG DOM before / after / replaceWith) plus the argument-coercion + childList-notification primitives those and ParentNode's own mutators build on. Included by both ParentNode (Element / Fragment / ShadowRoot) and the leaf CharacterData nodes (Text / Comment / ProcessingInstruction) — a leaf can be moved with before/after but must NOT gain appendChild/insertBefore, so those stay in ParentNode.

Includers must expose @__node__ (the backing node) and @document.

Instance Method Summary collapse

Instance Method Details

#child_node_after(args) ⇒ Object

ChildNode#after — insert nodes as following siblings of @__node__.



36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/dommy/internal/child_node.rb', line 36

def child_node_after(args)
  parent = @__node__.parent
  return nil unless parent

  arg_nodes = backend_nodes_in(args)
  viable_next = @__node__.next_sibling
  viable_next = viable_next.next_sibling while viable_next && arg_nodes.any? { |n| n == viable_next }

  nodes = args.flat_map { |arg| detach_dom_nodes(arg) }
  insert_child_nodes(nodes, viable_next, parent)
  notify_child_list(added: nodes, target: parent)
  nil
end

#child_node_before(args) ⇒ Object

ChildNode#before — insert nodes as preceding siblings of @__node__. Follows the spec's "viable previous sibling" dance: the reference child is the first preceding sibling NOT among the argument nodes, resolved AFTER the arguments are detached (converting them into a node removes them from their old parents). Nodes are then inserted forward before the (fixed) reference — reversing would emit them backwards.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/dommy/internal/child_node.rb', line 20

def child_node_before(args)
  parent = @__node__.parent
  return nil unless parent

  arg_nodes = backend_nodes_in(args)
  viable_prev = @__node__.previous_sibling
  viable_prev = viable_prev.previous_sibling while viable_prev && arg_nodes.any? { |n| n == viable_prev }

  nodes = args.flat_map { |arg| detach_dom_nodes(arg) }
  ref = viable_prev.nil? ? parent.children.first : viable_prev.next_sibling
  insert_child_nodes(nodes, ref, parent)
  notify_child_list(added: nodes, target: parent)
  nil
end

#child_node_replace_with(args) ⇒ Object

ChildNode#replaceWith — replace @__node__ with the given nodes.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/dommy/internal/child_node.rb', line 51

def child_node_replace_with(args)
  parent = @__node__.parent
  return nil unless parent

  arg_nodes = backend_nodes_in(args)
  viable_next = @__node__.next_sibling
  viable_next = viable_next.next_sibling while viable_next && arg_nodes.any? { |n| n == viable_next }

  removed = @__node__
  nodes = args.flat_map { |arg| detach_dom_nodes(arg) }
  if @__node__.parent == parent
    # `@__node__` survived the conversion (it wasn't among the arguments):
    # insert the new nodes before it, then unlink it — a true replace.
    nodes.each { |n| @__node__.add_previous_sibling(n) }
    @__node__.unlink
    notify_child_list(added: nodes, removed: [removed], target: parent)
  else
    # `@__node__` was itself an argument, so the conversion already moved
    # it into `nodes`; pre-insert the set before the viable next sibling.
    insert_child_nodes(nodes, viable_next, parent)
    notify_child_list(added: nodes, target: parent)
  end
  nil
end

#nullable_dom_string(value) ⇒ Object

WebIDL nullable DOMString coercion (DOMString?): JS null and undefined both become the null value, which callers treat as the empty string.



78
79
80
81
82
# File 'lib/dommy/internal/child_node.rb', line 78

def nullable_dom_string(value)
  return "" if value.nil? || (defined?(Bridge::UNDEFINED) && value.equal?(Bridge::UNDEFINED))

  value.to_s
end

#validate_insert_before_ref!(args) ⇒ Object

WebIDL check for insertBefore(node, child): child is a required but nullable Node, so a missing argument or a value that is neither a Node nor null/undefined is a TypeError before any DOM step runs. Now safe since the seeded stubs report the correct arity (2), so .length-based WPT helpers always pass the reference argument.

Raises:



89
90
91
92
93
94
95
96
97
# File 'lib/dommy/internal/child_node.rb', line 89

def validate_insert_before_ref!(args)
  raise Bridge::TypeError, "insertBefore requires 2 arguments." if args.length < 2

  ref = args[1]
  return if ref.nil? || (defined?(Bridge::UNDEFINED) && ref.equal?(Bridge::UNDEFINED))
  return if ref.is_a?(Dommy::Node)

  raise Bridge::TypeError, "The reference child is not a Node."
end