Module: Dommy::Internal::NodeEquality

Defined in:
lib/dommy/internal/node_equality.rb

Overview

WHATWG DOM “equals” / Node.isEqualNode (dom.spec.whatwg.org/#concept-node-equals). Operates on Dommy node wrappers through their bridge-facing accessors (js_get for the per-type data, #child_nodes / #attributes for the structure), so it works uniformly across the heterogeneous node classes.

Class Method Summary collapse

Class Method Details

.attribute_descriptors(node) ⇒ Object



64
65
66
67
68
69
70
71
72
# File 'lib/dommy/internal/node_equality.rb', line 64

def attribute_descriptors(node)
  return [] unless node.respond_to?(:attributes)

  map = node.attributes
  (0...map.length).map do |i|
    attr = map.item(i)
    [attr.namespace_uri, attr.local_name, attr.value]
  end
end

.attributes_equal?(a, b) ⇒ Boolean

Equal attribute lists, order-independent (each attribute of A must have a (namespace, localName, value) match in B, and the counts must match).

Returns:

  • (Boolean)


56
57
58
59
60
61
62
# File 'lib/dommy/internal/node_equality.rb', line 56

def attributes_equal?(a, b)
  attrs_a = attribute_descriptors(a)
  attrs_b = attribute_descriptors(b)
  return false unless attrs_a.length == attrs_b.length

  attrs_a.all? { |x| attrs_b.include?(x) }
end

.children(node) ⇒ Object



74
75
76
77
78
79
# File 'lib/dommy/internal/node_equality.rb', line 74

def children(node)
  return [] unless node.respond_to?(:child_nodes)

  list = node.child_nodes
  list.respond_to?(:to_a) ? list.to_a : (0...list.length).map { |i| list.item(i) }
end

.data_equal?(a, b, type) ⇒ Boolean

Per-type “these two nodes have equal own properties” (children compared separately by #equal?).

Returns:

  • (Boolean)


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/dommy/internal/node_equality.rb', line 29

def data_equal?(a, b, type)
  case type
  when Node::ELEMENT_NODE
    prop(a, "namespaceURI") == prop(b, "namespaceURI") &&
      prop(a, "prefix") == prop(b, "prefix") &&
      prop(a, "localName") == prop(b, "localName") &&
      attributes_equal?(a, b)
  when Node::DOCUMENT_TYPE_NODE
    prop(a, "nodeName") == prop(b, "nodeName") &&
      prop(a, "publicId") == prop(b, "publicId") &&
      prop(a, "systemId") == prop(b, "systemId")
  when Node::PROCESSING_INSTRUCTION_NODE
    prop(a, "target") == prop(b, "target") && prop(a, "data") == prop(b, "data")
  when Node::TEXT_NODE, Node::CDATA_SECTION_NODE, Node::COMMENT_NODE
    prop(a, "data") == prop(b, "data")
  when Node::ATTRIBUTE_NODE
    prop(a, "namespaceURI") == prop(b, "namespaceURI") &&
      prop(a, "localName") == prop(b, "localName") &&
      prop(a, "value") == prop(b, "value")
  else
    # Document / DocumentFragment carry no own properties to compare.
    true
  end
end

.equal?(a, b) ⇒ Boolean

Returns:

  • (Boolean)


12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/dommy/internal/node_equality.rb', line 12

def equal?(a, b)
  return false if b.nil?

  type = prop(a, "nodeType")
  return false unless type == prop(b, "nodeType")
  return false unless data_equal?(a, b, type)

  kids_a = children(a)
  kids_b = children(b)
  return false unless kids_a.length == kids_b.length

  kids_a.each_index { |i| return false unless equal?(kids_a[i], kids_b[i]) }
  true
end

.prop(node, key) ⇒ Object



81
82
83
# File 'lib/dommy/internal/node_equality.rb', line 81

def prop(node, key)
  node.__js_get__(key)
end