dommy 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/dommy/animation.rb +4 -0
  4. data/lib/dommy/attr.rb +11 -5
  5. data/lib/dommy/backend/makiri_adapter.rb +330 -0
  6. data/lib/dommy/backend.rb +114 -33
  7. data/lib/dommy/blob.rb +2 -0
  8. data/lib/dommy/bridge.rb +11 -0
  9. data/lib/dommy/browser.rb +217 -0
  10. data/lib/dommy/compression_streams.rb +4 -0
  11. data/lib/dommy/crypto.rb +4 -0
  12. data/lib/dommy/css.rb +487 -50
  13. data/lib/dommy/custom_elements.rb +2 -2
  14. data/lib/dommy/data_transfer.rb +2 -0
  15. data/lib/dommy/data_uri.rb +35 -0
  16. data/lib/dommy/deferred_response.rb +59 -0
  17. data/lib/dommy/document.rb +386 -228
  18. data/lib/dommy/dom_exception.rb +2 -0
  19. data/lib/dommy/dom_parser.rb +7 -17
  20. data/lib/dommy/element.rb +502 -155
  21. data/lib/dommy/event.rb +240 -9
  22. data/lib/dommy/fetch.rb +152 -34
  23. data/lib/dommy/form_data.rb +2 -0
  24. data/lib/dommy/history.rb +2 -0
  25. data/lib/dommy/html_canvas_element.rb +230 -0
  26. data/lib/dommy/html_collection.rb +5 -6
  27. data/lib/dommy/html_elements.rb +304 -27
  28. data/lib/dommy/interaction/debug.rb +35 -0
  29. data/lib/dommy/interaction/dom_summary.rb +131 -0
  30. data/lib/dommy/interaction/driver.rb +244 -0
  31. data/lib/dommy/interaction/event_synthesis.rb +56 -0
  32. data/lib/dommy/interaction/field_interactor.rb +117 -0
  33. data/lib/dommy/interaction/form_submission.rb +268 -0
  34. data/lib/dommy/interaction/locator.rb +158 -0
  35. data/lib/dommy/interaction/role_query.rb +58 -0
  36. data/lib/dommy/interaction.rb +32 -0
  37. data/lib/dommy/internal/accessibility_tree.rb +215 -0
  38. data/lib/dommy/internal/accessible_description.rb +38 -0
  39. data/lib/dommy/internal/accessible_name.rb +301 -0
  40. data/lib/dommy/internal/aria_role.rb +252 -0
  41. data/lib/dommy/internal/aria_snapshot.rb +64 -0
  42. data/lib/dommy/internal/aria_state.rb +151 -0
  43. data/lib/dommy/internal/css/calc.rb +242 -0
  44. data/lib/dommy/internal/css/cascade.rb +430 -0
  45. data/lib/dommy/internal/css/color.rb +381 -0
  46. data/lib/dommy/internal/css/computed_style_declaration.rb +130 -0
  47. data/lib/dommy/internal/css/counters.rb +227 -0
  48. data/lib/dommy/internal/css/custom_properties.rb +183 -0
  49. data/lib/dommy/internal/css/media_query.rb +302 -0
  50. data/lib/dommy/internal/css/parser.rb +265 -0
  51. data/lib/dommy/internal/css/property_registry.rb +512 -0
  52. data/lib/dommy/internal/css/rule_index.rb +494 -0
  53. data/lib/dommy/internal/css/supports.rb +158 -0
  54. data/lib/dommy/internal/css/ua_stylesheet.rb +53 -0
  55. data/lib/dommy/internal/css_pseudo_handlers.rb +283 -42
  56. data/lib/dommy/internal/css_rule_text.rb +160 -0
  57. data/lib/dommy/internal/dom_matching.rb +80 -9
  58. data/lib/dommy/internal/element_matching.rb +109 -0
  59. data/lib/dommy/internal/global_functions.rb +33 -0
  60. data/lib/dommy/internal/mutation_coordinator.rb +95 -4
  61. data/lib/dommy/internal/namespaces.rb +49 -5
  62. data/lib/dommy/internal/node_wrapper_cache.rb +163 -26
  63. data/lib/dommy/internal/parent_node.rb +82 -5
  64. data/lib/dommy/internal/selector_ast.rb +124 -0
  65. data/lib/dommy/internal/selector_index.rb +146 -0
  66. data/lib/dommy/internal/selector_matcher.rb +756 -0
  67. data/lib/dommy/internal/selector_parser.rb +283 -131
  68. data/lib/dommy/internal/shadow_root_registry.rb +9 -2
  69. data/lib/dommy/internal/template_content_registry.rb +26 -18
  70. data/lib/dommy/internal/xml_serialization.rb +344 -0
  71. data/lib/dommy/intersection_observer.rb +2 -0
  72. data/lib/dommy/js/bridge_conformance.rb +80 -0
  73. data/lib/dommy/js/constructor_resolver.rb +44 -0
  74. data/lib/dommy/js/custom_element_bridge.rb +90 -0
  75. data/lib/dommy/js/dom_interfaces.rb +162 -0
  76. data/lib/dommy/js/handle_table.rb +60 -0
  77. data/lib/dommy/js/host_bridge.rb +517 -0
  78. data/lib/dommy/js/host_runtime.js +1495 -0
  79. data/lib/dommy/js/import_map.rb +58 -0
  80. data/lib/dommy/js/marshaller.rb +240 -0
  81. data/lib/dommy/js/module_loader.rb +99 -0
  82. data/lib/dommy/js/observable_runtime.js +742 -0
  83. data/lib/dommy/js/runtime.rb +115 -0
  84. data/lib/dommy/js/script_boot.rb +221 -0
  85. data/lib/dommy/js/wire_tags.rb +62 -0
  86. data/lib/dommy/location.rb +2 -0
  87. data/lib/dommy/media_query_list.rb +50 -14
  88. data/lib/dommy/message_channel.rb +22 -6
  89. data/lib/dommy/minitest/assertions.rb +27 -0
  90. data/lib/dommy/mutation_observer.rb +89 -4
  91. data/lib/dommy/navigator.rb +34 -2
  92. data/lib/dommy/node.rb +24 -14
  93. data/lib/dommy/notification.rb +2 -0
  94. data/lib/dommy/parser.rb +1 -1
  95. data/lib/dommy/performance.rb +21 -1
  96. data/lib/dommy/promise.rb +94 -10
  97. data/lib/dommy/range.rb +173 -31
  98. data/lib/dommy/resources.rb +178 -0
  99. data/lib/dommy/rspec/capy_style_matchers.rb +126 -0
  100. data/lib/dommy/scheduler.rb +149 -13
  101. data/lib/dommy/screen.rb +91 -0
  102. data/lib/dommy/shadow_root.rb +76 -13
  103. data/lib/dommy/storage.rb +2 -1
  104. data/lib/dommy/streams.rb +6 -0
  105. data/lib/dommy/text_codec.rb +7 -1
  106. data/lib/dommy/tree_walker.rb +33 -10
  107. data/lib/dommy/url.rb +13 -1
  108. data/lib/dommy/version.rb +1 -1
  109. data/lib/dommy/window.rb +199 -11
  110. data/lib/dommy/worker.rb +8 -4
  111. data/lib/dommy/xml_http_request.rb +47 -6
  112. data/lib/dommy.rb +36 -1
  113. metadata +96 -10
  114. data/lib/dommy/backend/nokogiri_adapter.rb +0 -127
  115. data/lib/dommy/backend/nokolexbor_adapter.rb +0 -117
metadata CHANGED
@@ -1,18 +1,52 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dommy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - takahashim
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-05-31 00:00:00.000000000 Z
11
- dependencies: []
12
- description: 'A pure Ruby DOM polyfill built on Nokogiri::HTML5, inspired by happy-dom
13
- and jsdom. It gives Ruby tests a browser style DOM with events, MutationObserver,
14
- Custom Elements, Shadow DOM, the File API, timers, and Storage, without requiring
15
- a real browser.
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: makiri
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 0.5.1
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: 0.5.1
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '1.0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: base64
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: 'A pure Ruby DOM polyfill built on Makiri, inspired by happy-dom and
47
+ jsdom. It gives Ruby tests a browser style DOM with events, MutationObserver, Custom
48
+ Elements, Shadow DOM, the File API, timers, and Storage, without requiring a real
49
+ browser.
16
50
 
17
51
  '
18
52
  email:
@@ -26,12 +60,12 @@ files:
26
60
  - lib/dommy/animation.rb
27
61
  - lib/dommy/attr.rb
28
62
  - lib/dommy/backend.rb
29
- - lib/dommy/backend/nokogiri_adapter.rb
30
- - lib/dommy/backend/nokolexbor_adapter.rb
63
+ - lib/dommy/backend/makiri_adapter.rb
31
64
  - lib/dommy/blob.rb
32
65
  - lib/dommy/bridge.rb
33
66
  - lib/dommy/bridge/constructor_registry.rb
34
67
  - lib/dommy/bridge/methods.rb
68
+ - lib/dommy/browser.rb
35
69
  - lib/dommy/callable_invoker.rb
36
70
  - lib/dommy/compression_streams.rb
37
71
  - lib/dommy/cookie_store.rb
@@ -39,6 +73,8 @@ files:
39
73
  - lib/dommy/css.rb
40
74
  - lib/dommy/custom_elements.rb
41
75
  - lib/dommy/data_transfer.rb
76
+ - lib/dommy/data_uri.rb
77
+ - lib/dommy/deferred_response.rb
42
78
  - lib/dommy/document.rb
43
79
  - lib/dommy/dom_exception.rb
44
80
  - lib/dommy/dom_parser.rb
@@ -49,11 +85,41 @@ files:
49
85
  - lib/dommy/file_reader.rb
50
86
  - lib/dommy/form_data.rb
51
87
  - lib/dommy/history.rb
88
+ - lib/dommy/html_canvas_element.rb
52
89
  - lib/dommy/html_collection.rb
53
90
  - lib/dommy/html_elements.rb
91
+ - lib/dommy/interaction.rb
92
+ - lib/dommy/interaction/debug.rb
93
+ - lib/dommy/interaction/dom_summary.rb
94
+ - lib/dommy/interaction/driver.rb
95
+ - lib/dommy/interaction/event_synthesis.rb
96
+ - lib/dommy/interaction/field_interactor.rb
97
+ - lib/dommy/interaction/form_submission.rb
98
+ - lib/dommy/interaction/locator.rb
99
+ - lib/dommy/interaction/role_query.rb
100
+ - lib/dommy/internal/accessibility_tree.rb
101
+ - lib/dommy/internal/accessible_description.rb
102
+ - lib/dommy/internal/accessible_name.rb
103
+ - lib/dommy/internal/aria_role.rb
104
+ - lib/dommy/internal/aria_snapshot.rb
105
+ - lib/dommy/internal/aria_state.rb
54
106
  - lib/dommy/internal/cookie_jar.rb
107
+ - lib/dommy/internal/css/calc.rb
108
+ - lib/dommy/internal/css/cascade.rb
109
+ - lib/dommy/internal/css/color.rb
110
+ - lib/dommy/internal/css/computed_style_declaration.rb
111
+ - lib/dommy/internal/css/counters.rb
112
+ - lib/dommy/internal/css/custom_properties.rb
113
+ - lib/dommy/internal/css/media_query.rb
114
+ - lib/dommy/internal/css/parser.rb
115
+ - lib/dommy/internal/css/property_registry.rb
116
+ - lib/dommy/internal/css/rule_index.rb
117
+ - lib/dommy/internal/css/supports.rb
118
+ - lib/dommy/internal/css/ua_stylesheet.rb
55
119
  - lib/dommy/internal/css_pseudo_handlers.rb
120
+ - lib/dommy/internal/css_rule_text.rb
56
121
  - lib/dommy/internal/dom_matching.rb
122
+ - lib/dommy/internal/element_matching.rb
57
123
  - lib/dommy/internal/global_functions.rb
58
124
  - lib/dommy/internal/idna.rb
59
125
  - lib/dommy/internal/idna_data.rb
@@ -71,11 +137,29 @@ files:
71
137
  - lib/dommy/internal/range_text_serializer.rb
72
138
  - lib/dommy/internal/reflected_attributes.rb
73
139
  - lib/dommy/internal/scope_resolution.rb
140
+ - lib/dommy/internal/selector_ast.rb
141
+ - lib/dommy/internal/selector_index.rb
142
+ - lib/dommy/internal/selector_matcher.rb
74
143
  - lib/dommy/internal/selector_parser.rb
75
144
  - lib/dommy/internal/shadow_root_registry.rb
76
145
  - lib/dommy/internal/template_content_registry.rb
77
146
  - lib/dommy/internal/url_parser.rb
147
+ - lib/dommy/internal/xml_serialization.rb
78
148
  - lib/dommy/intersection_observer.rb
149
+ - lib/dommy/js/bridge_conformance.rb
150
+ - lib/dommy/js/constructor_resolver.rb
151
+ - lib/dommy/js/custom_element_bridge.rb
152
+ - lib/dommy/js/dom_interfaces.rb
153
+ - lib/dommy/js/handle_table.rb
154
+ - lib/dommy/js/host_bridge.rb
155
+ - lib/dommy/js/host_runtime.js
156
+ - lib/dommy/js/import_map.rb
157
+ - lib/dommy/js/marshaller.rb
158
+ - lib/dommy/js/module_loader.rb
159
+ - lib/dommy/js/observable_runtime.js
160
+ - lib/dommy/js/runtime.rb
161
+ - lib/dommy/js/script_boot.rb
162
+ - lib/dommy/js/wire_tags.rb
79
163
  - lib/dommy/location.rb
80
164
  - lib/dommy/media_query_list.rb
81
165
  - lib/dommy/message_channel.rb
@@ -91,10 +175,12 @@ files:
91
175
  - lib/dommy/promise.rb
92
176
  - lib/dommy/range.rb
93
177
  - lib/dommy/resize_observer.rb
178
+ - lib/dommy/resources.rb
94
179
  - lib/dommy/rspec.rb
95
180
  - lib/dommy/rspec/capy_style_matchers.rb
96
181
  - lib/dommy/rspec/matchers.rb
97
182
  - lib/dommy/scheduler.rb
183
+ - lib/dommy/screen.rb
98
184
  - lib/dommy/shadow_root.rb
99
185
  - lib/dommy/storage.rb
100
186
  - lib/dommy/streams.rb
@@ -128,7 +214,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
214
  - !ruby/object:Gem::Version
129
215
  version: '0'
130
216
  requirements: []
131
- rubygems_version: 3.6.2
217
+ rubygems_version: 3.6.9
132
218
  specification_version: 4
133
219
  summary: happy-dom-style DOM polyfill in pure Ruby
134
220
  test_files: []
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "nokogiri"
4
-
5
- module Dommy
6
- module Backend
7
- # Nokogiri (libxml2-based) backend. Mature, full XML namespace
8
- # support. Default backend.
9
- module Nokogiri
10
- # Class references for `is_a?` / type-checking from Dommy internals.
11
- Element = ::Nokogiri::XML::Element
12
- Document = ::Nokogiri::XML::Document
13
- Text = ::Nokogiri::XML::Text
14
- Comment = ::Nokogiri::XML::Comment
15
- DocumentFragment = ::Nokogiri::XML::DocumentFragment
16
- Node = ::Nokogiri::XML::Node
17
-
18
- module_function
19
-
20
- def parse(html)
21
- ::Nokogiri::HTML5(html.to_s, max_errors: 0)
22
- end
23
-
24
- # Parse an XML string into an XML document (DOMParser "text/xml" etc.).
25
- def parse_xml(xml)
26
- ::Nokogiri::XML(xml.to_s)
27
- end
28
-
29
- def fragment(html, owner_doc:)
30
- # owner_doc is unused by Nokogiri — the fragment carries its
31
- # own document. The Parser layer copies nodes into the target.
32
- ::Nokogiri::HTML5.fragment(html.to_s, max_errors: 0)
33
- end
34
-
35
- def create_element(name, doc)
36
- ::Nokogiri::XML::Node.new(name, doc)
37
- end
38
-
39
- def create_text(content, doc)
40
- ::Nokogiri::XML::Text.new(content, doc)
41
- end
42
-
43
- def create_comment(content, doc)
44
- ::Nokogiri::XML::Comment.new(doc, content)
45
- end
46
-
47
- def create_cdata(content, doc)
48
- ::Nokogiri::XML::CDATA.new(doc, content.to_s)
49
- end
50
-
51
- def cdata_class = ::Nokogiri::XML::CDATA
52
-
53
- def namespace_of(node)
54
- node.namespace
55
- end
56
-
57
- def add_namespace_definition(node, prefix, href)
58
- node.add_namespace_definition(prefix, href)
59
- end
60
-
61
- # ----- Namespaced attributes -----
62
-
63
- def get_attribute_ns(node, namespace, local_name)
64
- find_attr_ns(node, namespace, local_name)&.value
65
- end
66
-
67
- def has_attribute_ns?(node, namespace, local_name)
68
- !find_attr_ns(node, namespace, local_name).nil?
69
- end
70
-
71
- def set_attribute_ns(node, namespace, prefix, local_name, qualified_name, value)
72
- # WHATWG "set an attribute value": when an attribute with this
73
- # (namespace, localName) already exists, only its value changes —
74
- # the existing prefix/qualified name is preserved, not replaced by
75
- # the one in this call.
76
- existing = find_attr_ns(node, namespace, local_name)
77
- if existing
78
- existing.value = value.to_s
79
- return value.to_s
80
- end
81
-
82
- if namespace.nil? || namespace.to_s.empty?
83
- node[qualified_name] = value.to_s
84
- return value.to_s
85
- end
86
-
87
- # Defining the namespace before the qualified-name assignment lets
88
- # libxml2 bind the prefix to it (verified behavior). Reuse an existing
89
- # matching definition so repeated sets don't pile up declarations.
90
- node.namespace_definitions.find { |d| d.href == namespace.to_s && d.prefix == prefix } ||
91
- node.add_namespace_definition(prefix, namespace.to_s)
92
- node[qualified_name] = value.to_s
93
- value.to_s
94
- end
95
-
96
- def remove_attribute_ns(node, namespace, local_name)
97
- find_attr_ns(node, namespace, local_name)&.remove
98
- nil
99
- end
100
-
101
- def attribute_ns_info(attr_node)
102
- ns = attr_node.namespace
103
- {
104
- namespace_uri: ns&.href,
105
- prefix: ns&.prefix,
106
- local_name: attr_node.name,
107
- qualified_name: ns&.prefix ? "#{ns.prefix}:#{attr_node.name}" : attr_node.name,
108
- value: attr_node.value,
109
- }
110
- end
111
-
112
- def attribute_nodes(node)
113
- node.attribute_nodes
114
- end
115
-
116
- # Finds the attribute node matching (namespace, localName). A null
117
- # namespace matches only the un-namespaced attribute of that local name.
118
- def find_attr_ns(node, namespace, local_name)
119
- if namespace.nil? || namespace.to_s.empty?
120
- node.attribute_nodes.find { |a| a.namespace.nil? && a.name == local_name.to_s }
121
- else
122
- node.attribute_with_ns(local_name.to_s, namespace.to_s)
123
- end
124
- end
125
- end
126
- end
127
- end
@@ -1,117 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "nokolexbor"
4
-
5
- module Dommy
6
- module Backend
7
- # Nokolexbor (Lexbor-based) backend. 3-7× faster CSS selectors,
8
- # 6× faster HTML5 parsing. HTML-only — XML namespaces are not
9
- # tracked, so SVG detection falls back to ancestor walking.
10
- module Nokolexbor
11
- # Class references for `is_a?` / type-checking.
12
- Element = ::Nokolexbor::Element
13
- Document = ::Nokolexbor::Document
14
- Text = ::Nokolexbor::Text
15
- Comment = ::Nokolexbor::Comment
16
- DocumentFragment = ::Nokolexbor::DocumentFragment
17
- Node = ::Nokolexbor::Node
18
-
19
- # Fake "namespace" wrapper returned for nodes inside <svg> subtrees.
20
- # Provides the same `href` API that Nokogiri's Namespace object has,
21
- # so calling code can treat them uniformly.
22
- SvgNamespace = Struct.new(:href) do
23
- def initialize
24
- super("http://www.w3.org/2000/svg")
25
- end
26
- end
27
-
28
- SVG_NAMESPACE = SvgNamespace.new.freeze
29
-
30
- module_function
31
-
32
- def parse(html)
33
- ::Nokolexbor.parse(html.to_s)
34
- end
35
-
36
- def fragment(html, owner_doc:)
37
- ::Nokolexbor::DocumentFragment.parse(html.to_s)
38
- end
39
-
40
- def create_element(name, doc)
41
- ::Nokolexbor::Node.new(name, doc)
42
- end
43
-
44
- def create_text(content, doc)
45
- ::Nokolexbor::Text.new(content, doc)
46
- end
47
-
48
- def create_comment(content, doc)
49
- # Nokolexbor's argument order is (content, doc).
50
- ::Nokolexbor::Comment.new(content, doc)
51
- end
52
-
53
- # Nokolexbor doesn't track XML namespaces. We synthesize one for
54
- # SVG by walking ancestors — necessary so `element_class_for`
55
- # routes SVG tags to their specialized classes.
56
- def namespace_of(node)
57
- return nil unless node.respond_to?(:name)
58
-
59
- in_svg_subtree?(node) ? SVG_NAMESPACE : nil
60
- end
61
-
62
- def add_namespace_definition(_node, _prefix, _href)
63
- # No-op: Nokolexbor doesn't support XML namespaces.
64
- end
65
-
66
- # ----- Namespaced attributes (degraded) -----
67
- # Nokolexbor has no namespace model, so *AttributeNS collapses to the
68
- # qualified name in the null namespace. Fine for HTML (all attributes are
69
- # null-namespace); foreign-content (SVG/MathML) fidelity is lost.
70
-
71
- def get_attribute_ns(node, _namespace, local_name)
72
- node[local_name.to_s]
73
- end
74
-
75
- def has_attribute_ns?(node, _namespace, local_name)
76
- node.key?(local_name.to_s)
77
- end
78
-
79
- def set_attribute_ns(node, _namespace, _prefix, _local_name, qualified_name, value)
80
- node[qualified_name] = value.to_s
81
- value.to_s
82
- end
83
-
84
- def remove_attribute_ns(node, _namespace, local_name)
85
- node.remove_attribute(local_name.to_s) if node.key?(local_name.to_s)
86
- nil
87
- end
88
-
89
- def attribute_ns_info(attr_node)
90
- {
91
- namespace_uri: nil,
92
- prefix: nil,
93
- local_name: attr_node.name,
94
- qualified_name: attr_node.name,
95
- value: attr_node.value,
96
- }
97
- end
98
-
99
- def attribute_nodes(node)
100
- node.attribute_nodes
101
- end
102
-
103
- # Internal helper — visible to allow testing.
104
- def in_svg_subtree?(node)
105
- return true if node.name.to_s.downcase == "svg"
106
-
107
- current = node.parent
108
- while current
109
- return true if current.respond_to?(:name) && current.name.to_s.downcase == "svg"
110
- current = current.respond_to?(:parent) ? current.parent : nil
111
- end
112
-
113
- false
114
- end
115
- end
116
- end
117
- end