Module: Dommy::Node

Included in:
Attr, CharacterDataNode, Document, DocumentType, Element, Fragment, ShadowRoot
Defined in:
lib/dommy/node.rb

Overview

Node — common base mixin. All node-like classes (Element, TextNode, CommentNode, CharacterDataNode, Document, Fragment, DocumentType, ShadowRoot) include this so el.is_a?(Dommy::Node) works.

Real classes already define nodeType / nodeName / nodeValue / parentNode / isConnected / cloneNode independently; this module is primarily an identity marker. Adding new shared methods later is straightforward.

Constant Summary collapse

ELEMENT_NODE =

Standardized nodeType constants — duplicated from Element so callers can refer to Dommy::Node::ELEMENT_NODE without depending on a specific subclass.

1
ATTRIBUTE_NODE =
2
TEXT_NODE =
3
CDATA_SECTION_NODE =
4
PROCESSING_INSTRUCTION_NODE =
7
COMMENT_NODE =
8
DOCUMENT_NODE =
9
DOCUMENT_TYPE_NODE =
10
DOCUMENT_FRAGMENT_NODE =
11
DOCUMENT_POSITION_DISCONNECTED =
0x01
DOCUMENT_POSITION_PRECEDING =
0x02
DOCUMENT_POSITION_FOLLOWING =
0x04
DOCUMENT_POSITION_CONTAINS =
0x08
DOCUMENT_POSITION_CONTAINED_BY =
0x10
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC =
0x20
HTML_NAMESPACE =
"http://www.w3.org/1999/xhtml"
XML_NAMESPACE =
"http://www.w3.org/XML/1998/namespace"
XMLNS_NAMESPACE =
"http://www.w3.org/2000/xmlns/"

Instance Method Summary collapse

Instance Method Details

#compare_document_position(other) ⇒ Object

Node.compareDocumentPosition(other) — a bitmask describing where other sits relative to this node: 0 for the same node, CONTAINS/CONTAINED_BY for ancestor/descendant, PRECEDING/FOLLOWING for tree order, or DISCONNECTED (with a stable IMPLEMENTATION_SPECIFIC|PRECEDING) for unrelated nodes. Generic over any node with a backing Nokogiri node.



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/dommy/node.rb', line 316

def compare_document_position(other)
  return 0 if equal?(other)

  self_node = compare_backend_node(self)
  other_node = compare_backend_node(other)
  unless self_node && other_node
    return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_PRECEDING
  end

  self_ancestors = node_ancestor_chain(self_node)
  other_ancestors = node_ancestor_chain(other_node)

  common = self_ancestors.find { |a| other_ancestors.include?(a) }
  unless common
    return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_PRECEDING
  end
  return DOCUMENT_POSITION_CONTAINED_BY | DOCUMENT_POSITION_FOLLOWING if common == self_node
  return DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING if common == other_node

  self_branch = node_branch_under(common, self_ancestors)
  other_branch = node_branch_under(common, other_ancestors)
  common.children.each do |child|
    return DOCUMENT_POSITION_FOLLOWING if child == self_branch
    return DOCUMENT_POSITION_PRECEDING if child == other_branch
  end
  DOCUMENT_POSITION_DISCONNECTED
end

#get_root_node(_options = nil) ⇒ Object

Node.getRootNode — the topmost ancestor of this node (the document, a ShadowRoot, a detached subtree root, or the node itself). Generic default for any node backed by a Nokogiri node; classes with special roots (Element's shadow handling) override it.



348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/dommy/node.rb', line 348

def get_root_node(_options = nil)
  return self unless respond_to?(:__dommy_backend_node__) && instance_variable_defined?(:@document)

  node = __dommy_backend_node__
  node = node.parent while node.respond_to?(:parent) && node.parent
  # The topmost node of an attached subtree is the Nokogiri document, which
  # has no element wrapper — map it to the Document. A detached node's root is
  # itself.
  return @document if @document && node.equal?(@document.backend_doc)

  (@document && @document.wrap_node(node)) || self
end

#is_default_namespace(namespace) ⇒ Object

Node.isDefaultNamespace(namespace) — true if namespace (null/"" → null) is the default namespace in this node's scope.



420
421
422
423
424
# File 'lib/dommy/node.rb', line 420

def is_default_namespace(namespace)
  ns = namespace.nil? ? nil : namespace.to_s
  ns = nil if ns == ""
  lookup_namespace_uri(nil) == ns
end

#is_equal_node(other) ⇒ Object

WHATWG Node.isEqualNode — deep structural equality (type-specific data plus equal, in-order, recursively-equal children). Available on every node class that includes Node; the bridge routes "isEqualNode" here.



302
303
304
# File 'lib/dommy/node.rb', line 302

def is_equal_node(other)
  Internal::NodeEquality.equal?(self, other)
end

#is_same_node(other) ⇒ Object

Node.isSameNode — strict reference identity (deprecated alias for ===).



307
308
309
# File 'lib/dommy/node.rb', line 307

def is_same_node(other)
  equal?(other)
end

#lookup_namespace_uri(prefix) ⇒ Object

Node.lookupNamespaceURI(prefix) — WHATWG "locate a namespace": walk from the nearest enclosing element up its ancestors, matching the element's own namespace (by prefix) and its xmlns declarations. xml / xmlns are implicitly bound. Non-element scopes (fragment, doctype, disconnected attr) locate nothing.



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/dommy/node.rb', line 370

def lookup_namespace_uri(prefix)
  wanted = namespace_prefix_arg(prefix)
  el = starting_namespace_element
  return nil unless el
  return XML_NAMESPACE if wanted == "xml"
  return XMLNS_NAMESPACE if wanted == "xmlns"

  each_namespace_ancestor(el) do |node|
    ns = node.namespace_uri
    return ns if ns && !ns.to_s.empty? && wrapper_prefix(node) == wanted

    node.attributes.each do |attr|
      next unless attr.namespace_uri == XMLNS_NAMESPACE

      ap = normalize_ns_prefix(attr.__js_get__("prefix"))
      value = attr.value.to_s
      if ap == "xmlns" && attr.local_name == wanted
        return value.empty? ? nil : value
      elsif ap.nil? && attr.local_name == "xmlns" && wanted.nil?
        return value.empty? ? nil : value
      end
    end
  end
  nil
end

#lookup_prefix(namespace) ⇒ Object

Node.lookupPrefix(namespace) — WHATWG "locate a prefix": a prefix bound to namespace in this node's scope, or null.



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/dommy/node.rb', line 398

def lookup_prefix(namespace)
  ns = namespace.to_s
  return nil if ns.empty?

  el = starting_namespace_element
  return nil unless el

  each_namespace_ancestor(el) do |node|
    return wrapper_prefix(node) if node.namespace_uri == ns && wrapper_prefix(node)

    node.attributes.each do |attr|
      next unless attr.namespace_uri == XMLNS_NAMESPACE
      next unless normalize_ns_prefix(attr.__js_get__("prefix")) == "xmlns"

      return attr.local_name if attr.value.to_s == ns
    end
  end
  nil
end