Class: Dommy::Attr

Inherits:
Object
  • Object
show all
Includes:
Bridge::Methods, EventTarget, Node
Defined in:
lib/dommy/attr.rb

Overview

Attr — wraps an HTML attribute as a Node-like object. In real DOM each attribute on an element is an Attr; el.getAttributeNode returns the instance, attr.value = "x" mutates the element's attribute, attr.ownerElement points back to the element.

We represent two states:

- "owned" — the Attr is attached to an Element. value reads/writes
go through the element's Makiri attribute slot.
- "detached" — created via `document.createAttribute(name)` but
not yet attached. Value is stored locally; `setAttributeNode`
transfers it to an element.

Constant Summary

Constants included from Node

Node::ATTRIBUTE_NODE, Node::CDATA_SECTION_NODE, Node::COMMENT_NODE, Node::DOCUMENT_FRAGMENT_NODE, Node::DOCUMENT_NODE, Node::DOCUMENT_POSITION_CONTAINED_BY, Node::DOCUMENT_POSITION_CONTAINS, Node::DOCUMENT_POSITION_DISCONNECTED, Node::DOCUMENT_POSITION_FOLLOWING, Node::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, Node::DOCUMENT_POSITION_PRECEDING, Node::DOCUMENT_TYPE_NODE, Node::ELEMENT_NODE, Node::HTML_NAMESPACE, Node::PROCESSING_INSTRUCTION_NODE, Node::TEXT_NODE, Node::XMLNS_NAMESPACE, Node::XML_NAMESPACE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Bridge::Methods

included

Methods included from EventTarget

#__dommy_dump_event_failure__, #__internal_deliver_event__, #__internal_process_event_handler_return__, #add_event_listener, capture_flag, #deliver_at, #dispatch_event, #event_name_from_on, #invoke_listener_isolated, js_truthy?, #on_handler, #remove_event_listener, #set_on_handler

Methods included from Node

#compare_document_position, #get_root_node, #is_default_namespace, #is_equal_node, #is_same_node, #lookup_namespace_uri, #lookup_prefix

Constructor Details

#initialize(name, owner: nil, value: "", namespace_uri: nil, prefix: nil, local_name: nil, document: nil) ⇒ Attr

Returns a new instance of Attr.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/dommy/attr.rb', line 25

def initialize(name, owner: nil, value: "", namespace_uri: nil, prefix: nil, local_name: nil, document: nil)
  qname = name.to_s
  @owner = owner
  # The node document (for baseURI/ownerDocument when detached from an
  # element). Owned attrs derive it from their owner instead.
  @document = document
  @detached_value = value.to_s
  if namespace_uri && !namespace_uri.to_s.empty?
    # Namespaced attributes preserve case and carry prefix / localName.
    @name = qname
    @namespace_uri = namespace_uri.to_s
    @prefix = prefix
    @local_name = (local_name || qname.split(":", 2).last).to_s
  else
    # Null-namespace attribute: use the qualified name verbatim. The casing is
    # already decided by the caller — the backend stores `setAttribute` names
    # lower-cased (HTML) but preserves `setAttributeNS("", "FOO")` as "FOO",
    # and `createAttribute` lower-cases up front — so re-downcasing here would
    # wrongly fold a case-preserved null-namespace attribute.
    @name = qname
    @namespace_uri = nil
    @prefix = nil
    @local_name = local_name ? local_name.to_s : @name
  end
end

Instance Attribute Details

#local_nameObject (readonly)

Returns the value of attribute local_name.



19
20
21
# File 'lib/dommy/attr.rb', line 19

def local_name
  @local_name
end

#nameObject (readonly)

Returns the value of attribute name.



19
20
21
# File 'lib/dommy/attr.rb', line 19

def name
  @name
end

#namespace_uriObject (readonly)

Returns the value of attribute namespace_uri.



19
20
21
# File 'lib/dommy/attr.rb', line 19

def namespace_uri
  @namespace_uri
end

#prefixObject (readonly)

Returns the value of attribute prefix.



19
20
21
# File 'lib/dommy/attr.rb', line 19

def prefix
  @prefix
end

Instance Method Details

#__internal_attach__(element) ⇒ Object

Internal: called by Element when the attr is being transferred to (or detached from) an Element.



184
185
186
187
188
# File 'lib/dommy/attr.rb', line 184

def __internal_attach__(element)
  @owner = element
  @detached_value = ""
  nil
end

#__internal_detach__Object



190
191
192
193
194
195
# File 'lib/dommy/attr.rb', line 190

def __internal_detach__
  cached = value
  @owner = nil
  @detached_value = cached
  nil
end

#__internal_event_parent__Object



21
22
23
# File 'lib/dommy/attr.rb', line 21

def __internal_event_parent__
  nil
end

#__js_call__(method, args) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/dommy/attr.rb', line 147

def __js_call__(method, args)
  case method
  when "lookupNamespaceURI"
    lookup_namespace_uri(args[0])
  when "lookupPrefix"
    lookup_prefix(args[0])
  when "isDefaultNamespace"
    is_default_namespace(args[0])
  when "cloneNode"
    Attr.new(@name, owner: nil, value: value,
                    namespace_uri: @namespace_uri, prefix: @prefix, local_name: @local_name,
                    document: @document || (@owner.respond_to?(:document) ? @owner.document : nil))
  when "isSameNode"
    is_same_node(args[0])
  when "getRootNode"
    get_root_node(args[0])
  when "compareDocumentPosition"
    compare_document_position(args[0])
  when "appendChild", "insertBefore"
    raise DOMException::HierarchyRequestError, "an Attr may not have children"
  when "removeChild", "replaceChild"
    raise DOMException::NotFoundError, "the node to be removed is not a child of this node"
  when "hasChildNodes"
    false
  when "normalize"
    nil
  when "addEventListener"
    add_event_listener(args[0], args[1], args[2])
  when "removeEventListener"
    remove_event_listener(args[0], args[1], args[2])
  when "dispatchEvent"
    dispatch_event(args[0])
  end
end

#__js_get__(key) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/dommy/attr.rb', line 96

def __js_get__(key)
  case key
  when "name"
    @name
  when "value"
    value
  when "nodeName"
    @name
  when "nodeValue"
    value
  when "textContent"
    # Node.textContent for an Attr returns its value (WHATWG DOM).
    value
  when "ownerElement"
    @owner
  when "ownerDocument"
    owner_document
  when "localName"
    @local_name
  when "namespaceURI"
    @namespace_uri
  when "prefix"
    @prefix
  when "nodeType"
    2
  when "baseURI"
    base_uri
  when "specified"
    # Legacy/useless attribute — always true (WHATWG DOM).
    true
  else
    Bridge::ABSENT
  end
end

#__js_set__(key, val) ⇒ Object



131
132
133
134
135
136
137
138
139
140
# File 'lib/dommy/attr.rb', line 131

def __js_set__(key, val)
  case key
  when "value", "nodeValue", "textContent"
    self.value = val
  else
    return Bridge::UNHANDLED
  end

  nil
end

#base_uriObject

Node.baseURI — the node document's base URL. Derived from the owner element when attached, else the document the attr was created in.



58
59
60
61
62
# File 'lib/dommy/attr.rb', line 58

def base_uri
  return @owner.base_uri if @owner.respond_to?(:base_uri)

  @document&.base_uri
end

#owner_documentObject

Node.ownerDocument — the owner element's current document when attached (so it follows the element across adoptNode), else the creation document.



66
67
68
69
70
# File 'lib/dommy/attr.rb', line 66

def owner_document
  return @owner.document if @owner.respond_to?(:document)

  @document
end

#owner_elementObject

The Element this attr is on, or nil if detached.



52
53
54
# File 'lib/dommy/attr.rb', line 52

def owner_element
  @owner
end

#valueObject



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/dommy/attr.rb', line 72

def value
  if @owner
    if @namespace_uri
      Backend.get_attribute_ns(@owner.__dommy_backend_node__, @namespace_uri, @local_name).to_s
    else
      @owner.__dommy_backend_node__[@name].to_s
    end
  else
    @detached_value
  end
end

#value=(new_value) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
# File 'lib/dommy/attr.rb', line 84

def value=(new_value)
  if @owner
    if @namespace_uri
      @owner.set_attribute_ns(@namespace_uri, @name, new_value.to_s)
    else
      @owner.set_attribute(@name, new_value.to_s)
    end
  else
    @detached_value = new_value.to_s
  end
end