dommy 0.5.0 → 0.7.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.
- checksums.yaml +4 -4
- data/README.md +31 -13
- data/lib/dommy/animation.rb +288 -0
- data/lib/dommy/attr.rb +23 -11
- data/lib/dommy/backend/nokogiri_adapter.rb +51 -0
- data/lib/dommy/backend/nokolexbor_adapter.rb +80 -0
- data/lib/dommy/backend.rb +129 -0
- data/lib/dommy/blob.rb +2 -2
- data/lib/dommy/compression_streams.rb +147 -0
- data/lib/dommy/cookie_store.rb +128 -0
- data/lib/dommy/crypto.rb +396 -0
- data/lib/dommy/css.rb +7 -7
- data/lib/dommy/custom_elements.rb +6 -6
- data/lib/dommy/document.rb +190 -32
- data/lib/dommy/dom_parser.rb +5 -4
- data/lib/dommy/element.rb +356 -53
- data/lib/dommy/event.rb +431 -25
- data/lib/dommy/event_source.rb +131 -0
- data/lib/dommy/fetch.rb +76 -6
- data/lib/dommy/file_reader.rb +176 -0
- data/lib/dommy/form_data.rb +1 -3
- data/lib/dommy/history.rb +82 -0
- data/lib/dommy/html_collection.rb +4 -4
- data/lib/dommy/html_elements.rb +130 -67
- data/lib/dommy/internal/cookie_jar.rb +2 -0
- data/lib/dommy/internal/css_pseudo_handlers.rb +28 -0
- data/lib/dommy/internal/dom_matching.rb +4 -4
- data/lib/dommy/internal/idna.rb +443 -0
- data/lib/dommy/internal/idna_data.rb +10379 -0
- data/lib/dommy/internal/ipv4_parser.rb +78 -0
- data/lib/dommy/internal/node_traversal.rb +1 -1
- data/lib/dommy/internal/node_wrapper_cache.rb +23 -12
- data/lib/dommy/internal/observable_callback.rb +25 -0
- data/lib/dommy/internal/punycode.rb +202 -0
- data/lib/dommy/internal/range_text_serializer.rb +72 -0
- data/lib/dommy/internal/reflected_attributes.rb +45 -0
- data/lib/dommy/internal/template_content_registry.rb +6 -6
- data/lib/dommy/intersection_observer.rb +82 -0
- data/lib/dommy/{router.rb → location.rb} +8 -142
- data/lib/dommy/media_query_list.rb +118 -0
- data/lib/dommy/message_channel.rb +249 -0
- data/lib/dommy/{observer.rb → mutation_observer.rb} +21 -11
- data/lib/dommy/navigator.rb +365 -5
- data/lib/dommy/node.rb +12 -0
- data/lib/dommy/notification.rb +89 -0
- data/lib/dommy/parser.rb +13 -13
- data/lib/dommy/performance.rb +146 -0
- data/lib/dommy/performance_observer.rb +55 -0
- data/lib/dommy/range.rb +597 -0
- data/lib/dommy/resize_observer.rb +53 -0
- data/lib/dommy/shadow_root.rb +10 -8
- data/lib/dommy/streams.rb +386 -0
- data/lib/dommy/svg_elements.rb +3863 -0
- data/lib/dommy/text_codec.rb +175 -0
- data/lib/dommy/tree_walker.rb +21 -21
- data/lib/dommy/url.rb +274 -29
- data/lib/dommy/url_pattern.rb +144 -0
- data/lib/dommy/version.rb +1 -1
- data/lib/dommy/web_socket.rb +209 -0
- data/lib/dommy/window.rb +369 -0
- data/lib/dommy/worker.rb +143 -0
- data/lib/dommy/xml_http_request.rb +438 -0
- data/lib/dommy.rb +43 -5
- metadata +44 -29
- data/lib/dommy/world.rb +0 -209
data/lib/dommy/html_elements.rb
CHANGED
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Dommy
|
|
4
|
-
# Base for specialized HTMLElement subclasses.
|
|
5
|
-
#
|
|
4
|
+
# Base for specialized HTMLElement subclasses. Inherits reflection
|
|
5
|
+
# helpers from Internal::ReflectedAttributes (also shared with
|
|
6
|
+
# SVGElement).
|
|
6
7
|
class HTMLElement < Element
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
set_attribute(key, "")
|
|
17
|
-
elsif @__node__.key?(key)
|
|
18
|
-
remove_attribute(key)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def reflected_string(name)
|
|
23
|
-
@__node__[name.to_s.downcase].to_s
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def set_reflected_string(name, value)
|
|
27
|
-
set_attribute(name.to_s.downcase, value.to_s)
|
|
8
|
+
include Internal::ReflectedAttributes
|
|
9
|
+
|
|
10
|
+
# HTML attribute names are case-insensitive — the browser DOM
|
|
11
|
+
# lowercases everything. Override to make this explicit at the
|
|
12
|
+
# HTMLElement level (Element's default would already pick this up
|
|
13
|
+
# via namespace inspection, but spelling it out shortcuts the
|
|
14
|
+
# namespace check for HTML's hot path).
|
|
15
|
+
def case_sensitive_attribute_names?
|
|
16
|
+
false
|
|
28
17
|
end
|
|
29
18
|
end
|
|
30
19
|
|
|
@@ -165,6 +154,12 @@ module Dommy
|
|
|
165
154
|
# `<form>` — element collection, submit/reset, and a stubbed
|
|
166
155
|
# validation surface.
|
|
167
156
|
class HTMLFormElement < HTMLElement
|
|
157
|
+
# Own __js_call__ methods, on top of Element's.
|
|
158
|
+
JS_METHOD_NAMES = %w[submit reset requestSubmit checkValidity reportValidity].freeze
|
|
159
|
+
def __js_method_names__
|
|
160
|
+
super + JS_METHOD_NAMES
|
|
161
|
+
end
|
|
162
|
+
|
|
168
163
|
def name
|
|
169
164
|
reflected_string("name")
|
|
170
165
|
end
|
|
@@ -222,7 +217,7 @@ module Dommy
|
|
|
222
217
|
el = self
|
|
223
218
|
HTMLCollection.new do
|
|
224
219
|
el
|
|
225
|
-
.
|
|
220
|
+
.__dommy_backend_node__
|
|
226
221
|
.css("input, select, textarea, button, output, fieldset")
|
|
227
222
|
.map do |n|
|
|
228
223
|
el.document.wrap_node(n)
|
|
@@ -256,7 +251,7 @@ module Dommy
|
|
|
256
251
|
# `submitter` (if given) must be a button inside this form.
|
|
257
252
|
def request_submit(submitter = nil)
|
|
258
253
|
if submitter
|
|
259
|
-
unless submitter.respond_to?(:
|
|
254
|
+
unless submitter.respond_to?(:__dommy_backend_node__) && submitter.__dommy_backend_node__.ancestors.include?(@__node__)
|
|
260
255
|
raise DOMException::NotFoundError, "submitter is not a descendant of this form"
|
|
261
256
|
end
|
|
262
257
|
|
|
@@ -357,6 +352,15 @@ module Dommy
|
|
|
357
352
|
|
|
358
353
|
# `<input>` — covers the most-used form control surface.
|
|
359
354
|
class HTMLInputElement < HTMLElement
|
|
355
|
+
# Own __js_call__ methods, on top of Element's.
|
|
356
|
+
JS_METHOD_NAMES = %w[
|
|
357
|
+
select setSelectionRange setRangeText stepUp stepDown
|
|
358
|
+
checkValidity reportValidity setCustomValidity
|
|
359
|
+
].freeze
|
|
360
|
+
def __js_method_names__
|
|
361
|
+
super + JS_METHOD_NAMES
|
|
362
|
+
end
|
|
363
|
+
|
|
360
364
|
def type
|
|
361
365
|
raw = @__node__["type"].to_s
|
|
362
366
|
raw.empty? ? "text" : raw.downcase
|
|
@@ -433,14 +437,14 @@ module Dommy
|
|
|
433
437
|
end
|
|
434
438
|
|
|
435
439
|
# `files` — for `<input type="file">`. Browsers populate this via
|
|
436
|
-
# user interaction; in tests, code uses `
|
|
440
|
+
# user interaction; in tests, code uses `__driver_set_files__` to seed it.
|
|
437
441
|
def files
|
|
438
442
|
@__files ||= FileList.new
|
|
439
443
|
end
|
|
440
444
|
|
|
441
445
|
# Test-only seam: set the input's file list directly.
|
|
442
446
|
# Accepts an array (wrapped in a FileList) or a FileList itself.
|
|
443
|
-
def
|
|
447
|
+
def __driver_set_files__(files_input)
|
|
444
448
|
@__files = files_input.is_a?(FileList) ? files_input : FileList.new(Array(files_input))
|
|
445
449
|
end
|
|
446
450
|
|
|
@@ -1392,13 +1396,13 @@ module Dommy
|
|
|
1392
1396
|
def host_attr_value(name)
|
|
1393
1397
|
return "" unless @host
|
|
1394
1398
|
|
|
1395
|
-
@host.
|
|
1399
|
+
@host.__dommy_backend_node__[name].to_s
|
|
1396
1400
|
end
|
|
1397
1401
|
|
|
1398
1402
|
def host_attr_present?(name)
|
|
1399
1403
|
return false unless @host
|
|
1400
1404
|
|
|
1401
|
-
@host.
|
|
1405
|
+
@host.__dommy_backend_node__.key?(name.to_s)
|
|
1402
1406
|
end
|
|
1403
1407
|
|
|
1404
1408
|
def host_type
|
|
@@ -1493,7 +1497,7 @@ module Dommy
|
|
|
1493
1497
|
sel = closest("select")
|
|
1494
1498
|
return 0 unless sel
|
|
1495
1499
|
|
|
1496
|
-
sel.options.find_index { |o| o.
|
|
1500
|
+
sel.options.find_index { |o| o.__dommy_backend_node__ == @__node__ } || 0
|
|
1497
1501
|
end
|
|
1498
1502
|
|
|
1499
1503
|
def __js_get__(key)
|
|
@@ -1580,6 +1584,14 @@ module Dommy
|
|
|
1580
1584
|
|
|
1581
1585
|
# `<textarea>` — multi-line text input.
|
|
1582
1586
|
class HTMLTextAreaElement < HTMLElement
|
|
1587
|
+
# Own __js_call__ methods, on top of Element's.
|
|
1588
|
+
JS_METHOD_NAMES = %w[
|
|
1589
|
+
select setSelectionRange setRangeText checkValidity reportValidity setCustomValidity
|
|
1590
|
+
].freeze
|
|
1591
|
+
def __js_method_names__
|
|
1592
|
+
super + JS_METHOD_NAMES
|
|
1593
|
+
end
|
|
1594
|
+
|
|
1583
1595
|
def value
|
|
1584
1596
|
@__node__["value"] || text_content
|
|
1585
1597
|
end
|
|
@@ -1876,7 +1888,7 @@ module Dommy
|
|
|
1876
1888
|
el = self
|
|
1877
1889
|
HTMLCollection.new do
|
|
1878
1890
|
el
|
|
1879
|
-
.
|
|
1891
|
+
.__dommy_backend_node__
|
|
1880
1892
|
.css("input, select, textarea, button, output, fieldset")
|
|
1881
1893
|
.map do |n|
|
|
1882
1894
|
el.document.wrap_node(n)
|
|
@@ -2042,6 +2054,12 @@ module Dommy
|
|
|
2042
2054
|
# `slot` attribute go to the unnamed default slot. If nothing is
|
|
2043
2055
|
# assigned, the slot's own children render as fallback content.
|
|
2044
2056
|
class HTMLSlotElement < HTMLElement
|
|
2057
|
+
# Own __js_call__ methods, on top of Element's.
|
|
2058
|
+
JS_METHOD_NAMES = %w[assignedNodes assignedElements assign].freeze
|
|
2059
|
+
def __js_method_names__
|
|
2060
|
+
super + JS_METHOD_NAMES
|
|
2061
|
+
end
|
|
2062
|
+
|
|
2045
2063
|
def name
|
|
2046
2064
|
reflected_string("name")
|
|
2047
2065
|
end
|
|
@@ -2073,7 +2091,7 @@ module Dommy
|
|
|
2073
2091
|
# call and fire `slotchange` in both modes; named mode simply
|
|
2074
2092
|
# ignores the override.
|
|
2075
2093
|
def assign(*nodes)
|
|
2076
|
-
@__manual_assignment = nodes.flatten.select { |n| n.respond_to?(:
|
|
2094
|
+
@__manual_assignment = nodes.flatten.select { |n| n.respond_to?(:__dommy_backend_node__) }
|
|
2077
2095
|
dispatch_event(Event.new("slotchange", "bubbles" => true))
|
|
2078
2096
|
nil
|
|
2079
2097
|
end
|
|
@@ -2116,7 +2134,7 @@ module Dommy
|
|
|
2116
2134
|
private
|
|
2117
2135
|
|
|
2118
2136
|
def matching_light_nodes
|
|
2119
|
-
sr = @document.
|
|
2137
|
+
sr = @document.__internal_shadow_root_containing__(@__node__)
|
|
2120
2138
|
return [] unless sr
|
|
2121
2139
|
|
|
2122
2140
|
host = sr.host
|
|
@@ -2129,7 +2147,7 @@ module Dommy
|
|
|
2129
2147
|
end
|
|
2130
2148
|
|
|
2131
2149
|
host
|
|
2132
|
-
.
|
|
2150
|
+
.__dommy_backend_node__
|
|
2133
2151
|
.children
|
|
2134
2152
|
.map do |child|
|
|
2135
2153
|
wrapped = @document.wrap_node(child)
|
|
@@ -2150,6 +2168,12 @@ module Dommy
|
|
|
2150
2168
|
# `selectedIndex`, and dispatches change events. Minimal compared to
|
|
2151
2169
|
# happy-dom's full HTMLSelectElement, but covers common test cases.
|
|
2152
2170
|
class HTMLSelectElement < HTMLElement
|
|
2171
|
+
# Own __js_call__ methods, on top of Element's.
|
|
2172
|
+
JS_METHOD_NAMES = %w[item add checkValidity reportValidity setCustomValidity].freeze
|
|
2173
|
+
def __js_method_names__
|
|
2174
|
+
super + JS_METHOD_NAMES
|
|
2175
|
+
end
|
|
2176
|
+
|
|
2153
2177
|
def name
|
|
2154
2178
|
reflected_string("name")
|
|
2155
2179
|
end
|
|
@@ -2176,7 +2200,7 @@ module Dommy
|
|
|
2176
2200
|
def options
|
|
2177
2201
|
el = self
|
|
2178
2202
|
HTMLOptionsCollection.new(self) do
|
|
2179
|
-
el.
|
|
2203
|
+
el.__dommy_backend_node__.css("option").map { |n| el.document.wrap_node(n) }.compact
|
|
2180
2204
|
end
|
|
2181
2205
|
end
|
|
2182
2206
|
|
|
@@ -2186,8 +2210,8 @@ module Dommy
|
|
|
2186
2210
|
def selected_options
|
|
2187
2211
|
el = self
|
|
2188
2212
|
HTMLCollection.new do
|
|
2189
|
-
opts = el.
|
|
2190
|
-
chosen = opts.select { |o| o.
|
|
2213
|
+
opts = el.__dommy_backend_node__.css("option").map { |n| el.document.wrap_node(n) }.compact
|
|
2214
|
+
chosen = opts.select { |o| o.__dommy_backend_node__.key?("selected") }
|
|
2191
2215
|
next chosen unless chosen.empty?
|
|
2192
2216
|
next [] if el.multiple
|
|
2193
2217
|
|
|
@@ -2207,7 +2231,7 @@ module Dommy
|
|
|
2207
2231
|
# not multiple, or -1 if multiple and none.
|
|
2208
2232
|
def selected_index
|
|
2209
2233
|
opts = options
|
|
2210
|
-
idx = opts.find_index { |o| o.
|
|
2234
|
+
idx = opts.find_index { |o| o.__dommy_backend_node__.key?("selected") }
|
|
2211
2235
|
return idx if idx
|
|
2212
2236
|
|
|
2213
2237
|
multiple ? -1 : (opts.empty? ? -1 : 0)
|
|
@@ -2218,7 +2242,7 @@ module Dommy
|
|
|
2218
2242
|
opts.each_with_index do |o, idx|
|
|
2219
2243
|
if idx == i.to_i
|
|
2220
2244
|
o.set_attribute("selected", "")
|
|
2221
|
-
elsif o.
|
|
2245
|
+
elsif o.__dommy_backend_node__.key?("selected")
|
|
2222
2246
|
o.remove_attribute("selected")
|
|
2223
2247
|
end
|
|
2224
2248
|
end
|
|
@@ -2227,15 +2251,15 @@ module Dommy
|
|
|
2227
2251
|
# `value` of the select = value of the selected option, or "".
|
|
2228
2252
|
def value
|
|
2229
2253
|
opts = options
|
|
2230
|
-
sel = opts.find { |o| o.
|
|
2231
|
-
sel ? (sel.
|
|
2254
|
+
sel = opts.find { |o| o.__dommy_backend_node__.key?("selected") } || opts.first
|
|
2255
|
+
sel ? (sel.__dommy_backend_node__["value"] || sel.text_content).to_s : ""
|
|
2232
2256
|
end
|
|
2233
2257
|
|
|
2234
2258
|
def value=(new_value)
|
|
2235
|
-
target = options.find { |o| (o.
|
|
2259
|
+
target = options.find { |o| (o.__dommy_backend_node__["value"] || o.text_content).to_s == new_value.to_s }
|
|
2236
2260
|
return unless target
|
|
2237
2261
|
|
|
2238
|
-
options.each { |o| o.remove_attribute("selected") if o.
|
|
2262
|
+
options.each { |o| o.remove_attribute("selected") if o.__dommy_backend_node__.key?("selected") }
|
|
2239
2263
|
target.set_attribute("selected", "")
|
|
2240
2264
|
end
|
|
2241
2265
|
|
|
@@ -2246,9 +2270,9 @@ module Dommy
|
|
|
2246
2270
|
|
|
2247
2271
|
# `select.add(option, before)` — appends or inserts before `before`.
|
|
2248
2272
|
def add(option, before = nil)
|
|
2249
|
-
return nil unless option.respond_to?(:
|
|
2273
|
+
return nil unless option.respond_to?(:__dommy_backend_node__)
|
|
2250
2274
|
|
|
2251
|
-
if before.respond_to?(:
|
|
2275
|
+
if before.respond_to?(:__dommy_backend_node__)
|
|
2252
2276
|
insert_before(option, before)
|
|
2253
2277
|
else
|
|
2254
2278
|
append_child(option)
|
|
@@ -2378,6 +2402,12 @@ module Dommy
|
|
|
2378
2402
|
# `close(returnValue?)`. Dommy has no modal stack, so showModal is
|
|
2379
2403
|
# functionally identical to show (no backdrop, no escape-to-close).
|
|
2380
2404
|
class HTMLDialogElement < HTMLElement
|
|
2405
|
+
# Own __js_call__ methods, on top of Element's.
|
|
2406
|
+
JS_METHOD_NAMES = %w[show showModal close].freeze
|
|
2407
|
+
def __js_method_names__
|
|
2408
|
+
super + JS_METHOD_NAMES
|
|
2409
|
+
end
|
|
2410
|
+
|
|
2381
2411
|
def open
|
|
2382
2412
|
reflected_boolean("open")
|
|
2383
2413
|
end
|
|
@@ -2663,7 +2693,7 @@ module Dommy
|
|
|
2663
2693
|
row = closest("tr")
|
|
2664
2694
|
return -1 unless row
|
|
2665
2695
|
|
|
2666
|
-
row.cells.find_index { |c| c.
|
|
2696
|
+
row.cells.find_index { |c| c.__dommy_backend_node__ == @__node__ } || -1
|
|
2667
2697
|
end
|
|
2668
2698
|
|
|
2669
2699
|
def col_span
|
|
@@ -2749,11 +2779,17 @@ module Dommy
|
|
|
2749
2779
|
# `rowIndex` walks the enclosing table; `sectionRowIndex` walks
|
|
2750
2780
|
# the enclosing thead/tbody/tfoot.
|
|
2751
2781
|
class HTMLTableRowElement < HTMLElement
|
|
2782
|
+
# Own __js_call__ methods, on top of Element's.
|
|
2783
|
+
JS_METHOD_NAMES = %w[insertCell deleteCell].freeze
|
|
2784
|
+
def __js_method_names__
|
|
2785
|
+
super + JS_METHOD_NAMES
|
|
2786
|
+
end
|
|
2787
|
+
|
|
2752
2788
|
def cells
|
|
2753
2789
|
el = self
|
|
2754
2790
|
HTMLCollection.new do
|
|
2755
2791
|
el
|
|
2756
|
-
.
|
|
2792
|
+
.__dommy_backend_node__
|
|
2757
2793
|
.element_children
|
|
2758
2794
|
.select { |n| %w[td th].include?(n.name) }
|
|
2759
2795
|
.map { |n| el.document.wrap_node(n) }
|
|
@@ -2765,7 +2801,7 @@ module Dommy
|
|
|
2765
2801
|
table = closest("table")
|
|
2766
2802
|
return -1 unless table
|
|
2767
2803
|
|
|
2768
|
-
table.rows.find_index { |r| r.
|
|
2804
|
+
table.rows.find_index { |r| r.__dommy_backend_node__ == @__node__ } || -1
|
|
2769
2805
|
end
|
|
2770
2806
|
|
|
2771
2807
|
def section_row_index
|
|
@@ -2823,11 +2859,17 @@ module Dommy
|
|
|
2823
2859
|
# `<thead>` / `<tbody>` / `<tfoot>` — share section-level row
|
|
2824
2860
|
# collection + insertRow / deleteRow.
|
|
2825
2861
|
class HTMLTableSectionElement < HTMLElement
|
|
2862
|
+
# Own __js_call__ methods, on top of Element's.
|
|
2863
|
+
JS_METHOD_NAMES = %w[insertRow deleteRow].freeze
|
|
2864
|
+
def __js_method_names__
|
|
2865
|
+
super + JS_METHOD_NAMES
|
|
2866
|
+
end
|
|
2867
|
+
|
|
2826
2868
|
def rows
|
|
2827
2869
|
el = self
|
|
2828
2870
|
HTMLCollection.new do
|
|
2829
2871
|
el
|
|
2830
|
-
.
|
|
2872
|
+
.__dommy_backend_node__
|
|
2831
2873
|
.element_children
|
|
2832
2874
|
.select { |n| n.name == "tr" }
|
|
2833
2875
|
.map { |n| el.document.wrap_node(n) }
|
|
@@ -2877,16 +2919,25 @@ module Dommy
|
|
|
2877
2919
|
# tbody elements. `insertRow(-1)` appends to the last tbody (or
|
|
2878
2920
|
# creates one); `deleteRow` works against the merged `rows` list.
|
|
2879
2921
|
class HTMLTableElement < HTMLElement
|
|
2922
|
+
# Own __js_call__ methods, on top of Element's.
|
|
2923
|
+
JS_METHOD_NAMES = %w[
|
|
2924
|
+
insertRow deleteRow createCaption deleteCaption createTHead deleteTHead
|
|
2925
|
+
createTFoot deleteTFoot createTBody
|
|
2926
|
+
].freeze
|
|
2927
|
+
def __js_method_names__
|
|
2928
|
+
super + JS_METHOD_NAMES
|
|
2929
|
+
end
|
|
2930
|
+
|
|
2880
2931
|
def caption
|
|
2881
2932
|
@__node__.element_children.find { |n| n.name == "caption" }&.then { |n| @document.wrap_node(n) }
|
|
2882
2933
|
end
|
|
2883
2934
|
|
|
2884
2935
|
def caption=(new_caption)
|
|
2885
2936
|
delete_caption
|
|
2886
|
-
return unless new_caption.respond_to?(:
|
|
2937
|
+
return unless new_caption.respond_to?(:__dommy_backend_node__)
|
|
2887
2938
|
|
|
2888
2939
|
first = @__node__.children.first
|
|
2889
|
-
first ? first.add_previous_sibling(new_caption.
|
|
2940
|
+
first ? first.add_previous_sibling(new_caption.__dommy_backend_node__) : @__node__.add_child(new_caption.__dommy_backend_node__)
|
|
2890
2941
|
end
|
|
2891
2942
|
|
|
2892
2943
|
def t_head
|
|
@@ -2901,7 +2952,7 @@ module Dommy
|
|
|
2901
2952
|
el = self
|
|
2902
2953
|
HTMLCollection.new do
|
|
2903
2954
|
el
|
|
2904
|
-
.
|
|
2955
|
+
.__dommy_backend_node__
|
|
2905
2956
|
.element_children
|
|
2906
2957
|
.select { |n| n.name == "tbody" }
|
|
2907
2958
|
.map { |n| el.document.wrap_node(n) }
|
|
@@ -2913,10 +2964,10 @@ module Dommy
|
|
|
2913
2964
|
el = self
|
|
2914
2965
|
HTMLCollection.new do
|
|
2915
2966
|
ordered = []
|
|
2916
|
-
head = el.
|
|
2917
|
-
bodies = el.
|
|
2918
|
-
direct = el.
|
|
2919
|
-
foot = el.
|
|
2967
|
+
head = el.__dommy_backend_node__.element_children.find { |n| n.name == "thead" }
|
|
2968
|
+
bodies = el.__dommy_backend_node__.element_children.select { |n| n.name == "tbody" }
|
|
2969
|
+
direct = el.__dommy_backend_node__.element_children.select { |n| n.name == "tr" }
|
|
2970
|
+
foot = el.__dommy_backend_node__.element_children.find { |n| n.name == "tfoot" }
|
|
2920
2971
|
[head, *bodies, foot].compact.each do |sec|
|
|
2921
2972
|
sec.element_children.select { |n| n.name == "tr" }.each { |n| ordered << n }
|
|
2922
2973
|
end
|
|
@@ -2932,7 +2983,7 @@ module Dommy
|
|
|
2932
2983
|
|
|
2933
2984
|
cap = @document.create_element("caption")
|
|
2934
2985
|
first = @__node__.children.first
|
|
2935
|
-
first ? first.add_previous_sibling(cap.
|
|
2986
|
+
first ? first.add_previous_sibling(cap.__dommy_backend_node__) : @__node__.add_child(cap.__dommy_backend_node__)
|
|
2936
2987
|
cap
|
|
2937
2988
|
end
|
|
2938
2989
|
|
|
@@ -2949,10 +3000,10 @@ module Dommy
|
|
|
2949
3000
|
head = @document.create_element("thead")
|
|
2950
3001
|
cap = caption
|
|
2951
3002
|
if cap
|
|
2952
|
-
cap.
|
|
3003
|
+
cap.__dommy_backend_node__.add_next_sibling(head.__dommy_backend_node__)
|
|
2953
3004
|
else
|
|
2954
3005
|
first = @__node__.children.first
|
|
2955
|
-
first ? first.add_previous_sibling(head.
|
|
3006
|
+
first ? first.add_previous_sibling(head.__dommy_backend_node__) : @__node__.add_child(head.__dommy_backend_node__)
|
|
2956
3007
|
end
|
|
2957
3008
|
|
|
2958
3009
|
head
|
|
@@ -2968,7 +3019,7 @@ module Dommy
|
|
|
2968
3019
|
return existing if existing
|
|
2969
3020
|
|
|
2970
3021
|
foot = @document.create_element("tfoot")
|
|
2971
|
-
@__node__.add_child(foot.
|
|
3022
|
+
@__node__.add_child(foot.__dommy_backend_node__)
|
|
2972
3023
|
foot
|
|
2973
3024
|
end
|
|
2974
3025
|
|
|
@@ -2981,9 +3032,9 @@ module Dommy
|
|
|
2981
3032
|
tb = @document.create_element("tbody")
|
|
2982
3033
|
last_tbody = t_bodies.last
|
|
2983
3034
|
if last_tbody
|
|
2984
|
-
last_tbody.
|
|
3035
|
+
last_tbody.__dommy_backend_node__.add_next_sibling(tb.__dommy_backend_node__)
|
|
2985
3036
|
else
|
|
2986
|
-
@__node__.add_child(tb.
|
|
3037
|
+
@__node__.add_child(tb.__dommy_backend_node__)
|
|
2987
3038
|
end
|
|
2988
3039
|
|
|
2989
3040
|
tb
|
|
@@ -3006,10 +3057,10 @@ module Dommy
|
|
|
3006
3057
|
target_section.append_child(tr)
|
|
3007
3058
|
else
|
|
3008
3059
|
anchor = list[idx]
|
|
3009
|
-
section = anchor.
|
|
3060
|
+
section = anchor.__dommy_backend_node__.parent
|
|
3010
3061
|
if section
|
|
3011
|
-
anchor.
|
|
3012
|
-
@document.notify_child_list_mutation(target_node: section, added_nodes: [tr.
|
|
3062
|
+
anchor.__dommy_backend_node__.add_previous_sibling(tr.__dommy_backend_node__)
|
|
3063
|
+
@document.notify_child_list_mutation(target_node: section, added_nodes: [tr.__dommy_backend_node__], removed_nodes: [])
|
|
3013
3064
|
end
|
|
3014
3065
|
end
|
|
3015
3066
|
|
|
@@ -3077,6 +3128,12 @@ module Dommy
|
|
|
3077
3128
|
# absent in Dommy — getters return inert values, `play()` returns
|
|
3078
3129
|
# a resolved Promise, and `pause()` flips `paused` back to true.
|
|
3079
3130
|
class HTMLMediaElement < HTMLElement
|
|
3131
|
+
# Own __js_call__ methods, on top of Element's.
|
|
3132
|
+
JS_METHOD_NAMES = %w[play pause load canPlayType].freeze
|
|
3133
|
+
def __js_method_names__
|
|
3134
|
+
super + JS_METHOD_NAMES
|
|
3135
|
+
end
|
|
3136
|
+
|
|
3080
3137
|
NETWORK_EMPTY = 0
|
|
3081
3138
|
NETWORK_IDLE = 1
|
|
3082
3139
|
NETWORK_LOADING = 2
|
|
@@ -4449,7 +4506,13 @@ module Dommy
|
|
|
4449
4506
|
"html" => HTMLHtmlElement
|
|
4450
4507
|
}.freeze
|
|
4451
4508
|
|
|
4452
|
-
|
|
4453
|
-
|
|
4509
|
+
SVG_NAMESPACE_URI = "http://www.w3.org/2000/svg"
|
|
4510
|
+
|
|
4511
|
+
def self.element_class_for(tag_name, namespace_uri = nil)
|
|
4512
|
+
if namespace_uri == SVG_NAMESPACE_URI
|
|
4513
|
+
SVG_ELEMENT_CLASSES[tag_name.to_s.downcase] || SVGElement
|
|
4514
|
+
else
|
|
4515
|
+
HTML_ELEMENT_CLASSES[tag_name.to_s.downcase] || Element
|
|
4516
|
+
end
|
|
4454
4517
|
end
|
|
4455
4518
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dommy
|
|
4
|
+
module Internal
|
|
5
|
+
# Custom Nokogiri pseudo-class handlers so CSS selectors like
|
|
6
|
+
# `:disabled` / `:enabled` / `:checked` work in query_selector(_all).
|
|
7
|
+
# Nokogiri calls the method named after the pseudo-class with the current
|
|
8
|
+
# node list and expects the filtered list back. Receives raw Nokogiri
|
|
9
|
+
# nodes (not Dommy wrappers).
|
|
10
|
+
class CSSPseudoHandlers < BasicObject
|
|
11
|
+
include ::Kernel
|
|
12
|
+
|
|
13
|
+
def disabled(list)
|
|
14
|
+
list.find_all { |node| node.has_attribute?("disabled") }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def enabled(list)
|
|
18
|
+
list.find_all { |node| !node.has_attribute?("disabled") }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def checked(list)
|
|
22
|
+
list.find_all { |node| node.has_attribute?("checked") }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
CSS_PSEUDO_HANDLERS = CSSPseudoHandlers.new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -44,7 +44,7 @@ module Dommy
|
|
|
44
44
|
actual.positive?
|
|
45
45
|
when Integer
|
|
46
46
|
actual == expected
|
|
47
|
-
when Range
|
|
47
|
+
when ::Range
|
|
48
48
|
expected.cover?(actual)
|
|
49
49
|
else
|
|
50
50
|
false
|
|
@@ -57,7 +57,7 @@ module Dommy
|
|
|
57
57
|
#
|
|
58
58
|
# @param html [String]
|
|
59
59
|
def normalize_html(html)
|
|
60
|
-
|
|
60
|
+
Backend.fragment(html.to_s, owner_doc: nil).to_html.gsub(/\s+/, " ").strip
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
# Get the text_content of a scope, handling Document (which has
|
|
@@ -91,9 +91,9 @@ module Dommy
|
|
|
91
91
|
# ancestors (head/script/style/template), inline `display:none` /
|
|
92
92
|
# `visibility:hidden` on element or any ancestor.
|
|
93
93
|
def visible?(element)
|
|
94
|
-
return true unless element.respond_to?(:
|
|
94
|
+
return true unless element.respond_to?(:__dommy_backend_node__)
|
|
95
95
|
|
|
96
|
-
node = element.
|
|
96
|
+
node = element.__dommy_backend_node__
|
|
97
97
|
return false if node_invisible_self?(node)
|
|
98
98
|
|
|
99
99
|
NodeTraversal.each_ancestor(node) do |ancestor|
|