nokogiri 1.12.2 → 1.13.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of nokogiri might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +9 -7
- data/bin/nokogiri +63 -50
- data/dependencies.yml +5 -6
- data/ext/nokogiri/extconf.rb +51 -35
- data/ext/nokogiri/gumbo.c +11 -11
- data/ext/nokogiri/html4_element_description.c +1 -1
- data/ext/nokogiri/html4_sax_parser_context.c +2 -1
- data/ext/nokogiri/nokogiri.c +1 -1
- data/ext/nokogiri/nokogiri.h +3 -0
- data/ext/nokogiri/xml_document.c +36 -36
- data/ext/nokogiri/xml_document_fragment.c +0 -2
- data/ext/nokogiri/xml_dtd.c +2 -2
- data/ext/nokogiri/xml_encoding_handler.c +25 -11
- data/ext/nokogiri/xml_namespace.c +2 -2
- data/ext/nokogiri/xml_node.c +647 -335
- data/ext/nokogiri/xml_reader.c +37 -11
- data/ext/nokogiri/xml_xpath_context.c +72 -49
- data/gumbo-parser/src/parser.c +0 -11
- data/lib/nokogiri/class_resolver.rb +67 -0
- data/lib/nokogiri/css/node.rb +9 -8
- data/lib/nokogiri/css/parser.rb +11 -3
- data/lib/nokogiri/css/parser.y +10 -2
- data/lib/nokogiri/css/parser_extras.rb +20 -20
- data/lib/nokogiri/css/syntax_error.rb +1 -0
- data/lib/nokogiri/css/tokenizer.rb +2 -1
- data/lib/nokogiri/css/tokenizer.rex +2 -1
- data/lib/nokogiri/css/xpath_visitor.rb +174 -75
- data/lib/nokogiri/css.rb +38 -6
- data/lib/nokogiri/decorators/slop.rb +8 -7
- data/lib/nokogiri/extension.rb +1 -1
- data/lib/nokogiri/gumbo.rb +1 -0
- data/lib/nokogiri/html.rb +16 -10
- data/lib/nokogiri/html4/builder.rb +1 -0
- data/lib/nokogiri/html4/document.rb +84 -75
- data/lib/nokogiri/html4/document_fragment.rb +11 -7
- data/lib/nokogiri/html4/element_description.rb +1 -0
- data/lib/nokogiri/html4/element_description_defaults.rb +426 -520
- data/lib/nokogiri/html4/entity_lookup.rb +2 -1
- data/lib/nokogiri/html4/sax/parser.rb +2 -1
- data/lib/nokogiri/html4/sax/parser_context.rb +1 -0
- data/lib/nokogiri/html4/sax/push_parser.rb +7 -7
- data/lib/nokogiri/html4.rb +11 -5
- data/lib/nokogiri/html5/document.rb +24 -10
- data/lib/nokogiri/html5/document_fragment.rb +5 -2
- data/lib/nokogiri/html5/node.rb +6 -3
- data/lib/nokogiri/html5.rb +68 -64
- data/lib/nokogiri/jruby/dependencies.rb +10 -9
- data/lib/nokogiri/syntax_error.rb +1 -0
- data/lib/nokogiri/version/constant.rb +2 -1
- data/lib/nokogiri/version/info.rb +19 -13
- data/lib/nokogiri/version.rb +1 -0
- data/lib/nokogiri/xml/attr.rb +5 -3
- data/lib/nokogiri/xml/attribute_decl.rb +2 -1
- data/lib/nokogiri/xml/builder.rb +69 -31
- data/lib/nokogiri/xml/cdata.rb +2 -1
- data/lib/nokogiri/xml/character_data.rb +1 -0
- data/lib/nokogiri/xml/document.rb +178 -96
- data/lib/nokogiri/xml/document_fragment.rb +41 -38
- data/lib/nokogiri/xml/dtd.rb +3 -2
- data/lib/nokogiri/xml/element_content.rb +1 -0
- data/lib/nokogiri/xml/element_decl.rb +2 -1
- data/lib/nokogiri/xml/entity_decl.rb +3 -2
- data/lib/nokogiri/xml/entity_reference.rb +1 -0
- data/lib/nokogiri/xml/namespace.rb +2 -0
- data/lib/nokogiri/xml/node/save_options.rb +7 -4
- data/lib/nokogiri/xml/node.rb +512 -348
- data/lib/nokogiri/xml/node_set.rb +46 -54
- data/lib/nokogiri/xml/notation.rb +12 -0
- data/lib/nokogiri/xml/parse_options.rb +11 -7
- data/lib/nokogiri/xml/pp/character_data.rb +8 -6
- data/lib/nokogiri/xml/pp/node.rb +24 -26
- data/lib/nokogiri/xml/pp.rb +1 -0
- data/lib/nokogiri/xml/processing_instruction.rb +2 -1
- data/lib/nokogiri/xml/reader.rb +17 -19
- data/lib/nokogiri/xml/relax_ng.rb +1 -0
- data/lib/nokogiri/xml/sax/document.rb +20 -19
- data/lib/nokogiri/xml/sax/parser.rb +36 -34
- data/lib/nokogiri/xml/sax/parser_context.rb +7 -3
- data/lib/nokogiri/xml/sax/push_parser.rb +5 -5
- data/lib/nokogiri/xml/sax.rb +1 -0
- data/lib/nokogiri/xml/schema.rb +7 -6
- data/lib/nokogiri/xml/searchable.rb +42 -22
- data/lib/nokogiri/xml/syntax_error.rb +4 -4
- data/lib/nokogiri/xml/text.rb +1 -0
- data/lib/nokogiri/xml/xpath/syntax_error.rb +2 -1
- data/lib/nokogiri/xml/xpath.rb +12 -0
- data/lib/nokogiri/xml/xpath_context.rb +2 -3
- data/lib/nokogiri/xml.rb +3 -3
- data/lib/nokogiri/xslt/stylesheet.rb +1 -0
- data/lib/nokogiri/xslt.rb +3 -2
- data/lib/nokogiri.rb +19 -16
- data/lib/xsd/xmlparser/nokogiri.rb +25 -24
- data/patches/libxml2/0008-htmlParseComment-handle-abruptly-closed-comments.patch +61 -0
- data/patches/libxml2/0009-allow-wildcard-namespaces.patch +77 -0
- metadata +101 -27
data/lib/nokogiri/xml/node.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
-
# encoding:
|
1
|
+
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
|
+
|
3
4
|
require "stringio"
|
4
5
|
|
5
6
|
module Nokogiri
|
6
7
|
module XML
|
7
|
-
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# Nokogiri::XML::Node is the primary API you'll use to interact with your Document.
|
9
|
+
#
|
10
|
+
# == Attributes
|
11
|
+
#
|
12
|
+
# A Nokogiri::XML::Node may be treated similarly to a hash with regard to attributes. For
|
10
13
|
# example:
|
11
14
|
#
|
12
15
|
# node = Nokogiri::XML::DocumentFragment.parse("<a href='#foo' id='link'>link</a>").at_css("a")
|
@@ -17,41 +20,52 @@ module Nokogiri
|
|
17
20
|
# node['class'] = 'green' # => "green"
|
18
21
|
# node.to_html # => "<a href=\"#foo\" id=\"link\" class=\"green\">link</a>"
|
19
22
|
#
|
20
|
-
# See the method group entitled
|
23
|
+
# See the method group entitled Node@Working+With+Node+Attributes for the full set of methods.
|
24
|
+
#
|
25
|
+
# == Navigation
|
21
26
|
#
|
22
|
-
#
|
23
|
-
# tree. For navigating your tree, see:
|
27
|
+
# Nokogiri::XML::Node also has methods that let you move around your tree:
|
24
28
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# * {#next}
|
28
|
-
# * {#previous}
|
29
|
+
# [#parent, #children, #next, #previous]
|
30
|
+
# Navigate up, down, or through siblings.
|
29
31
|
#
|
30
|
-
#
|
31
|
-
# its subtree), there are a few methods you might want to use:
|
32
|
+
# See the method group entitled Node@Traversing+Document+Structure for the full set of methods.
|
32
33
|
#
|
33
|
-
#
|
34
|
-
#
|
34
|
+
# == Serialization
|
35
|
+
#
|
36
|
+
# When printing or otherwise emitting a document or a node (and its subtree), there are a few
|
37
|
+
# methods you might want to use:
|
38
|
+
#
|
39
|
+
# [#content, #text, #inner_text, #to_str]
|
40
|
+
# These methods will all **emit plaintext**,
|
41
|
+
# meaning that entities will be replaced (e.g., +<+ will be replaced with +<+), meaning
|
35
42
|
# that any sanitizing will likely be un-done in the output.
|
36
43
|
#
|
37
|
-
#
|
38
|
-
# properly-escaped markup
|
39
|
-
# parsers, etc.
|
44
|
+
# [#to_s, #to_xml, #to_html, #inner_html]
|
45
|
+
# These methods will all **emit properly-escaped markup**, meaning that it's suitable for
|
46
|
+
# consumption by browsers, parsers, etc.
|
47
|
+
#
|
48
|
+
# See the method group entitled Node@Serialization+and+Generating+Output for the full set of methods.
|
49
|
+
#
|
50
|
+
# == Searching
|
40
51
|
#
|
41
|
-
# You may search this node's subtree using
|
52
|
+
# You may search this node's subtree using methods like #xpath and #css.
|
53
|
+
#
|
54
|
+
# See the method group entitled Node@Searching+via+XPath+or+CSS+Queries for the full set of methods.
|
42
55
|
#
|
43
56
|
class Node
|
44
57
|
include Nokogiri::XML::PP::Node
|
45
58
|
include Nokogiri::XML::Searchable
|
59
|
+
include Nokogiri::ClassResolver
|
46
60
|
include Enumerable
|
47
61
|
|
48
|
-
# Element node type, see
|
62
|
+
# Element node type, see Nokogiri::XML::Node#element?
|
49
63
|
ELEMENT_NODE = 1
|
50
64
|
# Attribute node type
|
51
65
|
ATTRIBUTE_NODE = 2
|
52
|
-
# Text node type, see
|
66
|
+
# Text node type, see Nokogiri::XML::Node#text?
|
53
67
|
TEXT_NODE = 3
|
54
|
-
# CDATA node type, see
|
68
|
+
# CDATA node type, see Nokogiri::XML::Node#cdata?
|
55
69
|
CDATA_SECTION_NODE = 4
|
56
70
|
# Entity reference node type
|
57
71
|
ENTITY_REF_NODE = 5
|
@@ -59,9 +73,9 @@ module Nokogiri
|
|
59
73
|
ENTITY_NODE = 6
|
60
74
|
# PI node type
|
61
75
|
PI_NODE = 7
|
62
|
-
# Comment node type, see
|
76
|
+
# Comment node type, see Nokogiri::XML::Node#comment?
|
63
77
|
COMMENT_NODE = 8
|
64
|
-
# Document node type, see
|
78
|
+
# Document node type, see Nokogiri::XML::Node#xml?
|
65
79
|
DOCUMENT_NODE = 9
|
66
80
|
# Document type node type
|
67
81
|
DOCUMENT_TYPE_NODE = 10
|
@@ -69,7 +83,7 @@ module Nokogiri
|
|
69
83
|
DOCUMENT_FRAG_NODE = 11
|
70
84
|
# Notation node type
|
71
85
|
NOTATION_NODE = 12
|
72
|
-
# HTML document node type, see
|
86
|
+
# HTML document node type, see Nokogiri::XML::Node#html?
|
73
87
|
HTML_DOCUMENT_NODE = 13
|
74
88
|
# DTD node type
|
75
89
|
DTD_NODE = 14
|
@@ -88,13 +102,27 @@ module Nokogiri
|
|
88
102
|
# DOCB document node type
|
89
103
|
DOCB_DOCUMENT_NODE = 21
|
90
104
|
|
91
|
-
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
105
|
+
#
|
106
|
+
# :call-seq:
|
107
|
+
# new(name, document) -> Nokogiri::XML::Node
|
108
|
+
# new(name, document) { |node| ... } -> Nokogiri::XML::Node
|
109
|
+
#
|
110
|
+
# Create a new node with +name+ that belongs to +document+.
|
111
|
+
#
|
112
|
+
# If you intend to add a node to a document tree, it's likely that you will prefer one of the
|
113
|
+
# Nokogiri::XML::Node methods like #add_child, #add_next_sibling, #replace, etc. which will
|
114
|
+
# both create an element (or subtree) and place it in the document tree.
|
115
|
+
#
|
116
|
+
# Another alternative, if you are concerned about performance, is
|
117
|
+
# Nokogiri::XML::Document#create_element which accepts additional arguments for contents or
|
118
|
+
# attributes but (like this method) avoids parsing markup.
|
119
|
+
#
|
120
|
+
# [Parameters]
|
121
|
+
# - +name+ (String)
|
122
|
+
# - +document+ (Nokogiri::XML::Document) The document to which the the returned node will belong.
|
123
|
+
# [Yields] Nokogiri::XML::Node
|
124
|
+
# [Returns] Nokogiri::XML::Node
|
125
|
+
#
|
98
126
|
def initialize(name, document)
|
99
127
|
# This is intentionally empty.
|
100
128
|
end
|
@@ -105,18 +133,7 @@ module Nokogiri
|
|
105
133
|
document.decorate(self)
|
106
134
|
end
|
107
135
|
|
108
|
-
#
|
109
|
-
|
110
|
-
###
|
111
|
-
# Search this node's immediate children using CSS selector +selector+
|
112
|
-
def >(selector)
|
113
|
-
ns = document.root.namespaces
|
114
|
-
xpath CSS.xpath_for(selector, :prefix => "./", :ns => ns).first
|
115
|
-
end
|
116
|
-
|
117
|
-
# @!endgroup
|
118
|
-
|
119
|
-
# @!group Manipulating Document Structure
|
136
|
+
# :section: Manipulating Document Structure
|
120
137
|
|
121
138
|
###
|
122
139
|
# Add +node_or_tags+ as a child of this Node.
|
@@ -128,9 +145,9 @@ module Nokogiri
|
|
128
145
|
def add_child(node_or_tags)
|
129
146
|
node_or_tags = coerce(node_or_tags)
|
130
147
|
if node_or_tags.is_a?(XML::NodeSet)
|
131
|
-
node_or_tags.each { |n| add_child_node_and_reparent_attrs
|
148
|
+
node_or_tags.each { |n| add_child_node_and_reparent_attrs(n) }
|
132
149
|
else
|
133
|
-
add_child_node_and_reparent_attrs
|
150
|
+
add_child_node_and_reparent_attrs(node_or_tags)
|
134
151
|
end
|
135
152
|
node_or_tags
|
136
153
|
end
|
@@ -143,9 +160,9 @@ module Nokogiri
|
|
143
160
|
#
|
144
161
|
# Also see related method +add_child+.
|
145
162
|
def prepend_child(node_or_tags)
|
146
|
-
if first = children.first
|
163
|
+
if (first = children.first)
|
147
164
|
# Mimic the error add_child would raise.
|
148
|
-
raise
|
165
|
+
raise "Document already has a root node" if document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
149
166
|
first.__send__(:add_sibling, :previous, node_or_tags)
|
150
167
|
else
|
151
168
|
add_child(node_or_tags)
|
@@ -171,7 +188,7 @@ module Nokogiri
|
|
171
188
|
#
|
172
189
|
# Also see related method +add_child+.
|
173
190
|
def <<(node_or_tags)
|
174
|
-
add_child
|
191
|
+
add_child(node_or_tags)
|
175
192
|
self
|
176
193
|
end
|
177
194
|
|
@@ -183,9 +200,10 @@ module Nokogiri
|
|
183
200
|
#
|
184
201
|
# Also see related method +before+.
|
185
202
|
def add_previous_sibling(node_or_tags)
|
186
|
-
raise ArgumentError
|
203
|
+
raise ArgumentError,
|
204
|
+
"A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
187
205
|
|
188
|
-
add_sibling
|
206
|
+
add_sibling(:previous, node_or_tags)
|
189
207
|
end
|
190
208
|
|
191
209
|
###
|
@@ -196,9 +214,10 @@ module Nokogiri
|
|
196
214
|
#
|
197
215
|
# Also see related method +after+.
|
198
216
|
def add_next_sibling(node_or_tags)
|
199
|
-
raise ArgumentError
|
217
|
+
raise ArgumentError,
|
218
|
+
"A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
200
219
|
|
201
|
-
add_sibling
|
220
|
+
add_sibling(:next, node_or_tags)
|
202
221
|
end
|
203
222
|
|
204
223
|
####
|
@@ -209,7 +228,7 @@ module Nokogiri
|
|
209
228
|
#
|
210
229
|
# Also see related method +add_previous_sibling+.
|
211
230
|
def before(node_or_tags)
|
212
|
-
add_previous_sibling
|
231
|
+
add_previous_sibling(node_or_tags)
|
213
232
|
self
|
214
233
|
end
|
215
234
|
|
@@ -221,7 +240,7 @@ module Nokogiri
|
|
221
240
|
#
|
222
241
|
# Also see related method +add_next_sibling+.
|
223
242
|
def after(node_or_tags)
|
224
|
-
add_next_sibling
|
243
|
+
add_next_sibling(node_or_tags)
|
225
244
|
self
|
226
245
|
end
|
227
246
|
|
@@ -229,30 +248,24 @@ module Nokogiri
|
|
229
248
|
# Set the inner html for this Node to +node_or_tags+
|
230
249
|
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
|
231
250
|
#
|
232
|
-
# Returns self.
|
233
|
-
#
|
234
251
|
# Also see related method +children=+
|
235
252
|
def inner_html=(node_or_tags)
|
236
253
|
self.children = node_or_tags
|
237
|
-
self
|
238
254
|
end
|
239
255
|
|
240
256
|
####
|
241
257
|
# Set the inner html for this Node +node_or_tags+
|
242
258
|
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
|
243
259
|
#
|
244
|
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
245
|
-
#
|
246
260
|
# Also see related method +inner_html=+
|
247
261
|
def children=(node_or_tags)
|
248
262
|
node_or_tags = coerce(node_or_tags)
|
249
263
|
children.unlink
|
250
264
|
if node_or_tags.is_a?(XML::NodeSet)
|
251
|
-
node_or_tags.each { |n| add_child_node_and_reparent_attrs
|
265
|
+
node_or_tags.each { |n| add_child_node_and_reparent_attrs(n) }
|
252
266
|
else
|
253
|
-
add_child_node_and_reparent_attrs
|
267
|
+
add_child_node_and_reparent_attrs(node_or_tags)
|
254
268
|
end
|
255
|
-
node_or_tags
|
256
269
|
end
|
257
270
|
|
258
271
|
####
|
@@ -270,19 +283,19 @@ module Nokogiri
|
|
270
283
|
# libxml is trying to find a parent node that is an element or document
|
271
284
|
# so I can't tell if this is bug in libxml or not. issue #775.
|
272
285
|
if text?
|
273
|
-
replacee = Nokogiri::XML::Node.new
|
274
|
-
add_previous_sibling_node
|
286
|
+
replacee = Nokogiri::XML::Node.new("dummy", document)
|
287
|
+
add_previous_sibling_node(replacee)
|
275
288
|
unlink
|
276
|
-
return replacee.replace
|
289
|
+
return replacee.replace(node_or_tags)
|
277
290
|
end
|
278
291
|
|
279
292
|
node_or_tags = parent.coerce(node_or_tags)
|
280
293
|
|
281
294
|
if node_or_tags.is_a?(XML::NodeSet)
|
282
|
-
node_or_tags.each { |n| add_previous_sibling
|
295
|
+
node_or_tags.each { |n| add_previous_sibling(n) }
|
283
296
|
unlink
|
284
297
|
else
|
285
|
-
replace_node
|
298
|
+
replace_node(node_or_tags)
|
286
299
|
end
|
287
300
|
node_or_tags
|
288
301
|
end
|
@@ -295,7 +308,7 @@ module Nokogiri
|
|
295
308
|
#
|
296
309
|
# Also see related method +replace+.
|
297
310
|
def swap(node_or_tags)
|
298
|
-
replace
|
311
|
+
replace(node_or_tags)
|
299
312
|
self
|
300
313
|
end
|
301
314
|
|
@@ -309,7 +322,6 @@ module Nokogiri
|
|
309
322
|
# Set the parent Node for this Node
|
310
323
|
def parent=(parent_node)
|
311
324
|
parent_node.add_child(self)
|
312
|
-
parent_node
|
313
325
|
end
|
314
326
|
|
315
327
|
###
|
@@ -338,7 +350,7 @@ module Nokogiri
|
|
338
350
|
raise ArgumentError, "namespace must be declared on the same document"
|
339
351
|
end
|
340
352
|
|
341
|
-
set_namespace
|
353
|
+
set_namespace(ns)
|
342
354
|
end
|
343
355
|
|
344
356
|
###
|
@@ -347,52 +359,159 @@ module Nokogiri
|
|
347
359
|
# passed to it, allowing more convenient modification of the parser options.
|
348
360
|
def do_xinclude(options = XML::ParseOptions::DEFAULT_XML)
|
349
361
|
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
350
|
-
|
351
|
-
# give options to user
|
352
362
|
yield options if block_given?
|
353
363
|
|
354
364
|
# call c extension
|
355
365
|
process_xincludes(options.to_i)
|
356
366
|
end
|
357
367
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
368
|
+
alias_method :next, :next_sibling
|
369
|
+
alias_method :previous, :previous_sibling
|
370
|
+
alias_method :next=, :add_next_sibling
|
371
|
+
alias_method :previous=, :add_previous_sibling
|
372
|
+
alias_method :remove, :unlink
|
373
|
+
alias_method :name=, :node_name=
|
374
|
+
alias_method :add_namespace, :add_namespace_definition
|
365
375
|
|
366
|
-
#
|
376
|
+
# :section:
|
367
377
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
378
|
+
alias_method :inner_text, :content
|
379
|
+
alias_method :text, :content
|
380
|
+
alias_method :to_str, :content
|
381
|
+
alias_method :name, :node_name
|
382
|
+
alias_method :type, :node_type
|
383
|
+
alias_method :clone, :dup
|
384
|
+
alias_method :elements, :element_children
|
375
385
|
|
376
|
-
#
|
386
|
+
# :section: Working With Node Attributes
|
377
387
|
|
378
|
-
|
379
|
-
#
|
388
|
+
# :call-seq: [](name) → (String, nil)
|
389
|
+
#
|
390
|
+
# Fetch an attribute from this node.
|
391
|
+
#
|
392
|
+
# ⚠ Note that attributes with namespaces cannot be accessed with this method. To access
|
393
|
+
# namespaced attributes, use #attribute_with_ns.
|
394
|
+
#
|
395
|
+
# [Returns] (String, nil) value of the attribute +name+, or +nil+ if no matching attribute exists
|
396
|
+
#
|
397
|
+
# *Example*
|
398
|
+
#
|
399
|
+
# doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
|
400
|
+
# child = doc.at_css("child")
|
401
|
+
# child["size"] # => "large"
|
402
|
+
# child["class"] # => "big wide tall"
|
403
|
+
#
|
404
|
+
# *Example:* Namespaced attributes will not be returned.
|
405
|
+
#
|
406
|
+
# ⚠ Note namespaced attributes may be accessed with #attribute or #attribute_with_ns
|
407
|
+
#
|
408
|
+
# doc = Nokogiri::XML(<<~EOF)
|
409
|
+
# <root xmlns:width='http://example.com/widths'>
|
410
|
+
# <child width:size='broad'/>
|
411
|
+
# </root>
|
412
|
+
# EOF
|
413
|
+
# doc.at_css("child")["size"] # => nil
|
414
|
+
# doc.at_css("child").attribute("size").value # => "broad"
|
415
|
+
# doc.at_css("child").attribute_with_ns("size", "http://example.com/widths").value
|
416
|
+
# # => "broad"
|
417
|
+
#
|
380
418
|
def [](name)
|
381
419
|
get(name.to_s)
|
382
420
|
end
|
383
421
|
|
384
|
-
|
385
|
-
#
|
422
|
+
# :call-seq: []=(name, value) → value
|
423
|
+
#
|
424
|
+
# Update the attribute +name+ to +value+, or create the attribute if it does not exist.
|
425
|
+
#
|
426
|
+
# ⚠ Note that attributes with namespaces cannot be accessed with this method. To access
|
427
|
+
# namespaced attributes for update, use #attribute_with_ns. To add a namespaced attribute,
|
428
|
+
# see the example below.
|
429
|
+
#
|
430
|
+
# [Returns] +value+
|
431
|
+
#
|
432
|
+
# *Example*
|
433
|
+
#
|
434
|
+
# doc = Nokogiri::XML("<root><child/></root>")
|
435
|
+
# child = doc.at_css("child")
|
436
|
+
# child["size"] = "broad"
|
437
|
+
# child.to_html
|
438
|
+
# # => "<child size=\"broad\"></child>"
|
439
|
+
#
|
440
|
+
# *Example:* Add a namespaced attribute.
|
441
|
+
#
|
442
|
+
# doc = Nokogiri::XML(<<~EOF)
|
443
|
+
# <root xmlns:width='http://example.com/widths'>
|
444
|
+
# <child/>
|
445
|
+
# </root>
|
446
|
+
# EOF
|
447
|
+
# child = doc.at_css("child")
|
448
|
+
# child["size"] = "broad"
|
449
|
+
# ns = doc.root.namespace_definitions.find { |ns| ns.prefix == "width" }
|
450
|
+
# child.attribute("size").namespace = ns
|
451
|
+
# doc.to_html
|
452
|
+
# # => "<root xmlns:width=\"http://example.com/widths\">\n" +
|
453
|
+
# # " <child width:size=\"broad\"></child>\n" +
|
454
|
+
# # "</root>\n"
|
455
|
+
#
|
386
456
|
def []=(name, value)
|
387
|
-
set
|
457
|
+
set(name.to_s, value.to_s)
|
388
458
|
end
|
389
459
|
|
390
|
-
|
391
|
-
#
|
392
|
-
#
|
393
|
-
#
|
394
|
-
#
|
395
|
-
#
|
460
|
+
#
|
461
|
+
# :call-seq: attributes() → Hash<String ⇒ Nokogiri::XML::Attr>
|
462
|
+
#
|
463
|
+
# Fetch this node's attributes.
|
464
|
+
#
|
465
|
+
# ⚠ Because the keys do not include any namespace information for the attribute, in case of a
|
466
|
+
# simple name collision, not all attributes will be returned. In this case, you will need to
|
467
|
+
# use #attribute_nodes.
|
468
|
+
#
|
469
|
+
# [Returns]
|
470
|
+
# Hash containing attributes belonging to +self+. The hash keys are String attribute
|
471
|
+
# names (without the namespace), and the hash values are Nokogiri::XML::Attr.
|
472
|
+
#
|
473
|
+
# *Example* with no namespaces:
|
474
|
+
#
|
475
|
+
# doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
|
476
|
+
# doc.at_css("child").attributes
|
477
|
+
# # => {"size"=>#(Attr:0x550 { name = "size", value = "large" }),
|
478
|
+
# # "class"=>#(Attr:0x564 { name = "class", value = "big wide tall" })}
|
479
|
+
#
|
480
|
+
# *Example* with a namespace:
|
481
|
+
#
|
482
|
+
# doc = Nokogiri::XML("<root xmlns:desc='http://example.com/sizes'><child desc:size='large'/></root>")
|
483
|
+
# doc.at_css("child").attributes
|
484
|
+
# # => {"size"=>
|
485
|
+
# # #(Attr:0x550 {
|
486
|
+
# # name = "size",
|
487
|
+
# # namespace = #(Namespace:0x564 {
|
488
|
+
# # prefix = "desc",
|
489
|
+
# # href = "http://example.com/sizes"
|
490
|
+
# # }),
|
491
|
+
# # value = "large"
|
492
|
+
# # })}
|
493
|
+
#
|
494
|
+
# *Example* with an attribute name collision:
|
495
|
+
#
|
496
|
+
# ⚠ Note that only one of the attributes is returned in the Hash.
|
497
|
+
#
|
498
|
+
# doc = Nokogiri::XML(<<~EOF)
|
499
|
+
# <root xmlns:width='http://example.com/widths'
|
500
|
+
# xmlns:height='http://example.com/heights'>
|
501
|
+
# <child width:size='broad' height:size='tall'/>
|
502
|
+
# </root>
|
503
|
+
# EOF
|
504
|
+
# doc.at_css("child").attributes
|
505
|
+
# # => {"size"=>
|
506
|
+
# # #(Attr:0x550 {
|
507
|
+
# # name = "size",
|
508
|
+
# # namespace = #(Namespace:0x564 {
|
509
|
+
# # prefix = "height",
|
510
|
+
# # href = "http://example.com/heights"
|
511
|
+
# # }),
|
512
|
+
# # value = "tall"
|
513
|
+
# # })}
|
514
|
+
#
|
396
515
|
def attributes
|
397
516
|
attribute_nodes.each_with_object({}) do |node, hash|
|
398
517
|
hash[node.node_name] = node
|
@@ -408,7 +527,7 @@ module Nokogiri
|
|
408
527
|
###
|
409
528
|
# Does this Node's attributes include <value>
|
410
529
|
def value?(value)
|
411
|
-
values.include?
|
530
|
+
values.include?(value)
|
412
531
|
end
|
413
532
|
|
414
533
|
###
|
@@ -420,36 +539,36 @@ module Nokogiri
|
|
420
539
|
###
|
421
540
|
# Iterate over each attribute name and value pair for this Node.
|
422
541
|
def each
|
423
|
-
attribute_nodes.each
|
542
|
+
attribute_nodes.each do |node|
|
424
543
|
yield [node.node_name, node.value]
|
425
|
-
|
544
|
+
end
|
426
545
|
end
|
427
546
|
|
428
547
|
###
|
429
548
|
# Remove the attribute named +name+
|
430
549
|
def remove_attribute(name)
|
431
|
-
attr = attributes[name].remove if key?
|
550
|
+
attr = attributes[name].remove if key?(name)
|
432
551
|
clear_xpath_context if Nokogiri.jruby?
|
433
552
|
attr
|
434
553
|
end
|
435
554
|
|
436
|
-
#
|
555
|
+
#
|
556
|
+
# :call-seq: classes() → Array<String>
|
557
|
+
#
|
558
|
+
# Fetch CSS class names of a Node.
|
437
559
|
#
|
438
560
|
# This is a convenience function and is equivalent to:
|
561
|
+
#
|
439
562
|
# node.kwattr_values("class")
|
440
563
|
#
|
441
|
-
#
|
442
|
-
# @see #add_class
|
443
|
-
# @see #append_class
|
444
|
-
# @see #remove_class
|
564
|
+
# See related: #kwattr_values, #add_class, #append_class, #remove_class
|
445
565
|
#
|
446
|
-
#
|
566
|
+
# [Returns]
|
567
|
+
# The CSS classes (Array of String) present in the Node's "class" attribute. If the
|
568
|
+
# attribute is empty or non-existent, the return value is an empty array.
|
447
569
|
#
|
448
|
-
#
|
449
|
-
# the attribute is empty or non-existent, the return value is
|
450
|
-
# an empty array.
|
570
|
+
# *Example*
|
451
571
|
#
|
452
|
-
# @example
|
453
572
|
# node # => <div class="section title header"></div>
|
454
573
|
# node.classes # => ["section", "title", "header"]
|
455
574
|
#
|
@@ -457,42 +576,45 @@ module Nokogiri
|
|
457
576
|
kwattr_values("class")
|
458
577
|
end
|
459
578
|
|
460
|
-
#
|
461
|
-
#
|
462
|
-
#
|
463
|
-
#
|
464
|
-
#
|
579
|
+
#
|
580
|
+
# :call-seq: add_class(names) → self
|
581
|
+
#
|
582
|
+
# Ensure HTML CSS classes are present on +self+. Any CSS classes in +names+ that already exist
|
583
|
+
# in the "class" attribute are _not_ added. Note that any existing duplicates in the
|
584
|
+
# "class" attribute are not removed. Compare with #append_class.
|
465
585
|
#
|
466
586
|
# This is a convenience function and is equivalent to:
|
587
|
+
#
|
467
588
|
# node.kwattr_add("class", names)
|
468
589
|
#
|
469
|
-
#
|
470
|
-
#
|
471
|
-
#
|
472
|
-
#
|
590
|
+
# See related: #kwattr_add, #classes, #append_class, #remove_class
|
591
|
+
#
|
592
|
+
# [Parameters]
|
593
|
+
# - +names+ (String, Array<String>)
|
473
594
|
#
|
474
|
-
#
|
595
|
+
# CSS class names to be added to the Node's "class" attribute. May be a string containing
|
596
|
+
# whitespace-delimited names, or an Array of String names. Any class names already present
|
597
|
+
# will not be added. Any class names not present will be added. If no "class" attribute
|
598
|
+
# exists, one is created.
|
475
599
|
#
|
476
|
-
#
|
477
|
-
# attribute. May be a string containing whitespace-delimited
|
478
|
-
# names, or an Array of String names. Any class names already
|
479
|
-
# present will not be added. Any class names not present will
|
480
|
-
# be added. If no +class+ attribute exists, one is created.
|
600
|
+
# [Returns] +self+ (Node) for ease of chaining method calls.
|
481
601
|
#
|
482
|
-
#
|
602
|
+
# *Example:* Ensure that the node has CSS class "section"
|
483
603
|
#
|
484
|
-
# @example Ensure that a +Node+ has CSS class "section"
|
485
604
|
# node # => <div></div>
|
486
605
|
# node.add_class("section") # => <div class="section"></div>
|
487
606
|
# node.add_class("section") # => <div class="section"></div> # duplicate not added
|
488
607
|
#
|
489
|
-
#
|
608
|
+
# *Example:* Ensure that the node has CSS classes "section" and "header", via a String argument
|
609
|
+
#
|
610
|
+
# Note that the CSS class "section" is not added because it is already present.
|
611
|
+
# Note also that the pre-existing duplicate CSS class "section" is not removed.
|
612
|
+
#
|
490
613
|
# node # => <div class="section section"></div>
|
491
614
|
# node.add_class("section header") # => <div class="section section header"></div>
|
492
|
-
# # Note that the CSS class "section" is not added because it is already present.
|
493
|
-
# # Note also that the pre-existing duplicate CSS class "section" is not removed.
|
494
615
|
#
|
495
|
-
#
|
616
|
+
# *Example:* Ensure that the node has CSS classes "section" and "header", via an Array argument
|
617
|
+
#
|
496
618
|
# node # => <div></div>
|
497
619
|
# node.add_class(["section", "header"]) # => <div class="section header"></div>
|
498
620
|
#
|
@@ -500,39 +622,42 @@ module Nokogiri
|
|
500
622
|
kwattr_add("class", names)
|
501
623
|
end
|
502
624
|
|
503
|
-
#
|
504
|
-
#
|
625
|
+
#
|
626
|
+
# :call-seq: append_class(names) → self
|
627
|
+
#
|
628
|
+
# Add HTML CSS classes to +self+, regardless of duplication. Compare with #add_class.
|
505
629
|
#
|
506
630
|
# This is a convenience function and is equivalent to:
|
631
|
+
#
|
507
632
|
# node.kwattr_append("class", names)
|
508
633
|
#
|
509
|
-
#
|
510
|
-
#
|
511
|
-
#
|
512
|
-
#
|
634
|
+
# See related: #kwattr_append, #classes, #add_class, #remove_class
|
635
|
+
#
|
636
|
+
# [Parameters]
|
637
|
+
# - +names+ (String, Array<String>)
|
513
638
|
#
|
514
|
-
#
|
639
|
+
# CSS class names to be appended to the Node's "class" attribute. May be a string containing
|
640
|
+
# whitespace-delimited names, or an Array of String names. All class names passed in will be
|
641
|
+
# appended to the "class" attribute even if they are already present in the attribute
|
642
|
+
# value. If no "class" attribute exists, one is created.
|
515
643
|
#
|
516
|
-
#
|
517
|
-
# attribute. May be a string containing whitespace-delimited
|
518
|
-
# names, or an Array of String names. All class names passed
|
519
|
-
# in will be appended to the +class+ attribute even if they
|
520
|
-
# are already present in the attribute value. If no +class+
|
521
|
-
# attribute exists, one is created.
|
644
|
+
# [Returns] +self+ (Node) for ease of chaining method calls.
|
522
645
|
#
|
523
|
-
#
|
646
|
+
# *Example:* Append "section" to the node's CSS "class" attribute
|
524
647
|
#
|
525
|
-
# @example Append "section" to a +Node+'s CSS +class+ attriubute
|
526
648
|
# node # => <div></div>
|
527
649
|
# node.append_class("section") # => <div class="section"></div>
|
528
650
|
# node.append_class("section") # => <div class="section section"></div> # duplicate added!
|
529
651
|
#
|
530
|
-
#
|
652
|
+
# *Example:* Append "section" and "header" to the noded's CSS "class" attribute, via a String argument
|
653
|
+
#
|
654
|
+
# Note that the CSS class "section" is appended even though it is already present
|
655
|
+
#
|
531
656
|
# node # => <div class="section section"></div>
|
532
657
|
# node.append_class("section header") # => <div class="section section section header"></div>
|
533
|
-
# # Note that the CSS class "section" is appended even though it is already present.
|
534
658
|
#
|
535
|
-
#
|
659
|
+
# *Example:* Append "section" and "header" to the node's CSS "class" attribute, via an Array argument
|
660
|
+
#
|
536
661
|
# node # => <div></div>
|
537
662
|
# node.append_class(["section", "header"]) # => <div class="section header"></div>
|
538
663
|
# node.append_class(["section", "header"]) # => <div class="section header section header"></div>
|
@@ -541,118 +666,135 @@ module Nokogiri
|
|
541
666
|
kwattr_append("class", names)
|
542
667
|
end
|
543
668
|
|
544
|
-
#
|
545
|
-
#
|
546
|
-
#
|
669
|
+
# :call-seq:
|
670
|
+
# remove_class(css_classes) → self
|
671
|
+
#
|
672
|
+
# Remove HTML CSS classes from this node. Any CSS class names in +css_classes+ that exist in
|
673
|
+
# this node's "class" attribute are removed, including any multiple entries.
|
547
674
|
#
|
548
|
-
# If no CSS classes remain after this operation, or if +
|
549
|
-
#
|
675
|
+
# If no CSS classes remain after this operation, or if +css_classes+ is +nil+, the "class"
|
676
|
+
# attribute is deleted from the node.
|
550
677
|
#
|
551
678
|
# This is a convenience function and is equivalent to:
|
552
|
-
# node.kwattr_remove("class", names)
|
553
679
|
#
|
554
|
-
#
|
555
|
-
# @see #classes
|
556
|
-
# @see #add_class
|
557
|
-
# @see #append_class
|
680
|
+
# node.kwattr_remove("class", css_classes)
|
558
681
|
#
|
559
|
-
#
|
682
|
+
# Also see #kwattr_remove, #classes, #add_class, #append_class
|
560
683
|
#
|
561
|
-
#
|
562
|
-
#
|
563
|
-
# String names. Any class names already present will be removed. If no
|
564
|
-
# CSS classes remain, the +class+ attribute is deleted.
|
684
|
+
# [Parameters]
|
685
|
+
# - +css_classes+ (String, Array<String>)
|
565
686
|
#
|
566
|
-
#
|
687
|
+
# CSS class names to be removed from the Node's
|
688
|
+
# "class" attribute. May be a string containing whitespace-delimited names, or an Array of
|
689
|
+
# String names. Any class names already present will be removed. If no CSS classes remain,
|
690
|
+
# the "class" attribute is deleted.
|
567
691
|
#
|
568
|
-
#
|
569
|
-
#
|
692
|
+
# [Returns] +self+ (Nokogiri::XML::Node) for ease of chaining method calls.
|
693
|
+
#
|
694
|
+
# *Example*: Deleting a CSS class
|
695
|
+
#
|
696
|
+
# Note that all instances of the class "section" are removed from the "class" attribute.
|
697
|
+
#
|
698
|
+
# node # => <div class="section header section"></div>
|
570
699
|
# node.remove_class("section") # => <div class="header"></div>
|
571
|
-
#
|
700
|
+
#
|
701
|
+
# *Example*: Deleting the only remaining CSS class
|
702
|
+
#
|
703
|
+
# Note that the attribute is removed once there are no remaining classes.
|
704
|
+
#
|
705
|
+
# node # => <div class="section"></div>
|
706
|
+
# node.remove_class("section") # => <div></div>
|
707
|
+
#
|
708
|
+
# *Example*: Deleting multiple CSS classes
|
709
|
+
#
|
710
|
+
# Note that the "class" attribute is deleted once it's empty.
|
711
|
+
#
|
712
|
+
# node # => <div class="section header float"></div>
|
713
|
+
# node.remove_class(["section", "float"]) # => <div class="header"></div>
|
572
714
|
#
|
573
715
|
def remove_class(names = nil)
|
574
716
|
kwattr_remove("class", names)
|
575
717
|
end
|
576
718
|
|
577
|
-
#
|
719
|
+
# :call-seq:
|
720
|
+
# kwattr_values(attribute_name) → Array<String>
|
721
|
+
#
|
722
|
+
# Fetch values from a keyword attribute of a Node.
|
578
723
|
#
|
579
|
-
# A "keyword attribute" is a node attribute that contains a set
|
580
|
-
#
|
581
|
-
#
|
582
|
-
#
|
583
|
-
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
724
|
+
# A "keyword attribute" is a node attribute that contains a set of space-delimited
|
725
|
+
# values. Perhaps the most familiar example of this is the HTML "class" attribute used to
|
726
|
+
# contain CSS classes. But other keyword attributes exist, for instance
|
727
|
+
# {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
|
584
728
|
#
|
585
|
-
#
|
586
|
-
# @see #kwattr_add
|
587
|
-
# @see #kwattr_append
|
588
|
-
# @see #kwattr_remove
|
729
|
+
# See also #classes, #kwattr_add, #kwattr_append, #kwattr_remove
|
589
730
|
#
|
590
|
-
#
|
731
|
+
# [Parameters]
|
732
|
+
# - +attribute_name+ (String) The name of the keyword attribute to be inspected.
|
591
733
|
#
|
592
|
-
#
|
734
|
+
# [Returns]
|
735
|
+
# (Array<String>) The values present in the Node's +attribute_name+ attribute. If the
|
736
|
+
# attribute is empty or non-existent, the return value is an empty array.
|
593
737
|
#
|
594
|
-
#
|
595
|
-
# attribute. If the attribute is empty or non-existent, the
|
596
|
-
# return value is an empty array.
|
738
|
+
# *Example:*
|
597
739
|
#
|
598
|
-
# @example
|
599
740
|
# node # => <a rel="nofollow noopener external">link</a>
|
600
741
|
# node.kwattr_values("rel") # => ["nofollow", "noopener", "external"]
|
601
742
|
#
|
602
|
-
#
|
603
|
-
#
|
743
|
+
# Since v1.11.0
|
604
744
|
def kwattr_values(attribute_name)
|
605
745
|
keywordify(get_attribute(attribute_name) || [])
|
606
746
|
end
|
607
747
|
|
748
|
+
# :call-seq:
|
749
|
+
# kwattr_add(attribute_name, keywords) → self
|
750
|
+
#
|
608
751
|
# Ensure that values are present in a keyword attribute.
|
609
752
|
#
|
610
|
-
# Any values in +keywords+ that already exist in the
|
611
|
-
#
|
612
|
-
#
|
613
|
-
# with {#kwattr_append}.
|
753
|
+
# Any values in +keywords+ that already exist in the Node's attribute values are _not_
|
754
|
+
# added. Note that any existing duplicates in the attribute values are not removed. Compare
|
755
|
+
# with #kwattr_append.
|
614
756
|
#
|
615
|
-
# A "keyword attribute" is a node attribute that contains a set
|
616
|
-
#
|
617
|
-
#
|
618
|
-
#
|
619
|
-
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
757
|
+
# A "keyword attribute" is a node attribute that contains a set of space-delimited
|
758
|
+
# values. Perhaps the most familiar example of this is the HTML "class" attribute used to
|
759
|
+
# contain CSS classes. But other keyword attributes exist, for instance
|
760
|
+
# {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
|
620
761
|
#
|
621
|
-
#
|
622
|
-
# @see #kwattr_values
|
623
|
-
# @see #kwattr_append
|
624
|
-
# @see #kwattr_remove
|
762
|
+
# See also #add_class, #kwattr_values, #kwattr_append, #kwattr_remove
|
625
763
|
#
|
626
|
-
#
|
764
|
+
# [Parameters]
|
765
|
+
# - +attribute_name+ (String) The name of the keyword attribute to be modified.
|
766
|
+
# - +keywords+ (String, Array<String>)
|
767
|
+
# Keywords to be added to the attribute named +attribute_name+. May be a string containing
|
768
|
+
# whitespace-delimited values, or an Array of String values. Any values already present will
|
769
|
+
# not be added. Any values not present will be added. If the named attribute does not exist,
|
770
|
+
# it is created.
|
627
771
|
#
|
628
|
-
#
|
772
|
+
# [Returns] +self+ (Nokogiri::XML::Node) for ease of chaining method calls.
|
629
773
|
#
|
630
|
-
#
|
631
|
-
# +attribute_name+. May be a string containing
|
632
|
-
# whitespace-delimited values, or an Array of String
|
633
|
-
# values. Any values already present will not be added. Any
|
634
|
-
# values not present will be added. If the named attribute
|
635
|
-
# does not exist, it is created.
|
774
|
+
# *Example:* Ensure that a +Node+ has "nofollow" in its +rel+ attribute.
|
636
775
|
#
|
637
|
-
#
|
776
|
+
# Note that duplicates are not added.
|
638
777
|
#
|
639
|
-
# @example Ensure that a +Node+ has "nofollow" in its +rel+ attribute.
|
640
778
|
# node # => <a></a>
|
641
779
|
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
|
642
|
-
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
|
780
|
+
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
|
781
|
+
#
|
782
|
+
# *Example:* Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via a
|
783
|
+
# String argument.
|
784
|
+
#
|
785
|
+
# Note that "nofollow" is not added because it is already present. Note also that the
|
786
|
+
# pre-existing duplicate "nofollow" is not removed.
|
643
787
|
#
|
644
|
-
# @example Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via a String argument.
|
645
788
|
# node # => <a rel="nofollow nofollow"></a>
|
646
789
|
# node.kwattr_add("rel", "nofollow noreferrer") # => <a rel="nofollow nofollow noreferrer"></a>
|
647
|
-
# # Note that "nofollow" is not added because it is already present.
|
648
|
-
# # Note also that the pre-existing duplicate "nofollow" is not removed.
|
649
790
|
#
|
650
|
-
#
|
791
|
+
# *Example:* Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via
|
792
|
+
# an Array argument.
|
793
|
+
#
|
651
794
|
# node # => <a></a>
|
652
795
|
# node.kwattr_add("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
|
653
796
|
#
|
654
|
-
#
|
655
|
-
#
|
797
|
+
# Since v1.11.0
|
656
798
|
def kwattr_add(attribute_name, keywords)
|
657
799
|
keywords = keywordify(keywords)
|
658
800
|
current_kws = kwattr_values(attribute_name)
|
@@ -661,50 +803,51 @@ module Nokogiri
|
|
661
803
|
self
|
662
804
|
end
|
663
805
|
|
664
|
-
#
|
665
|
-
#
|
806
|
+
# :call-seq:
|
807
|
+
# kwattr_append(attribute_name, keywords) → self
|
666
808
|
#
|
667
|
-
#
|
668
|
-
#
|
669
|
-
# of this is the HTML +class+ attribute used to contain CSS
|
670
|
-
# classes. But other keyword attributes exist, for instance
|
671
|
-
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
809
|
+
# Add keywords to a Node's keyword attribute, regardless of duplication. Compare with
|
810
|
+
# #kwattr_add.
|
672
811
|
#
|
673
|
-
#
|
674
|
-
#
|
675
|
-
#
|
676
|
-
#
|
812
|
+
# A "keyword attribute" is a node attribute that contains a set of space-delimited
|
813
|
+
# values. Perhaps the most familiar example of this is the HTML "class" attribute used to
|
814
|
+
# contain CSS classes. But other keyword attributes exist, for instance
|
815
|
+
# {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
|
677
816
|
#
|
678
|
-
#
|
817
|
+
# See also #append_class, #kwattr_values, #kwattr_add, #kwattr_remove
|
679
818
|
#
|
680
|
-
#
|
819
|
+
# [Parameters]
|
820
|
+
# - +attribute_name+ (String) The name of the keyword attribute to be modified.
|
821
|
+
# - +keywords+ (String, Array<String>)
|
822
|
+
# Keywords to be added to the attribute named +attribute_name+. May be a string containing
|
823
|
+
# whitespace-delimited values, or an Array of String values. All values passed in will be
|
824
|
+
# appended to the named attribute even if they are already present in the attribute. If the
|
825
|
+
# named attribute does not exist, it is created.
|
681
826
|
#
|
682
|
-
#
|
683
|
-
# +attribute_name+. May be a string containing
|
684
|
-
# whitespace-delimited values, or an Array of String
|
685
|
-
# values. All values passed in will be appended to the named
|
686
|
-
# attribute even if they are already present in the
|
687
|
-
# attribute. If the named attribute does not exist, it is
|
688
|
-
# created.
|
827
|
+
# [Returns] +self+ (Node) for ease of chaining method calls.
|
689
828
|
#
|
690
|
-
#
|
829
|
+
# *Example:* Append "nofollow" to the +rel+ attribute.
|
830
|
+
#
|
831
|
+
# Note that duplicates are added.
|
691
832
|
#
|
692
|
-
# @example Append "nofollow" to the +rel+ attribute.
|
693
833
|
# node # => <a></a>
|
694
834
|
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow"></a>
|
695
|
-
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow nofollow"></a>
|
835
|
+
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow nofollow"></a>
|
836
|
+
#
|
837
|
+
# *Example:* Append "nofollow" and "noreferrer" to the +rel+ attribute, via a String argument.
|
838
|
+
#
|
839
|
+
# Note that "nofollow" is appended even though it is already present.
|
696
840
|
#
|
697
|
-
# @example Append "nofollow" and "noreferrer" to the +rel+ attribute, via a String argument.
|
698
841
|
# node # => <a rel="nofollow"></a>
|
699
842
|
# node.kwattr_append("rel", "nofollow noreferrer") # => <a rel="nofollow nofollow noreferrer"></a>
|
700
|
-
# # Note that "nofollow" is appended even though it is already present.
|
701
843
|
#
|
702
|
-
#
|
844
|
+
#
|
845
|
+
# *Example:* Append "nofollow" and "noreferrer" to the +rel+ attribute, via an Array argument.
|
846
|
+
#
|
703
847
|
# node # => <a></a>
|
704
848
|
# node.kwattr_append("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
|
705
849
|
#
|
706
|
-
#
|
707
|
-
#
|
850
|
+
# Since v1.11.0
|
708
851
|
def kwattr_append(attribute_name, keywords)
|
709
852
|
keywords = keywordify(keywords)
|
710
853
|
current_kws = kwattr_values(attribute_name)
|
@@ -713,44 +856,41 @@ module Nokogiri
|
|
713
856
|
self
|
714
857
|
end
|
715
858
|
|
716
|
-
#
|
717
|
-
# keywords
|
718
|
-
# including any multiple entries.
|
859
|
+
# :call-seq:
|
860
|
+
# kwattr_remove(attribute_name, keywords) → self
|
719
861
|
#
|
720
|
-
#
|
721
|
-
#
|
862
|
+
# Remove keywords from a keyword attribute. Any matching keywords that exist in the named
|
863
|
+
# attribute are removed, including any multiple entries.
|
722
864
|
#
|
723
|
-
#
|
724
|
-
#
|
725
|
-
# of this is the HTML +class+ attribute used to contain CSS
|
726
|
-
# classes. But other keyword attributes exist, for instance
|
727
|
-
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
865
|
+
# If no keywords remain after this operation, or if +keywords+ is +nil+, the attribute is
|
866
|
+
# deleted from the node.
|
728
867
|
#
|
729
|
-
#
|
730
|
-
#
|
731
|
-
#
|
732
|
-
#
|
868
|
+
# A "keyword attribute" is a node attribute that contains a set of space-delimited
|
869
|
+
# values. Perhaps the most familiar example of this is the HTML "class" attribute used to
|
870
|
+
# contain CSS classes. But other keyword attributes exist, for instance
|
871
|
+
# {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
|
733
872
|
#
|
734
|
-
#
|
873
|
+
# See also #remove_class, #kwattr_values, #kwattr_add, #kwattr_append
|
735
874
|
#
|
736
|
-
#
|
875
|
+
# [Parameters]
|
876
|
+
# - +attribute_name+ (String) The name of the keyword attribute to be modified.
|
877
|
+
# - +keywords+ (String, Array<String>)
|
878
|
+
# Keywords to be removed from the attribute named +attribute_name+. May be a string
|
879
|
+
# containing whitespace-delimited values, or an Array of String values. Any keywords present
|
880
|
+
# in the named attribute will be removed. If no keywords remain, or if +keywords+ is nil,
|
881
|
+
# the attribute is deleted.
|
737
882
|
#
|
738
|
-
#
|
739
|
-
# +attribute_name+. May be a string containing
|
740
|
-
# whitespace-delimited values, or an Array of String
|
741
|
-
# values. Any keywords present in the named attribute will be
|
742
|
-
# removed. If no keywords remain, or if +keywords+ is nil, the
|
743
|
-
# attribute is deleted.
|
883
|
+
# [Returns] +self+ (Node) for ease of chaining method calls.
|
744
884
|
#
|
745
|
-
#
|
885
|
+
# *Example:*
|
886
|
+
#
|
887
|
+
# Note that the +rel+ attribute is deleted when empty.
|
746
888
|
#
|
747
|
-
# @example
|
748
889
|
# node # => <a rel="nofollow noreferrer">link</a>
|
749
890
|
# node.kwattr_remove("rel", "nofollow") # => <a rel="noreferrer">link</a>
|
750
|
-
# node.kwattr_remove("rel", "noreferrer") # => <a>link</a>
|
751
|
-
#
|
752
|
-
# @since v1.11.0
|
891
|
+
# node.kwattr_remove("rel", "noreferrer") # => <a>link</a>
|
753
892
|
#
|
893
|
+
# Since v1.11.0
|
754
894
|
def kwattr_remove(attribute_name, keywords)
|
755
895
|
if keywords.nil?
|
756
896
|
remove_attribute(attribute_name)
|
@@ -768,13 +908,13 @@ module Nokogiri
|
|
768
908
|
self
|
769
909
|
end
|
770
910
|
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
911
|
+
alias_method :delete, :remove_attribute
|
912
|
+
alias_method :get_attribute, :[]
|
913
|
+
alias_method :attr, :[]
|
914
|
+
alias_method :set_attribute, :[]=
|
915
|
+
alias_method :has_attribute?, :key?
|
776
916
|
|
777
|
-
#
|
917
|
+
# :section:
|
778
918
|
|
779
919
|
###
|
780
920
|
# Returns true if this Node matches +selector+
|
@@ -786,8 +926,7 @@ module Nokogiri
|
|
786
926
|
# Create a DocumentFragment containing +tags+ that is relative to _this_
|
787
927
|
# context node.
|
788
928
|
def fragment(tags)
|
789
|
-
|
790
|
-
type::DocumentFragment.new(document, tags, self)
|
929
|
+
document.related_class("DocumentFragment").new(document, tags, self)
|
791
930
|
end
|
792
931
|
|
793
932
|
###
|
@@ -805,19 +944,18 @@ module Nokogiri
|
|
805
944
|
end
|
806
945
|
|
807
946
|
options ||= (document.html? ? ParseOptions::DEFAULT_HTML : ParseOptions::DEFAULT_XML)
|
808
|
-
if Integer === options
|
809
|
-
options = Nokogiri::XML::ParseOptions.new(options)
|
810
|
-
end
|
811
|
-
# Give the options to the user
|
947
|
+
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
812
948
|
yield options if block_given?
|
813
949
|
|
814
|
-
contents = string_or_io.respond_to?(:read)
|
815
|
-
string_or_io.read
|
950
|
+
contents = if string_or_io.respond_to?(:read)
|
951
|
+
string_or_io.read
|
952
|
+
else
|
816
953
|
string_or_io
|
954
|
+
end
|
817
955
|
|
818
956
|
return Nokogiri::XML::NodeSet.new(document) if contents.empty?
|
819
957
|
|
820
|
-
# libxml2 does not obey the
|
958
|
+
# libxml2 does not obey the +recover+ option after encountering errors during +in_context+
|
821
959
|
# parsing, and so this horrible hack is here to try to emulate recovery behavior.
|
822
960
|
#
|
823
961
|
# Unfortunately, this means we're no longer parsing "in context" and so namespaces that
|
@@ -827,16 +965,16 @@ module Nokogiri
|
|
827
965
|
#
|
828
966
|
# I think preferable behavior would be to either:
|
829
967
|
#
|
830
|
-
# a. add an error noting that we "fell back" and pointing the user to turning off the
|
968
|
+
# a. add an error noting that we "fell back" and pointing the user to turning off the +recover+ option
|
831
969
|
# b. don't recover, but raise a sensible exception
|
832
970
|
#
|
833
971
|
# For context and background: https://github.com/sparklemotion/nokogiri/issues/313
|
834
972
|
# FIXME bug report: https://github.com/sparklemotion/nokogiri/issues/2092
|
835
973
|
error_count = document.errors.length
|
836
974
|
node_set = in_context(contents, options.to_i)
|
837
|
-
if
|
975
|
+
if node_set.empty? && (document.errors.length > error_count)
|
838
976
|
if options.recover?
|
839
|
-
fragment =
|
977
|
+
fragment = document.related_class("DocumentFragment").parse(contents)
|
840
978
|
node_set = fragment.children
|
841
979
|
else
|
842
980
|
raise document.errors[error_count]
|
@@ -845,20 +983,42 @@ module Nokogiri
|
|
845
983
|
node_set
|
846
984
|
end
|
847
985
|
|
848
|
-
|
849
|
-
#
|
850
|
-
#
|
851
|
-
#
|
852
|
-
#
|
853
|
-
#
|
854
|
-
#
|
855
|
-
#
|
856
|
-
#
|
857
|
-
#
|
858
|
-
#
|
859
|
-
#
|
860
|
-
#
|
861
|
-
#
|
986
|
+
# :call-seq:
|
987
|
+
# namespaces() → Hash<String(Namespace#prefix) ⇒ String(Namespace#href)>
|
988
|
+
#
|
989
|
+
# Fetch all the namespaces on this node and its ancestors.
|
990
|
+
#
|
991
|
+
# Note that the keys in this hash XML attributes that would be used to define this namespace,
|
992
|
+
# such as "xmlns:prefix", not just the prefix.
|
993
|
+
#
|
994
|
+
# The default namespace for this node will be included with key "xmlns".
|
995
|
+
#
|
996
|
+
# See also #namespace_scopes
|
997
|
+
#
|
998
|
+
# [Returns]
|
999
|
+
# Hash containing all the namespaces on this node and its ancestors. The hash keys are the
|
1000
|
+
# namespace prefix, and the hash value for each key is the namespace URI.
|
1001
|
+
#
|
1002
|
+
# *Example:*
|
1003
|
+
#
|
1004
|
+
# doc = Nokogiri::XML(<<~EOF)
|
1005
|
+
# <root xmlns="http://example.com/root" xmlns:in_scope="http://example.com/in_scope">
|
1006
|
+
# <first/>
|
1007
|
+
# <second xmlns="http://example.com/child"/>
|
1008
|
+
# <third xmlns:foo="http://example.com/foo"/>
|
1009
|
+
# </root>
|
1010
|
+
# EOF
|
1011
|
+
# doc.at_xpath("//root:first", "root" => "http://example.com/root").namespaces
|
1012
|
+
# # => {"xmlns"=>"http://example.com/root",
|
1013
|
+
# # "xmlns:in_scope"=>"http://example.com/in_scope"}
|
1014
|
+
# doc.at_xpath("//child:second", "child" => "http://example.com/child").namespaces
|
1015
|
+
# # => {"xmlns"=>"http://example.com/child",
|
1016
|
+
# # "xmlns:in_scope"=>"http://example.com/in_scope"}
|
1017
|
+
# doc.at_xpath("//root:third", "root" => "http://example.com/root").namespaces
|
1018
|
+
# # => {"xmlns:foo"=>"http://example.com/foo",
|
1019
|
+
# # "xmlns"=>"http://example.com/root",
|
1020
|
+
# # "xmlns:in_scope"=>"http://example.com/in_scope"}
|
1021
|
+
#
|
862
1022
|
def namespaces
|
863
1023
|
namespace_scopes.each_with_object({}) do |ns, hash|
|
864
1024
|
prefix = ns.prefix
|
@@ -882,14 +1042,14 @@ module Nokogiri
|
|
882
1042
|
type == DOCUMENT_NODE
|
883
1043
|
end
|
884
1044
|
|
885
|
-
# Returns true if this is an HTML4::Document node
|
1045
|
+
# Returns true if this is an HTML4::Document or HTML5::Document node
|
886
1046
|
def html?
|
887
1047
|
type == HTML_DOCUMENT_NODE
|
888
1048
|
end
|
889
1049
|
|
890
1050
|
# Returns true if this is a Document
|
891
1051
|
def document?
|
892
|
-
is_a?
|
1052
|
+
is_a?(XML::Document)
|
893
1053
|
end
|
894
1054
|
|
895
1055
|
# Returns true if this is a ProcessingInstruction node
|
@@ -927,7 +1087,7 @@ module Nokogiri
|
|
927
1087
|
type == ELEMENT_NODE
|
928
1088
|
end
|
929
1089
|
|
930
|
-
|
1090
|
+
alias_method :elem?, :element?
|
931
1091
|
|
932
1092
|
###
|
933
1093
|
# Turn this node in to a string. If the document is HTML, this method
|
@@ -943,9 +1103,9 @@ module Nokogiri
|
|
943
1103
|
|
944
1104
|
# Get the path to this node as a CSS expression
|
945
1105
|
def css_path
|
946
|
-
path.split(
|
947
|
-
part.
|
948
|
-
|
1106
|
+
path.split(%r{/}).map do |part|
|
1107
|
+
part.empty? ? nil : part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
|
1108
|
+
end.compact.join(" > ")
|
949
1109
|
end
|
950
1110
|
|
951
1111
|
###
|
@@ -958,7 +1118,7 @@ module Nokogiri
|
|
958
1118
|
parents = [parent]
|
959
1119
|
|
960
1120
|
while parents.last.respond_to?(:parent)
|
961
|
-
break unless ctx_parent = parents.last.parent
|
1121
|
+
break unless (ctx_parent = parents.last.parent)
|
962
1122
|
parents << ctx_parent
|
963
1123
|
end
|
964
1124
|
|
@@ -967,16 +1127,16 @@ module Nokogiri
|
|
967
1127
|
root = parents.last
|
968
1128
|
search_results = root.search(selector)
|
969
1129
|
|
970
|
-
NodeSet.new(document, parents.find_all
|
1130
|
+
NodeSet.new(document, parents.find_all do |parent|
|
971
1131
|
search_results.include?(parent)
|
972
|
-
|
1132
|
+
end)
|
973
1133
|
end
|
974
1134
|
|
975
1135
|
####
|
976
1136
|
# Yields self and all children to +block+ recursively.
|
977
1137
|
def traverse(&block)
|
978
1138
|
children.each { |j| j.traverse(&block) }
|
979
|
-
|
1139
|
+
yield(self)
|
980
1140
|
end
|
981
1141
|
|
982
1142
|
###
|
@@ -999,10 +1159,10 @@ module Nokogiri
|
|
999
1159
|
def <=>(other)
|
1000
1160
|
return nil unless other.is_a?(Nokogiri::XML::Node)
|
1001
1161
|
return nil unless document == other.document
|
1002
|
-
compare
|
1162
|
+
compare(other)
|
1003
1163
|
end
|
1004
1164
|
|
1005
|
-
#
|
1165
|
+
# :section: Serialization and Generating Output
|
1006
1166
|
|
1007
1167
|
###
|
1008
1168
|
# Serialize Node using +options+. Save options can also be set using a
|
@@ -1019,18 +1179,22 @@ module Nokogiri
|
|
1019
1179
|
# end
|
1020
1180
|
#
|
1021
1181
|
def serialize(*args, &block)
|
1022
|
-
options = args.first.is_a?(Hash)
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1182
|
+
options = if args.first.is_a?(Hash)
|
1183
|
+
args.shift
|
1184
|
+
else
|
1185
|
+
{
|
1186
|
+
encoding: args[0],
|
1187
|
+
save_with: args[1],
|
1188
|
+
}
|
1189
|
+
end
|
1026
1190
|
|
1027
1191
|
encoding = options[:encoding] || document.encoding
|
1028
1192
|
options[:encoding] = encoding
|
1029
1193
|
|
1030
|
-
outstring =
|
1194
|
+
outstring = +""
|
1031
1195
|
outstring.force_encoding(Encoding.find(encoding || "utf-8"))
|
1032
1196
|
io = StringIO.new(outstring)
|
1033
|
-
write_to
|
1197
|
+
write_to(io, options, &block)
|
1034
1198
|
io.string
|
1035
1199
|
end
|
1036
1200
|
|
@@ -1042,7 +1206,7 @@ module Nokogiri
|
|
1042
1206
|
# See Node#write_to for a list of +options+. For formatted output,
|
1043
1207
|
# use Node#to_xhtml instead.
|
1044
1208
|
def to_html(options = {})
|
1045
|
-
to_format
|
1209
|
+
to_format(SaveOptions::DEFAULT_HTML, options)
|
1046
1210
|
end
|
1047
1211
|
|
1048
1212
|
###
|
@@ -1063,7 +1227,7 @@ module Nokogiri
|
|
1063
1227
|
#
|
1064
1228
|
# See Node#write_to for a list of +options+
|
1065
1229
|
def to_xhtml(options = {})
|
1066
|
-
to_format
|
1230
|
+
to_format(SaveOptions::DEFAULT_XHTML, options)
|
1067
1231
|
end
|
1068
1232
|
|
1069
1233
|
###
|
@@ -1111,7 +1275,7 @@ module Nokogiri
|
|
1111
1275
|
#
|
1112
1276
|
# See Node#write_to for a list of +options+
|
1113
1277
|
def write_html_to(io, options = {})
|
1114
|
-
write_format_to
|
1278
|
+
write_format_to(SaveOptions::DEFAULT_HTML, io, options)
|
1115
1279
|
end
|
1116
1280
|
|
1117
1281
|
###
|
@@ -1119,7 +1283,7 @@ module Nokogiri
|
|
1119
1283
|
#
|
1120
1284
|
# See Node#write_to for a list of +options+
|
1121
1285
|
def write_xhtml_to(io, options = {})
|
1122
|
-
write_format_to
|
1286
|
+
write_format_to(SaveOptions::DEFAULT_XHTML, io, options)
|
1123
1287
|
end
|
1124
1288
|
|
1125
1289
|
###
|
@@ -1130,7 +1294,7 @@ module Nokogiri
|
|
1130
1294
|
# See Node#write_to for a list of options
|
1131
1295
|
def write_xml_to(io, options = {})
|
1132
1296
|
options[:save_with] ||= SaveOptions::DEFAULT_XML
|
1133
|
-
write_to
|
1297
|
+
write_to(io, options)
|
1134
1298
|
end
|
1135
1299
|
|
1136
1300
|
def canonicalize(mode = XML::XML_C14N_1_0, inclusive_namespaces = nil, with_comments = false)
|
@@ -1141,7 +1305,7 @@ module Nokogiri
|
|
1141
1305
|
end
|
1142
1306
|
end
|
1143
1307
|
|
1144
|
-
#
|
1308
|
+
# :section:
|
1145
1309
|
|
1146
1310
|
protected
|
1147
1311
|
|
@@ -1159,9 +1323,9 @@ module Nokogiri
|
|
1159
1323
|
return data
|
1160
1324
|
end
|
1161
1325
|
|
1162
|
-
raise ArgumentError,
|
1163
|
-
Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
1164
|
-
(You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
|
1326
|
+
raise ArgumentError, <<~EOERR
|
1327
|
+
Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
1328
|
+
(You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
|
1165
1329
|
EOERR
|
1166
1330
|
end
|
1167
1331
|
|
@@ -1170,32 +1334,33 @@ Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
|
1170
1334
|
def keywordify(keywords)
|
1171
1335
|
case keywords
|
1172
1336
|
when Enumerable
|
1173
|
-
|
1337
|
+
keywords
|
1174
1338
|
when String
|
1175
|
-
|
1339
|
+
keywords.scan(/\S+/)
|
1176
1340
|
else
|
1177
|
-
raise ArgumentError
|
1341
|
+
raise ArgumentError,
|
1342
|
+
"Keyword attributes must be passed as either a String or an Enumerable, but received #{keywords.class}"
|
1178
1343
|
end
|
1179
1344
|
end
|
1180
1345
|
|
1181
1346
|
def add_sibling(next_or_previous, node_or_tags)
|
1182
1347
|
raise("Cannot add sibling to a node with no parent") unless parent
|
1183
1348
|
|
1184
|
-
impl =
|
1185
|
-
iter =
|
1349
|
+
impl = next_or_previous == :next ? :add_next_sibling_node : :add_previous_sibling_node
|
1350
|
+
iter = next_or_previous == :next ? :reverse_each : :each
|
1186
1351
|
|
1187
1352
|
node_or_tags = parent.coerce(node_or_tags)
|
1188
1353
|
if node_or_tags.is_a?(XML::NodeSet)
|
1189
1354
|
if text?
|
1190
|
-
pivot = Nokogiri::XML::Node.new
|
1191
|
-
send
|
1355
|
+
pivot = Nokogiri::XML::Node.new("dummy", document)
|
1356
|
+
send(impl, pivot)
|
1192
1357
|
else
|
1193
1358
|
pivot = self
|
1194
1359
|
end
|
1195
|
-
node_or_tags.send(iter) { |n| pivot.send
|
1360
|
+
node_or_tags.send(iter) { |n| pivot.send(impl, n) }
|
1196
1361
|
pivot.unlink if text?
|
1197
1362
|
else
|
1198
|
-
send
|
1363
|
+
send(impl, node_or_tags)
|
1199
1364
|
end
|
1200
1365
|
node_or_tags
|
1201
1366
|
end
|
@@ -1214,19 +1379,18 @@ Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
|
1214
1379
|
return (io << dump_html) if USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
1215
1380
|
|
1216
1381
|
options[:save_with] ||= save_option
|
1217
|
-
write_to
|
1382
|
+
write_to(io, options)
|
1218
1383
|
end
|
1219
1384
|
|
1220
1385
|
def inspect_attributes
|
1221
1386
|
[:name, :namespace, :attribute_nodes, :children]
|
1222
1387
|
end
|
1223
1388
|
|
1224
|
-
|
1225
|
-
IMPLIED_XPATH_CONTEXTS = [".//".freeze].freeze
|
1389
|
+
IMPLIED_XPATH_CONTEXTS = [".//"].freeze
|
1226
1390
|
|
1227
1391
|
def add_child_node_and_reparent_attrs(node)
|
1228
|
-
add_child_node
|
1229
|
-
node.attribute_nodes.find_all { |a| a.name
|
1392
|
+
add_child_node(node)
|
1393
|
+
node.attribute_nodes.find_all { |a| a.name.include?(":") }.each do |attr_node|
|
1230
1394
|
attr_node.remove
|
1231
1395
|
node[attr_node.name] = attr_node.value
|
1232
1396
|
end
|