nokogiri 1.12.5 → 1.14.3
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 +41 -0
- data/LICENSE-DEPENDENCIES.md +830 -509
- data/LICENSE.md +1 -1
- data/README.md +23 -14
- data/bin/nokogiri +63 -50
- data/dependencies.yml +33 -66
- data/ext/nokogiri/extconf.rb +159 -63
- data/ext/nokogiri/gumbo.c +21 -11
- data/ext/nokogiri/html4_document.c +2 -2
- data/ext/nokogiri/html4_element_description.c +1 -1
- data/ext/nokogiri/html4_entity_lookup.c +2 -2
- data/ext/nokogiri/html4_sax_parser_context.c +3 -9
- data/ext/nokogiri/html4_sax_push_parser.c +1 -1
- data/ext/nokogiri/nokogiri.c +38 -51
- data/ext/nokogiri/nokogiri.h +26 -14
- data/ext/nokogiri/test_global_handlers.c +1 -1
- data/ext/nokogiri/xml_attr.c +3 -3
- data/ext/nokogiri/xml_attribute_decl.c +5 -5
- data/ext/nokogiri/xml_cdata.c +3 -3
- data/ext/nokogiri/xml_comment.c +1 -1
- data/ext/nokogiri/xml_document.c +53 -44
- data/ext/nokogiri/xml_document_fragment.c +1 -3
- data/ext/nokogiri/xml_dtd.c +11 -11
- data/ext/nokogiri/xml_element_content.c +3 -3
- data/ext/nokogiri/xml_element_decl.c +5 -5
- data/ext/nokogiri/xml_encoding_handler.c +28 -14
- data/ext/nokogiri/xml_entity_decl.c +6 -6
- data/ext/nokogiri/xml_entity_reference.c +1 -1
- data/ext/nokogiri/xml_namespace.c +80 -14
- data/ext/nokogiri/xml_node.c +982 -396
- data/ext/nokogiri/xml_node_set.c +4 -6
- data/ext/nokogiri/xml_processing_instruction.c +1 -1
- data/ext/nokogiri/xml_reader.c +133 -32
- data/ext/nokogiri/xml_relax_ng.c +1 -3
- data/ext/nokogiri/xml_sax_parser.c +23 -17
- data/ext/nokogiri/xml_sax_parser_context.c +11 -9
- data/ext/nokogiri/xml_sax_push_parser.c +1 -3
- data/ext/nokogiri/xml_schema.c +4 -6
- data/ext/nokogiri/xml_syntax_error.c +1 -1
- data/ext/nokogiri/xml_text.c +2 -2
- data/ext/nokogiri/xml_xpath_context.c +144 -114
- data/ext/nokogiri/xslt_stylesheet.c +122 -23
- data/gumbo-parser/Makefile +10 -0
- data/gumbo-parser/src/attribute.h +1 -1
- data/gumbo-parser/src/error.c +2 -2
- data/gumbo-parser/src/error.h +1 -1
- data/gumbo-parser/src/foreign_attrs.c +2 -2
- data/gumbo-parser/src/{gumbo.h → nokogiri_gumbo.h} +1 -0
- data/gumbo-parser/src/parser.c +8 -16
- data/gumbo-parser/src/replacement.h +1 -1
- data/gumbo-parser/src/string_buffer.h +1 -1
- data/gumbo-parser/src/string_piece.c +1 -1
- data/gumbo-parser/src/svg_attrs.c +2 -2
- data/gumbo-parser/src/svg_tags.c +2 -2
- data/gumbo-parser/src/tag.c +2 -1
- data/gumbo-parser/src/tag_lookup.c +7 -7
- data/gumbo-parser/src/tag_lookup.gperf +1 -0
- data/gumbo-parser/src/tag_lookup.h +1 -1
- data/gumbo-parser/src/token_buffer.h +1 -1
- data/gumbo-parser/src/tokenizer.c +1 -1
- data/gumbo-parser/src/tokenizer.h +1 -1
- data/gumbo-parser/src/utf8.c +1 -1
- data/gumbo-parser/src/utf8.h +1 -1
- data/gumbo-parser/src/util.c +1 -3
- data/gumbo-parser/src/util.h +4 -0
- data/gumbo-parser/src/vector.h +1 -1
- data/lib/nokogiri/class_resolver.rb +67 -0
- data/lib/nokogiri/css/node.rb +9 -8
- data/lib/nokogiri/css/parser.rb +360 -341
- data/lib/nokogiri/css/parser.y +249 -244
- data/lib/nokogiri/css/parser_extras.rb +22 -20
- data/lib/nokogiri/css/syntax_error.rb +1 -0
- data/lib/nokogiri/css/tokenizer.rb +4 -3
- data/lib/nokogiri/css/tokenizer.rex +3 -2
- data/lib/nokogiri/css/xpath_visitor.rb +184 -85
- data/lib/nokogiri/css.rb +44 -6
- data/lib/nokogiri/decorators/slop.rb +8 -7
- data/lib/nokogiri/encoding_handler.rb +57 -0
- data/lib/nokogiri/extension.rb +4 -3
- 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 +56 -164
- 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 +432 -532
- data/lib/nokogiri/html4/encoding_reader.rb +121 -0
- data/lib/nokogiri/html4/entity_lookup.rb +2 -1
- data/lib/nokogiri/html4/sax/parser.rb +5 -2
- 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 +12 -5
- data/lib/nokogiri/html5/document.rb +126 -32
- data/lib/nokogiri/html5/document_fragment.rb +14 -4
- data/lib/nokogiri/html5/node.rb +12 -7
- data/lib/nokogiri/html5.rb +138 -222
- data/lib/nokogiri/jruby/dependencies.rb +2 -19
- data/lib/nokogiri/jruby/nokogiri_jars.rb +43 -0
- data/lib/nokogiri/syntax_error.rb +1 -0
- data/lib/nokogiri/version/constant.rb +2 -1
- data/lib/nokogiri/version/info.rb +32 -24
- data/lib/nokogiri/version.rb +1 -0
- data/lib/nokogiri/xml/attr.rb +54 -3
- data/lib/nokogiri/xml/attribute_decl.rb +2 -1
- data/lib/nokogiri/xml/builder.rb +35 -33
- data/lib/nokogiri/xml/cdata.rb +2 -1
- data/lib/nokogiri/xml/character_data.rb +1 -0
- data/lib/nokogiri/xml/document.rb +232 -143
- data/lib/nokogiri/xml/document_fragment.rb +88 -42
- 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 +44 -0
- data/lib/nokogiri/xml/node/save_options.rb +14 -8
- data/lib/nokogiri/xml/node.rb +708 -383
- data/lib/nokogiri/xml/node_set.rb +134 -59
- data/lib/nokogiri/xml/notation.rb +12 -0
- data/lib/nokogiri/xml/parse_options.rb +140 -56
- data/lib/nokogiri/xml/pp/character_data.rb +8 -6
- data/lib/nokogiri/xml/pp/node.rb +26 -26
- data/lib/nokogiri/xml/pp.rb +1 -0
- data/lib/nokogiri/xml/processing_instruction.rb +3 -1
- data/lib/nokogiri/xml/reader.rb +20 -24
- 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 +38 -36
- 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 +93 -62
- data/lib/nokogiri/xml/syntax_error.rb +5 -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 +4 -3
- data/lib/nokogiri/xslt/stylesheet.rb +1 -0
- data/lib/nokogiri/xslt.rb +21 -13
- data/lib/nokogiri.rb +22 -27
- data/lib/xsd/xmlparser/nokogiri.rb +28 -25
- data/patches/libxml2/0009-allow-wildcard-namespaces.patch +77 -0
- data/patches/libxslt/0001-update-automake-files-for-arm64.patch +2445 -1919
- data/ports/archives/libxml2-2.10.4.tar.xz +0 -0
- data/ports/archives/libxslt-1.1.37.tar.xz +0 -0
- metadata +20 -171
- data/patches/libxml2/0004-use-glibc-strlen.patch +0 -53
- data/patches/libxml2/0005-avoid-isnan-isinf.patch +0 -81
- data/patches/libxml2/0006-update-automake-files-for-arm64.patch +0 -2511
- data/patches/libxml2/0007-Fix-XPath-recursion-limit.patch +0 -31
- data/patches/libxslt/0002-Fix-xml2-config-check-in-configure-script.patch +0 -19
- data/ports/archives/libxml2-2.9.12.tar.gz +0 -0
- data/ports/archives/libxslt-1.1.34.tar.gz +0 -0
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
|
26
|
+
#
|
27
|
+
# Nokogiri::XML::Node also has methods that let you move around your tree:
|
21
28
|
#
|
22
|
-
#
|
23
|
-
#
|
29
|
+
# [#parent, #children, #next, #previous]
|
30
|
+
# Navigate up, down, or through siblings.
|
24
31
|
#
|
25
|
-
#
|
26
|
-
# * {#children}
|
27
|
-
# * {#next}
|
28
|
-
# * {#previous}
|
32
|
+
# See the method group entitled Node@Traversing+Document+Structure for the full set of methods.
|
29
33
|
#
|
30
|
-
#
|
31
|
-
# its subtree), there are a few methods you might want to use:
|
34
|
+
# == Serialization
|
32
35
|
#
|
33
|
-
#
|
34
|
-
#
|
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,15 +102,29 @@ 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
|
-
# This is intentionally empty.
|
127
|
+
# This is intentionally empty, and sets the method signature for subclasses.
|
100
128
|
end
|
101
129
|
|
102
130
|
###
|
@@ -105,161 +133,237 @@ 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.
|
123
|
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
|
124
140
|
#
|
125
|
-
#
|
141
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
|
142
|
+
# containing markup.
|
143
|
+
#
|
144
|
+
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
|
145
|
+
# a DocumentFragment, NodeSet, or String).
|
126
146
|
#
|
127
147
|
# Also see related method +<<+.
|
128
148
|
def add_child(node_or_tags)
|
129
149
|
node_or_tags = coerce(node_or_tags)
|
130
150
|
if node_or_tags.is_a?(XML::NodeSet)
|
131
|
-
node_or_tags.each { |n| add_child_node_and_reparent_attrs
|
151
|
+
node_or_tags.each { |n| add_child_node_and_reparent_attrs(n) }
|
132
152
|
else
|
133
|
-
add_child_node_and_reparent_attrs
|
153
|
+
add_child_node_and_reparent_attrs(node_or_tags)
|
134
154
|
end
|
135
155
|
node_or_tags
|
136
156
|
end
|
137
157
|
|
138
158
|
###
|
139
159
|
# Add +node_or_tags+ as the first child of this Node.
|
140
|
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
|
141
160
|
#
|
142
|
-
#
|
161
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
|
162
|
+
# containing markup.
|
163
|
+
#
|
164
|
+
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
|
165
|
+
# a DocumentFragment, NodeSet, or String).
|
143
166
|
#
|
144
167
|
# Also see related method +add_child+.
|
145
168
|
def prepend_child(node_or_tags)
|
146
|
-
if first = children.first
|
169
|
+
if (first = children.first)
|
147
170
|
# Mimic the error add_child would raise.
|
148
|
-
raise
|
171
|
+
raise "Document already has a root node" if document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
172
|
+
|
149
173
|
first.__send__(:add_sibling, :previous, node_or_tags)
|
150
174
|
else
|
151
175
|
add_child(node_or_tags)
|
152
176
|
end
|
153
177
|
end
|
154
178
|
|
155
|
-
|
156
|
-
#
|
179
|
+
# :call-seq:
|
180
|
+
# wrap(markup) -> self
|
181
|
+
# wrap(node) -> self
|
182
|
+
#
|
183
|
+
# Wrap this Node with the node parsed from +markup+ or a dup of the +node+.
|
184
|
+
#
|
185
|
+
# [Parameters]
|
186
|
+
# - *markup* (String)
|
187
|
+
# Markup that is parsed and used as the wrapper. This node's parent, if it exists, is used
|
188
|
+
# as the context node for parsing; otherwise the associated document is used. If the parsed
|
189
|
+
# fragment has multiple roots, the first root node is used as the wrapper.
|
190
|
+
# - *node* (Nokogiri::XML::Node)
|
191
|
+
# An element that is `#dup`ed and used as the wrapper.
|
192
|
+
#
|
193
|
+
# [Returns] +self+, to support chaining.
|
194
|
+
#
|
195
|
+
# Also see NodeSet#wrap
|
196
|
+
#
|
197
|
+
# *Example* with a +String+ argument:
|
198
|
+
#
|
199
|
+
# doc = Nokogiri::HTML5(<<~HTML)
|
200
|
+
# <html><body>
|
201
|
+
# <a>asdf</a>
|
202
|
+
# </body></html>
|
203
|
+
# HTML
|
204
|
+
# doc.at_css("a").wrap("<div></div>")
|
205
|
+
# doc.to_html
|
206
|
+
# # => <html><head></head><body>
|
207
|
+
# # <div><a>asdf</a></div>
|
208
|
+
# # </body></html>
|
209
|
+
#
|
210
|
+
# *Example* with a +Node+ argument:
|
211
|
+
#
|
212
|
+
# doc = Nokogiri::HTML5(<<~HTML)
|
213
|
+
# <html><body>
|
214
|
+
# <a>asdf</a>
|
215
|
+
# </body></html>
|
216
|
+
# HTML
|
217
|
+
# doc.at_css("a").wrap(doc.create_element("div"))
|
218
|
+
# doc.to_html
|
219
|
+
# # <html><head></head><body>
|
220
|
+
# # <div><a>asdf</a></div>
|
221
|
+
# # </body></html>
|
157
222
|
#
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
223
|
+
def wrap(node_or_tags)
|
224
|
+
case node_or_tags
|
225
|
+
when String
|
226
|
+
context_node = parent || document
|
227
|
+
new_parent = context_node.coerce(node_or_tags).first
|
228
|
+
if new_parent.nil?
|
229
|
+
raise "Failed to parse '#{node_or_tags}' in the context of a '#{context_node.name}' element"
|
230
|
+
end
|
231
|
+
when XML::Node
|
232
|
+
new_parent = node_or_tags.dup
|
233
|
+
else
|
234
|
+
raise ArgumentError, "Requires a String or Node argument, and cannot accept a #{node_or_tags.class}"
|
235
|
+
end
|
236
|
+
|
237
|
+
if parent
|
238
|
+
add_next_sibling(new_parent)
|
239
|
+
else
|
240
|
+
new_parent.unlink
|
241
|
+
end
|
162
242
|
new_parent.add_child(self)
|
243
|
+
|
163
244
|
self
|
164
245
|
end
|
165
246
|
|
166
247
|
###
|
167
248
|
# Add +node_or_tags+ as a child of this Node.
|
168
|
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
|
169
249
|
#
|
170
|
-
#
|
250
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
|
251
|
+
# containing markup.
|
252
|
+
#
|
253
|
+
# Returns +self+, to support chaining of calls (e.g., root << child1 << child2)
|
171
254
|
#
|
172
255
|
# Also see related method +add_child+.
|
173
256
|
def <<(node_or_tags)
|
174
|
-
add_child
|
257
|
+
add_child(node_or_tags)
|
175
258
|
self
|
176
259
|
end
|
177
260
|
|
178
261
|
###
|
179
262
|
# Insert +node_or_tags+ before this Node (as a sibling).
|
180
|
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
|
181
263
|
#
|
182
|
-
#
|
264
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
|
265
|
+
# containing markup.
|
266
|
+
#
|
267
|
+
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
|
268
|
+
# a DocumentFragment, NodeSet, or String).
|
183
269
|
#
|
184
270
|
# Also see related method +before+.
|
185
271
|
def add_previous_sibling(node_or_tags)
|
186
|
-
raise ArgumentError
|
272
|
+
raise ArgumentError,
|
273
|
+
"A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
187
274
|
|
188
|
-
add_sibling
|
275
|
+
add_sibling(:previous, node_or_tags)
|
189
276
|
end
|
190
277
|
|
191
278
|
###
|
192
279
|
# Insert +node_or_tags+ after this Node (as a sibling).
|
193
|
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
|
194
280
|
#
|
195
|
-
#
|
281
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
|
282
|
+
# containing markup.
|
283
|
+
#
|
284
|
+
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
|
285
|
+
# a DocumentFragment, NodeSet, or String).
|
196
286
|
#
|
197
287
|
# Also see related method +after+.
|
198
288
|
def add_next_sibling(node_or_tags)
|
199
|
-
raise ArgumentError
|
289
|
+
raise ArgumentError,
|
290
|
+
"A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
200
291
|
|
201
|
-
add_sibling
|
292
|
+
add_sibling(:next, node_or_tags)
|
202
293
|
end
|
203
294
|
|
204
295
|
####
|
205
296
|
# Insert +node_or_tags+ before this node (as a sibling).
|
206
|
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
|
207
297
|
#
|
208
|
-
#
|
298
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
|
299
|
+
# containing markup.
|
300
|
+
#
|
301
|
+
# Returns +self+, to support chaining of calls.
|
209
302
|
#
|
210
303
|
# Also see related method +add_previous_sibling+.
|
211
304
|
def before(node_or_tags)
|
212
|
-
add_previous_sibling
|
305
|
+
add_previous_sibling(node_or_tags)
|
213
306
|
self
|
214
307
|
end
|
215
308
|
|
216
309
|
####
|
217
310
|
# Insert +node_or_tags+ after this node (as a sibling).
|
218
|
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
|
219
311
|
#
|
220
|
-
#
|
312
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a String
|
313
|
+
# containing markup.
|
314
|
+
#
|
315
|
+
# Returns +self+, to support chaining of calls.
|
221
316
|
#
|
222
317
|
# Also see related method +add_next_sibling+.
|
223
318
|
def after(node_or_tags)
|
224
|
-
add_next_sibling
|
319
|
+
add_next_sibling(node_or_tags)
|
225
320
|
self
|
226
321
|
end
|
227
322
|
|
228
323
|
####
|
229
|
-
# Set the
|
230
|
-
#
|
324
|
+
# Set the content for this Node to +node_or_tags+.
|
325
|
+
#
|
326
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a String
|
327
|
+
# containing markup.
|
328
|
+
#
|
329
|
+
# ⚠ Please note that despite the name, this method will *not* always parse a String argument
|
330
|
+
# as HTML. A String argument will be parsed with the +DocumentFragment+ parser related to this
|
331
|
+
# node's document.
|
231
332
|
#
|
232
|
-
#
|
333
|
+
# For example, if the document is an HTML4::Document then the string will be parsed as HTML4
|
334
|
+
# using HTML4::DocumentFragment; but if the document is an XML::Document then it will
|
335
|
+
# parse the string as XML using XML::DocumentFragment.
|
233
336
|
#
|
234
337
|
# Also see related method +children=+
|
235
338
|
def inner_html=(node_or_tags)
|
236
339
|
self.children = node_or_tags
|
237
|
-
self
|
238
340
|
end
|
239
341
|
|
240
342
|
####
|
241
|
-
# Set the
|
242
|
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
|
343
|
+
# Set the content for this Node +node_or_tags+
|
243
344
|
#
|
244
|
-
#
|
345
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a String
|
346
|
+
# containing markup.
|
245
347
|
#
|
246
348
|
# Also see related method +inner_html=+
|
247
349
|
def children=(node_or_tags)
|
248
350
|
node_or_tags = coerce(node_or_tags)
|
249
351
|
children.unlink
|
250
352
|
if node_or_tags.is_a?(XML::NodeSet)
|
251
|
-
node_or_tags.each { |n| add_child_node_and_reparent_attrs
|
353
|
+
node_or_tags.each { |n| add_child_node_and_reparent_attrs(n) }
|
252
354
|
else
|
253
|
-
add_child_node_and_reparent_attrs
|
355
|
+
add_child_node_and_reparent_attrs(node_or_tags)
|
254
356
|
end
|
255
|
-
node_or_tags
|
256
357
|
end
|
257
358
|
|
258
359
|
####
|
259
360
|
# Replace this Node with +node_or_tags+.
|
260
|
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
|
261
361
|
#
|
262
|
-
#
|
362
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
|
363
|
+
# containing markup.
|
364
|
+
#
|
365
|
+
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
|
366
|
+
# a DocumentFragment, NodeSet, or String).
|
263
367
|
#
|
264
368
|
# Also see related method +swap+.
|
265
369
|
def replace(node_or_tags)
|
@@ -270,37 +374,40 @@ module Nokogiri
|
|
270
374
|
# libxml is trying to find a parent node that is an element or document
|
271
375
|
# so I can't tell if this is bug in libxml or not. issue #775.
|
272
376
|
if text?
|
273
|
-
replacee = Nokogiri::XML::Node.new
|
274
|
-
add_previous_sibling_node
|
377
|
+
replacee = Nokogiri::XML::Node.new("dummy", document)
|
378
|
+
add_previous_sibling_node(replacee)
|
275
379
|
unlink
|
276
|
-
return replacee.replace
|
380
|
+
return replacee.replace(node_or_tags)
|
277
381
|
end
|
278
382
|
|
279
383
|
node_or_tags = parent.coerce(node_or_tags)
|
280
384
|
|
281
385
|
if node_or_tags.is_a?(XML::NodeSet)
|
282
|
-
node_or_tags.each { |n| add_previous_sibling
|
386
|
+
node_or_tags.each { |n| add_previous_sibling(n) }
|
283
387
|
unlink
|
284
388
|
else
|
285
|
-
replace_node
|
389
|
+
replace_node(node_or_tags)
|
286
390
|
end
|
287
391
|
node_or_tags
|
288
392
|
end
|
289
393
|
|
290
394
|
####
|
291
395
|
# Swap this Node for +node_or_tags+
|
292
|
-
#
|
396
|
+
#
|
397
|
+
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
|
398
|
+
# Containing markup.
|
293
399
|
#
|
294
400
|
# Returns self, to support chaining of calls.
|
295
401
|
#
|
296
402
|
# Also see related method +replace+.
|
297
403
|
def swap(node_or_tags)
|
298
|
-
replace
|
404
|
+
replace(node_or_tags)
|
299
405
|
self
|
300
406
|
end
|
301
407
|
|
302
408
|
####
|
303
|
-
# Set the Node's content to a Text node containing +string+. The string gets XML escaped, not
|
409
|
+
# Set the Node's content to a Text node containing +string+. The string gets XML escaped, not
|
410
|
+
# interpreted as markup.
|
304
411
|
def content=(string)
|
305
412
|
self.native_content = encode_special_chars(string.to_s)
|
306
413
|
end
|
@@ -309,7 +416,6 @@ module Nokogiri
|
|
309
416
|
# Set the parent Node for this Node
|
310
417
|
def parent=(parent_node)
|
311
418
|
parent_node.add_child(self)
|
312
|
-
parent_node
|
313
419
|
end
|
314
420
|
|
315
421
|
###
|
@@ -338,7 +444,7 @@ module Nokogiri
|
|
338
444
|
raise ArgumentError, "namespace must be declared on the same document"
|
339
445
|
end
|
340
446
|
|
341
|
-
set_namespace
|
447
|
+
set_namespace(ns)
|
342
448
|
end
|
343
449
|
|
344
450
|
###
|
@@ -347,52 +453,159 @@ module Nokogiri
|
|
347
453
|
# passed to it, allowing more convenient modification of the parser options.
|
348
454
|
def do_xinclude(options = XML::ParseOptions::DEFAULT_XML)
|
349
455
|
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
350
|
-
|
351
|
-
# give options to user
|
352
456
|
yield options if block_given?
|
353
457
|
|
354
458
|
# call c extension
|
355
459
|
process_xincludes(options.to_i)
|
356
460
|
end
|
357
461
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
462
|
+
alias_method :next, :next_sibling
|
463
|
+
alias_method :previous, :previous_sibling
|
464
|
+
alias_method :next=, :add_next_sibling
|
465
|
+
alias_method :previous=, :add_previous_sibling
|
466
|
+
alias_method :remove, :unlink
|
467
|
+
alias_method :name=, :node_name=
|
468
|
+
alias_method :add_namespace, :add_namespace_definition
|
365
469
|
|
366
|
-
#
|
470
|
+
# :section:
|
367
471
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
472
|
+
alias_method :inner_text, :content
|
473
|
+
alias_method :text, :content
|
474
|
+
alias_method :to_str, :content
|
475
|
+
alias_method :name, :node_name
|
476
|
+
alias_method :type, :node_type
|
477
|
+
alias_method :clone, :dup
|
478
|
+
alias_method :elements, :element_children
|
375
479
|
|
376
|
-
#
|
480
|
+
# :section: Working With Node Attributes
|
377
481
|
|
378
|
-
|
379
|
-
#
|
482
|
+
# :call-seq: [](name) → (String, nil)
|
483
|
+
#
|
484
|
+
# Fetch an attribute from this node.
|
485
|
+
#
|
486
|
+
# ⚠ Note that attributes with namespaces cannot be accessed with this method. To access
|
487
|
+
# namespaced attributes, use #attribute_with_ns.
|
488
|
+
#
|
489
|
+
# [Returns] (String, nil) value of the attribute +name+, or +nil+ if no matching attribute exists
|
490
|
+
#
|
491
|
+
# *Example*
|
492
|
+
#
|
493
|
+
# doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
|
494
|
+
# child = doc.at_css("child")
|
495
|
+
# child["size"] # => "large"
|
496
|
+
# child["class"] # => "big wide tall"
|
497
|
+
#
|
498
|
+
# *Example:* Namespaced attributes will not be returned.
|
499
|
+
#
|
500
|
+
# ⚠ Note namespaced attributes may be accessed with #attribute or #attribute_with_ns
|
501
|
+
#
|
502
|
+
# doc = Nokogiri::XML(<<~EOF)
|
503
|
+
# <root xmlns:width='http://example.com/widths'>
|
504
|
+
# <child width:size='broad'/>
|
505
|
+
# </root>
|
506
|
+
# EOF
|
507
|
+
# doc.at_css("child")["size"] # => nil
|
508
|
+
# doc.at_css("child").attribute("size").value # => "broad"
|
509
|
+
# doc.at_css("child").attribute_with_ns("size", "http://example.com/widths").value
|
510
|
+
# # => "broad"
|
511
|
+
#
|
380
512
|
def [](name)
|
381
513
|
get(name.to_s)
|
382
514
|
end
|
383
515
|
|
384
|
-
|
385
|
-
#
|
516
|
+
# :call-seq: []=(name, value) → value
|
517
|
+
#
|
518
|
+
# Update the attribute +name+ to +value+, or create the attribute if it does not exist.
|
519
|
+
#
|
520
|
+
# ⚠ Note that attributes with namespaces cannot be accessed with this method. To access
|
521
|
+
# namespaced attributes for update, use #attribute_with_ns. To add a namespaced attribute,
|
522
|
+
# see the example below.
|
523
|
+
#
|
524
|
+
# [Returns] +value+
|
525
|
+
#
|
526
|
+
# *Example*
|
527
|
+
#
|
528
|
+
# doc = Nokogiri::XML("<root><child/></root>")
|
529
|
+
# child = doc.at_css("child")
|
530
|
+
# child["size"] = "broad"
|
531
|
+
# child.to_html
|
532
|
+
# # => "<child size=\"broad\"></child>"
|
533
|
+
#
|
534
|
+
# *Example:* Add a namespaced attribute.
|
535
|
+
#
|
536
|
+
# doc = Nokogiri::XML(<<~EOF)
|
537
|
+
# <root xmlns:width='http://example.com/widths'>
|
538
|
+
# <child/>
|
539
|
+
# </root>
|
540
|
+
# EOF
|
541
|
+
# child = doc.at_css("child")
|
542
|
+
# child["size"] = "broad"
|
543
|
+
# ns = doc.root.namespace_definitions.find { |ns| ns.prefix == "width" }
|
544
|
+
# child.attribute("size").namespace = ns
|
545
|
+
# doc.to_html
|
546
|
+
# # => "<root xmlns:width=\"http://example.com/widths\">\n" +
|
547
|
+
# # " <child width:size=\"broad\"></child>\n" +
|
548
|
+
# # "</root>\n"
|
549
|
+
#
|
386
550
|
def []=(name, value)
|
387
|
-
set
|
551
|
+
set(name.to_s, value.to_s)
|
388
552
|
end
|
389
553
|
|
390
|
-
|
391
|
-
#
|
392
|
-
#
|
393
|
-
#
|
394
|
-
#
|
395
|
-
#
|
554
|
+
#
|
555
|
+
# :call-seq: attributes() → Hash<String ⇒ Nokogiri::XML::Attr>
|
556
|
+
#
|
557
|
+
# Fetch this node's attributes.
|
558
|
+
#
|
559
|
+
# ⚠ Because the keys do not include any namespace information for the attribute, in case of a
|
560
|
+
# simple name collision, not all attributes will be returned. In this case, you will need to
|
561
|
+
# use #attribute_nodes.
|
562
|
+
#
|
563
|
+
# [Returns]
|
564
|
+
# Hash containing attributes belonging to +self+. The hash keys are String attribute
|
565
|
+
# names (without the namespace), and the hash values are Nokogiri::XML::Attr.
|
566
|
+
#
|
567
|
+
# *Example* with no namespaces:
|
568
|
+
#
|
569
|
+
# doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
|
570
|
+
# doc.at_css("child").attributes
|
571
|
+
# # => {"size"=>#(Attr:0x550 { name = "size", value = "large" }),
|
572
|
+
# # "class"=>#(Attr:0x564 { name = "class", value = "big wide tall" })}
|
573
|
+
#
|
574
|
+
# *Example* with a namespace:
|
575
|
+
#
|
576
|
+
# doc = Nokogiri::XML("<root xmlns:desc='http://example.com/sizes'><child desc:size='large'/></root>")
|
577
|
+
# doc.at_css("child").attributes
|
578
|
+
# # => {"size"=>
|
579
|
+
# # #(Attr:0x550 {
|
580
|
+
# # name = "size",
|
581
|
+
# # namespace = #(Namespace:0x564 {
|
582
|
+
# # prefix = "desc",
|
583
|
+
# # href = "http://example.com/sizes"
|
584
|
+
# # }),
|
585
|
+
# # value = "large"
|
586
|
+
# # })}
|
587
|
+
#
|
588
|
+
# *Example* with an attribute name collision:
|
589
|
+
#
|
590
|
+
# ⚠ Note that only one of the attributes is returned in the Hash.
|
591
|
+
#
|
592
|
+
# doc = Nokogiri::XML(<<~EOF)
|
593
|
+
# <root xmlns:width='http://example.com/widths'
|
594
|
+
# xmlns:height='http://example.com/heights'>
|
595
|
+
# <child width:size='broad' height:size='tall'/>
|
596
|
+
# </root>
|
597
|
+
# EOF
|
598
|
+
# doc.at_css("child").attributes
|
599
|
+
# # => {"size"=>
|
600
|
+
# # #(Attr:0x550 {
|
601
|
+
# # name = "size",
|
602
|
+
# # namespace = #(Namespace:0x564 {
|
603
|
+
# # prefix = "height",
|
604
|
+
# # href = "http://example.com/heights"
|
605
|
+
# # }),
|
606
|
+
# # value = "tall"
|
607
|
+
# # })}
|
608
|
+
#
|
396
609
|
def attributes
|
397
610
|
attribute_nodes.each_with_object({}) do |node, hash|
|
398
611
|
hash[node.node_name] = node
|
@@ -408,7 +621,7 @@ module Nokogiri
|
|
408
621
|
###
|
409
622
|
# Does this Node's attributes include <value>
|
410
623
|
def value?(value)
|
411
|
-
values.include?
|
624
|
+
values.include?(value)
|
412
625
|
end
|
413
626
|
|
414
627
|
###
|
@@ -420,36 +633,36 @@ module Nokogiri
|
|
420
633
|
###
|
421
634
|
# Iterate over each attribute name and value pair for this Node.
|
422
635
|
def each
|
423
|
-
attribute_nodes.each
|
636
|
+
attribute_nodes.each do |node|
|
424
637
|
yield [node.node_name, node.value]
|
425
|
-
|
638
|
+
end
|
426
639
|
end
|
427
640
|
|
428
641
|
###
|
429
642
|
# Remove the attribute named +name+
|
430
643
|
def remove_attribute(name)
|
431
|
-
attr = attributes[name].remove if key?
|
644
|
+
attr = attributes[name].remove if key?(name)
|
432
645
|
clear_xpath_context if Nokogiri.jruby?
|
433
646
|
attr
|
434
647
|
end
|
435
648
|
|
436
|
-
#
|
649
|
+
#
|
650
|
+
# :call-seq: classes() → Array<String>
|
651
|
+
#
|
652
|
+
# Fetch CSS class names of a Node.
|
437
653
|
#
|
438
654
|
# This is a convenience function and is equivalent to:
|
655
|
+
#
|
439
656
|
# node.kwattr_values("class")
|
440
657
|
#
|
441
|
-
#
|
442
|
-
# @see #add_class
|
443
|
-
# @see #append_class
|
444
|
-
# @see #remove_class
|
658
|
+
# See related: #kwattr_values, #add_class, #append_class, #remove_class
|
445
659
|
#
|
446
|
-
#
|
660
|
+
# [Returns]
|
661
|
+
# The CSS classes (Array of String) present in the Node's "class" attribute. If the
|
662
|
+
# attribute is empty or non-existent, the return value is an empty array.
|
447
663
|
#
|
448
|
-
#
|
449
|
-
# the attribute is empty or non-existent, the return value is
|
450
|
-
# an empty array.
|
664
|
+
# *Example*
|
451
665
|
#
|
452
|
-
# @example
|
453
666
|
# node # => <div class="section title header"></div>
|
454
667
|
# node.classes # => ["section", "title", "header"]
|
455
668
|
#
|
@@ -457,42 +670,45 @@ module Nokogiri
|
|
457
670
|
kwattr_values("class")
|
458
671
|
end
|
459
672
|
|
460
|
-
#
|
461
|
-
#
|
462
|
-
#
|
463
|
-
#
|
464
|
-
#
|
673
|
+
#
|
674
|
+
# :call-seq: add_class(names) → self
|
675
|
+
#
|
676
|
+
# Ensure HTML CSS classes are present on +self+. Any CSS classes in +names+ that already exist
|
677
|
+
# in the "class" attribute are _not_ added. Note that any existing duplicates in the
|
678
|
+
# "class" attribute are not removed. Compare with #append_class.
|
465
679
|
#
|
466
680
|
# This is a convenience function and is equivalent to:
|
681
|
+
#
|
467
682
|
# node.kwattr_add("class", names)
|
468
683
|
#
|
469
|
-
#
|
470
|
-
# @see #classes
|
471
|
-
# @see #append_class
|
472
|
-
# @see #remove_class
|
684
|
+
# See related: #kwattr_add, #classes, #append_class, #remove_class
|
473
685
|
#
|
474
|
-
#
|
686
|
+
# [Parameters]
|
687
|
+
# - +names+ (String, Array<String>)
|
475
688
|
#
|
476
|
-
# CSS class names to be added to the Node's
|
477
|
-
#
|
478
|
-
#
|
479
|
-
#
|
480
|
-
# be added. If no +class+ attribute exists, one is created.
|
689
|
+
# CSS class names to be added to the Node's "class" attribute. May be a string containing
|
690
|
+
# whitespace-delimited names, or an Array of String names. Any class names already present
|
691
|
+
# will not be added. Any class names not present will be added. If no "class" attribute
|
692
|
+
# exists, one is created.
|
481
693
|
#
|
482
|
-
#
|
694
|
+
# [Returns] +self+ (Node) for ease of chaining method calls.
|
695
|
+
#
|
696
|
+
# *Example:* Ensure that the node has CSS class "section"
|
483
697
|
#
|
484
|
-
# @example Ensure that a +Node+ has CSS class "section"
|
485
698
|
# node # => <div></div>
|
486
699
|
# node.add_class("section") # => <div class="section"></div>
|
487
700
|
# node.add_class("section") # => <div class="section"></div> # duplicate not added
|
488
701
|
#
|
489
|
-
#
|
702
|
+
# *Example:* Ensure that the node has CSS classes "section" and "header", via a String argument
|
703
|
+
#
|
704
|
+
# Note that the CSS class "section" is not added because it is already present.
|
705
|
+
# Note also that the pre-existing duplicate CSS class "section" is not removed.
|
706
|
+
#
|
490
707
|
# node # => <div class="section section"></div>
|
491
708
|
# 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
709
|
#
|
495
|
-
#
|
710
|
+
# *Example:* Ensure that the node has CSS classes "section" and "header", via an Array argument
|
711
|
+
#
|
496
712
|
# node # => <div></div>
|
497
713
|
# node.add_class(["section", "header"]) # => <div class="section header"></div>
|
498
714
|
#
|
@@ -500,39 +716,42 @@ module Nokogiri
|
|
500
716
|
kwattr_add("class", names)
|
501
717
|
end
|
502
718
|
|
503
|
-
#
|
504
|
-
#
|
719
|
+
#
|
720
|
+
# :call-seq: append_class(names) → self
|
721
|
+
#
|
722
|
+
# Add HTML CSS classes to +self+, regardless of duplication. Compare with #add_class.
|
505
723
|
#
|
506
724
|
# This is a convenience function and is equivalent to:
|
725
|
+
#
|
507
726
|
# node.kwattr_append("class", names)
|
508
727
|
#
|
509
|
-
#
|
510
|
-
#
|
511
|
-
#
|
512
|
-
#
|
728
|
+
# See related: #kwattr_append, #classes, #add_class, #remove_class
|
729
|
+
#
|
730
|
+
# [Parameters]
|
731
|
+
# - +names+ (String, Array<String>)
|
513
732
|
#
|
514
|
-
#
|
733
|
+
# CSS class names to be appended to the Node's "class" attribute. May be a string containing
|
734
|
+
# whitespace-delimited names, or an Array of String names. All class names passed in will be
|
735
|
+
# appended to the "class" attribute even if they are already present in the attribute
|
736
|
+
# value. If no "class" attribute exists, one is created.
|
515
737
|
#
|
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.
|
738
|
+
# [Returns] +self+ (Node) for ease of chaining method calls.
|
522
739
|
#
|
523
|
-
#
|
740
|
+
# *Example:* Append "section" to the node's CSS "class" attribute
|
524
741
|
#
|
525
|
-
# @example Append "section" to a +Node+'s CSS +class+ attriubute
|
526
742
|
# node # => <div></div>
|
527
743
|
# node.append_class("section") # => <div class="section"></div>
|
528
744
|
# node.append_class("section") # => <div class="section section"></div> # duplicate added!
|
529
745
|
#
|
530
|
-
#
|
746
|
+
# *Example:* Append "section" and "header" to the noded's CSS "class" attribute, via a String argument
|
747
|
+
#
|
748
|
+
# Note that the CSS class "section" is appended even though it is already present
|
749
|
+
#
|
531
750
|
# node # => <div class="section section"></div>
|
532
751
|
# 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
752
|
#
|
535
|
-
#
|
753
|
+
# *Example:* Append "section" and "header" to the node's CSS "class" attribute, via an Array argument
|
754
|
+
#
|
536
755
|
# node # => <div></div>
|
537
756
|
# node.append_class(["section", "header"]) # => <div class="section header"></div>
|
538
757
|
# node.append_class(["section", "header"]) # => <div class="section header section header"></div>
|
@@ -541,118 +760,135 @@ module Nokogiri
|
|
541
760
|
kwattr_append("class", names)
|
542
761
|
end
|
543
762
|
|
544
|
-
#
|
545
|
-
#
|
546
|
-
#
|
763
|
+
# :call-seq:
|
764
|
+
# remove_class(css_classes) → self
|
765
|
+
#
|
766
|
+
# Remove HTML CSS classes from this node. Any CSS class names in +css_classes+ that exist in
|
767
|
+
# this node's "class" attribute are removed, including any multiple entries.
|
547
768
|
#
|
548
|
-
# If no CSS classes remain after this operation, or if +
|
549
|
-
#
|
769
|
+
# If no CSS classes remain after this operation, or if +css_classes+ is +nil+, the "class"
|
770
|
+
# attribute is deleted from the node.
|
550
771
|
#
|
551
772
|
# This is a convenience function and is equivalent to:
|
552
|
-
# node.kwattr_remove("class", names)
|
553
773
|
#
|
554
|
-
#
|
555
|
-
#
|
556
|
-
#
|
557
|
-
#
|
774
|
+
# node.kwattr_remove("class", css_classes)
|
775
|
+
#
|
776
|
+
# Also see #kwattr_remove, #classes, #add_class, #append_class
|
777
|
+
#
|
778
|
+
# [Parameters]
|
779
|
+
# - +css_classes+ (String, Array<String>)
|
780
|
+
#
|
781
|
+
# CSS class names to be removed from the Node's
|
782
|
+
# "class" attribute. May be a string containing whitespace-delimited names, or an Array of
|
783
|
+
# String names. Any class names already present will be removed. If no CSS classes remain,
|
784
|
+
# the "class" attribute is deleted.
|
558
785
|
#
|
559
|
-
#
|
786
|
+
# [Returns] +self+ (Nokogiri::XML::Node) for ease of chaining method calls.
|
560
787
|
#
|
561
|
-
#
|
562
|
-
# be a string containing whitespace-delimited names, or an Array of
|
563
|
-
# String names. Any class names already present will be removed. If no
|
564
|
-
# CSS classes remain, the +class+ attribute is deleted.
|
788
|
+
# *Example*: Deleting a CSS class
|
565
789
|
#
|
566
|
-
#
|
790
|
+
# Note that all instances of the class "section" are removed from the "class" attribute.
|
567
791
|
#
|
568
|
-
#
|
569
|
-
# node # => <div class="section header"></div>
|
792
|
+
# node # => <div class="section header section"></div>
|
570
793
|
# node.remove_class("section") # => <div class="header"></div>
|
571
|
-
#
|
794
|
+
#
|
795
|
+
# *Example*: Deleting the only remaining CSS class
|
796
|
+
#
|
797
|
+
# Note that the attribute is removed once there are no remaining classes.
|
798
|
+
#
|
799
|
+
# node # => <div class="section"></div>
|
800
|
+
# node.remove_class("section") # => <div></div>
|
801
|
+
#
|
802
|
+
# *Example*: Deleting multiple CSS classes
|
803
|
+
#
|
804
|
+
# Note that the "class" attribute is deleted once it's empty.
|
805
|
+
#
|
806
|
+
# node # => <div class="section header float"></div>
|
807
|
+
# node.remove_class(["section", "float"]) # => <div class="header"></div>
|
572
808
|
#
|
573
809
|
def remove_class(names = nil)
|
574
810
|
kwattr_remove("class", names)
|
575
811
|
end
|
576
812
|
|
577
|
-
#
|
813
|
+
# :call-seq:
|
814
|
+
# kwattr_values(attribute_name) → Array<String>
|
815
|
+
#
|
816
|
+
# Fetch values from a keyword attribute of a Node.
|
578
817
|
#
|
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).
|
818
|
+
# A "keyword attribute" is a node attribute that contains a set of space-delimited
|
819
|
+
# values. Perhaps the most familiar example of this is the HTML "class" attribute used to
|
820
|
+
# contain CSS classes. But other keyword attributes exist, for instance
|
821
|
+
# {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
|
584
822
|
#
|
585
|
-
#
|
586
|
-
# @see #kwattr_add
|
587
|
-
# @see #kwattr_append
|
588
|
-
# @see #kwattr_remove
|
823
|
+
# See also #classes, #kwattr_add, #kwattr_append, #kwattr_remove
|
589
824
|
#
|
590
|
-
#
|
825
|
+
# [Parameters]
|
826
|
+
# - +attribute_name+ (String) The name of the keyword attribute to be inspected.
|
591
827
|
#
|
592
|
-
#
|
828
|
+
# [Returns]
|
829
|
+
# (Array<String>) The values present in the Node's +attribute_name+ attribute. If the
|
830
|
+
# attribute is empty or non-existent, the return value is an empty array.
|
593
831
|
#
|
594
|
-
#
|
595
|
-
# attribute. If the attribute is empty or non-existent, the
|
596
|
-
# return value is an empty array.
|
832
|
+
# *Example:*
|
597
833
|
#
|
598
|
-
# @example
|
599
834
|
# node # => <a rel="nofollow noopener external">link</a>
|
600
835
|
# node.kwattr_values("rel") # => ["nofollow", "noopener", "external"]
|
601
836
|
#
|
602
|
-
#
|
603
|
-
#
|
837
|
+
# Since v1.11.0
|
604
838
|
def kwattr_values(attribute_name)
|
605
839
|
keywordify(get_attribute(attribute_name) || [])
|
606
840
|
end
|
607
841
|
|
842
|
+
# :call-seq:
|
843
|
+
# kwattr_add(attribute_name, keywords) → self
|
844
|
+
#
|
608
845
|
# Ensure that values are present in a keyword attribute.
|
609
846
|
#
|
610
|
-
# Any values in +keywords+ that already exist in the
|
611
|
-
#
|
612
|
-
#
|
613
|
-
# with {#kwattr_append}.
|
847
|
+
# Any values in +keywords+ that already exist in the Node's attribute values are _not_
|
848
|
+
# added. Note that any existing duplicates in the attribute values are not removed. Compare
|
849
|
+
# with #kwattr_append.
|
614
850
|
#
|
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).
|
851
|
+
# A "keyword attribute" is a node attribute that contains a set of space-delimited
|
852
|
+
# values. Perhaps the most familiar example of this is the HTML "class" attribute used to
|
853
|
+
# contain CSS classes. But other keyword attributes exist, for instance
|
854
|
+
# {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
|
620
855
|
#
|
621
|
-
#
|
622
|
-
# @see #kwattr_values
|
623
|
-
# @see #kwattr_append
|
624
|
-
# @see #kwattr_remove
|
856
|
+
# See also #add_class, #kwattr_values, #kwattr_append, #kwattr_remove
|
625
857
|
#
|
626
|
-
#
|
858
|
+
# [Parameters]
|
859
|
+
# - +attribute_name+ (String) The name of the keyword attribute to be modified.
|
860
|
+
# - +keywords+ (String, Array<String>)
|
861
|
+
# Keywords to be added to the attribute named +attribute_name+. May be a string containing
|
862
|
+
# whitespace-delimited values, or an Array of String values. Any values already present will
|
863
|
+
# not be added. Any values not present will be added. If the named attribute does not exist,
|
864
|
+
# it is created.
|
627
865
|
#
|
628
|
-
#
|
866
|
+
# [Returns] +self+ (Nokogiri::XML::Node) for ease of chaining method calls.
|
629
867
|
#
|
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.
|
868
|
+
# *Example:* Ensure that a +Node+ has "nofollow" in its +rel+ attribute.
|
636
869
|
#
|
637
|
-
#
|
870
|
+
# Note that duplicates are not added.
|
638
871
|
#
|
639
|
-
# @example Ensure that a +Node+ has "nofollow" in its +rel+ attribute.
|
640
872
|
# node # => <a></a>
|
641
873
|
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
|
642
|
-
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
|
874
|
+
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
|
875
|
+
#
|
876
|
+
# *Example:* Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via a
|
877
|
+
# String argument.
|
878
|
+
#
|
879
|
+
# Note that "nofollow" is not added because it is already present. Note also that the
|
880
|
+
# pre-existing duplicate "nofollow" is not removed.
|
643
881
|
#
|
644
|
-
# @example Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via a String argument.
|
645
882
|
# node # => <a rel="nofollow nofollow"></a>
|
646
883
|
# 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
884
|
#
|
650
|
-
#
|
885
|
+
# *Example:* Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via
|
886
|
+
# an Array argument.
|
887
|
+
#
|
651
888
|
# node # => <a></a>
|
652
889
|
# node.kwattr_add("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
|
653
890
|
#
|
654
|
-
#
|
655
|
-
#
|
891
|
+
# Since v1.11.0
|
656
892
|
def kwattr_add(attribute_name, keywords)
|
657
893
|
keywords = keywordify(keywords)
|
658
894
|
current_kws = kwattr_values(attribute_name)
|
@@ -661,50 +897,51 @@ module Nokogiri
|
|
661
897
|
self
|
662
898
|
end
|
663
899
|
|
664
|
-
#
|
665
|
-
#
|
900
|
+
# :call-seq:
|
901
|
+
# kwattr_append(attribute_name, keywords) → self
|
902
|
+
#
|
903
|
+
# Add keywords to a Node's keyword attribute, regardless of duplication. Compare with
|
904
|
+
# #kwattr_add.
|
666
905
|
#
|
667
|
-
# A "keyword attribute" is a node attribute that contains a set
|
668
|
-
#
|
669
|
-
#
|
670
|
-
#
|
671
|
-
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
906
|
+
# A "keyword attribute" is a node attribute that contains a set of space-delimited
|
907
|
+
# values. Perhaps the most familiar example of this is the HTML "class" attribute used to
|
908
|
+
# contain CSS classes. But other keyword attributes exist, for instance
|
909
|
+
# {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
|
672
910
|
#
|
673
|
-
#
|
674
|
-
# @see #kwattr_values
|
675
|
-
# @see #kwattr_add
|
676
|
-
# @see #kwattr_remove
|
911
|
+
# See also #append_class, #kwattr_values, #kwattr_add, #kwattr_remove
|
677
912
|
#
|
678
|
-
#
|
913
|
+
# [Parameters]
|
914
|
+
# - +attribute_name+ (String) The name of the keyword attribute to be modified.
|
915
|
+
# - +keywords+ (String, Array<String>)
|
916
|
+
# Keywords to be added to the attribute named +attribute_name+. May be a string containing
|
917
|
+
# whitespace-delimited values, or an Array of String values. All values passed in will be
|
918
|
+
# appended to the named attribute even if they are already present in the attribute. If the
|
919
|
+
# named attribute does not exist, it is created.
|
679
920
|
#
|
680
|
-
#
|
921
|
+
# [Returns] +self+ (Node) for ease of chaining method calls.
|
681
922
|
#
|
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.
|
923
|
+
# *Example:* Append "nofollow" to the +rel+ attribute.
|
689
924
|
#
|
690
|
-
#
|
925
|
+
# Note that duplicates are added.
|
691
926
|
#
|
692
|
-
# @example Append "nofollow" to the +rel+ attribute.
|
693
927
|
# node # => <a></a>
|
694
928
|
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow"></a>
|
695
|
-
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow nofollow"></a>
|
929
|
+
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow nofollow"></a>
|
930
|
+
#
|
931
|
+
# *Example:* Append "nofollow" and "noreferrer" to the +rel+ attribute, via a String argument.
|
932
|
+
#
|
933
|
+
# Note that "nofollow" is appended even though it is already present.
|
696
934
|
#
|
697
|
-
# @example Append "nofollow" and "noreferrer" to the +rel+ attribute, via a String argument.
|
698
935
|
# node # => <a rel="nofollow"></a>
|
699
936
|
# 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
937
|
#
|
702
|
-
#
|
938
|
+
#
|
939
|
+
# *Example:* Append "nofollow" and "noreferrer" to the +rel+ attribute, via an Array argument.
|
940
|
+
#
|
703
941
|
# node # => <a></a>
|
704
942
|
# node.kwattr_append("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
|
705
943
|
#
|
706
|
-
#
|
707
|
-
#
|
944
|
+
# Since v1.11.0
|
708
945
|
def kwattr_append(attribute_name, keywords)
|
709
946
|
keywords = keywordify(keywords)
|
710
947
|
current_kws = kwattr_values(attribute_name)
|
@@ -713,44 +950,41 @@ module Nokogiri
|
|
713
950
|
self
|
714
951
|
end
|
715
952
|
|
716
|
-
#
|
717
|
-
# keywords
|
718
|
-
# including any multiple entries.
|
953
|
+
# :call-seq:
|
954
|
+
# kwattr_remove(attribute_name, keywords) → self
|
719
955
|
#
|
720
|
-
#
|
721
|
-
#
|
956
|
+
# Remove keywords from a keyword attribute. Any matching keywords that exist in the named
|
957
|
+
# attribute are removed, including any multiple entries.
|
722
958
|
#
|
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).
|
959
|
+
# If no keywords remain after this operation, or if +keywords+ is +nil+, the attribute is
|
960
|
+
# deleted from the node.
|
728
961
|
#
|
729
|
-
#
|
730
|
-
#
|
731
|
-
#
|
732
|
-
#
|
962
|
+
# A "keyword attribute" is a node attribute that contains a set of space-delimited
|
963
|
+
# values. Perhaps the most familiar example of this is the HTML "class" attribute used to
|
964
|
+
# contain CSS classes. But other keyword attributes exist, for instance
|
965
|
+
# {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
|
733
966
|
#
|
734
|
-
#
|
967
|
+
# See also #remove_class, #kwattr_values, #kwattr_add, #kwattr_append
|
735
968
|
#
|
736
|
-
#
|
969
|
+
# [Parameters]
|
970
|
+
# - +attribute_name+ (String) The name of the keyword attribute to be modified.
|
971
|
+
# - +keywords+ (String, Array<String>)
|
972
|
+
# Keywords to be removed from the attribute named +attribute_name+. May be a string
|
973
|
+
# containing whitespace-delimited values, or an Array of String values. Any keywords present
|
974
|
+
# in the named attribute will be removed. If no keywords remain, or if +keywords+ is nil,
|
975
|
+
# the attribute is deleted.
|
737
976
|
#
|
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.
|
977
|
+
# [Returns] +self+ (Node) for ease of chaining method calls.
|
744
978
|
#
|
745
|
-
#
|
979
|
+
# *Example:*
|
980
|
+
#
|
981
|
+
# Note that the +rel+ attribute is deleted when empty.
|
746
982
|
#
|
747
|
-
# @example
|
748
983
|
# node # => <a rel="nofollow noreferrer">link</a>
|
749
984
|
# 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
|
985
|
+
# node.kwattr_remove("rel", "noreferrer") # => <a>link</a>
|
753
986
|
#
|
987
|
+
# Since v1.11.0
|
754
988
|
def kwattr_remove(attribute_name, keywords)
|
755
989
|
if keywords.nil?
|
756
990
|
remove_attribute(attribute_name)
|
@@ -768,13 +1002,13 @@ module Nokogiri
|
|
768
1002
|
self
|
769
1003
|
end
|
770
1004
|
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
1005
|
+
alias_method :delete, :remove_attribute
|
1006
|
+
alias_method :get_attribute, :[]
|
1007
|
+
alias_method :attr, :[]
|
1008
|
+
alias_method :set_attribute, :[]=
|
1009
|
+
alias_method :has_attribute?, :key?
|
776
1010
|
|
777
|
-
#
|
1011
|
+
# :section:
|
778
1012
|
|
779
1013
|
###
|
780
1014
|
# Returns true if this Node matches +selector+
|
@@ -786,8 +1020,7 @@ module Nokogiri
|
|
786
1020
|
# Create a DocumentFragment containing +tags+ that is relative to _this_
|
787
1021
|
# context node.
|
788
1022
|
def fragment(tags)
|
789
|
-
|
790
|
-
type::DocumentFragment.new(document, tags, self)
|
1023
|
+
document.related_class("DocumentFragment").new(document, tags, self)
|
791
1024
|
end
|
792
1025
|
|
793
1026
|
###
|
@@ -805,19 +1038,18 @@ module Nokogiri
|
|
805
1038
|
end
|
806
1039
|
|
807
1040
|
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
|
1041
|
+
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
812
1042
|
yield options if block_given?
|
813
1043
|
|
814
|
-
contents = string_or_io.respond_to?(:read)
|
815
|
-
string_or_io.read
|
1044
|
+
contents = if string_or_io.respond_to?(:read)
|
1045
|
+
string_or_io.read
|
1046
|
+
else
|
816
1047
|
string_or_io
|
1048
|
+
end
|
817
1049
|
|
818
1050
|
return Nokogiri::XML::NodeSet.new(document) if contents.empty?
|
819
1051
|
|
820
|
-
# libxml2 does not obey the
|
1052
|
+
# libxml2 does not obey the +recover+ option after encountering errors during +in_context+
|
821
1053
|
# parsing, and so this horrible hack is here to try to emulate recovery behavior.
|
822
1054
|
#
|
823
1055
|
# Unfortunately, this means we're no longer parsing "in context" and so namespaces that
|
@@ -827,16 +1059,16 @@ module Nokogiri
|
|
827
1059
|
#
|
828
1060
|
# I think preferable behavior would be to either:
|
829
1061
|
#
|
830
|
-
# a. add an error noting that we "fell back" and pointing the user to turning off the
|
1062
|
+
# a. add an error noting that we "fell back" and pointing the user to turning off the +recover+ option
|
831
1063
|
# b. don't recover, but raise a sensible exception
|
832
1064
|
#
|
833
1065
|
# For context and background: https://github.com/sparklemotion/nokogiri/issues/313
|
834
1066
|
# FIXME bug report: https://github.com/sparklemotion/nokogiri/issues/2092
|
835
1067
|
error_count = document.errors.length
|
836
1068
|
node_set = in_context(contents, options.to_i)
|
837
|
-
if
|
1069
|
+
if node_set.empty? && (document.errors.length > error_count)
|
838
1070
|
if options.recover?
|
839
|
-
fragment =
|
1071
|
+
fragment = document.related_class("DocumentFragment").parse(contents)
|
840
1072
|
node_set = fragment.children
|
841
1073
|
else
|
842
1074
|
raise document.errors[error_count]
|
@@ -845,20 +1077,42 @@ module Nokogiri
|
|
845
1077
|
node_set
|
846
1078
|
end
|
847
1079
|
|
848
|
-
|
849
|
-
#
|
850
|
-
#
|
851
|
-
#
|
852
|
-
#
|
853
|
-
#
|
854
|
-
#
|
855
|
-
#
|
856
|
-
#
|
857
|
-
#
|
858
|
-
#
|
859
|
-
#
|
860
|
-
#
|
861
|
-
#
|
1080
|
+
# :call-seq:
|
1081
|
+
# namespaces() → Hash<String(Namespace#prefix) ⇒ String(Namespace#href)>
|
1082
|
+
#
|
1083
|
+
# Fetch all the namespaces on this node and its ancestors.
|
1084
|
+
#
|
1085
|
+
# Note that the keys in this hash XML attributes that would be used to define this namespace,
|
1086
|
+
# such as "xmlns:prefix", not just the prefix.
|
1087
|
+
#
|
1088
|
+
# The default namespace for this node will be included with key "xmlns".
|
1089
|
+
#
|
1090
|
+
# See also #namespace_scopes
|
1091
|
+
#
|
1092
|
+
# [Returns]
|
1093
|
+
# Hash containing all the namespaces on this node and its ancestors. The hash keys are the
|
1094
|
+
# namespace prefix, and the hash value for each key is the namespace URI.
|
1095
|
+
#
|
1096
|
+
# *Example:*
|
1097
|
+
#
|
1098
|
+
# doc = Nokogiri::XML(<<~EOF)
|
1099
|
+
# <root xmlns="http://example.com/root" xmlns:in_scope="http://example.com/in_scope">
|
1100
|
+
# <first/>
|
1101
|
+
# <second xmlns="http://example.com/child"/>
|
1102
|
+
# <third xmlns:foo="http://example.com/foo"/>
|
1103
|
+
# </root>
|
1104
|
+
# EOF
|
1105
|
+
# doc.at_xpath("//root:first", "root" => "http://example.com/root").namespaces
|
1106
|
+
# # => {"xmlns"=>"http://example.com/root",
|
1107
|
+
# # "xmlns:in_scope"=>"http://example.com/in_scope"}
|
1108
|
+
# doc.at_xpath("//child:second", "child" => "http://example.com/child").namespaces
|
1109
|
+
# # => {"xmlns"=>"http://example.com/child",
|
1110
|
+
# # "xmlns:in_scope"=>"http://example.com/in_scope"}
|
1111
|
+
# doc.at_xpath("//root:third", "root" => "http://example.com/root").namespaces
|
1112
|
+
# # => {"xmlns:foo"=>"http://example.com/foo",
|
1113
|
+
# # "xmlns"=>"http://example.com/root",
|
1114
|
+
# # "xmlns:in_scope"=>"http://example.com/in_scope"}
|
1115
|
+
#
|
862
1116
|
def namespaces
|
863
1117
|
namespace_scopes.each_with_object({}) do |ns, hash|
|
864
1118
|
prefix = ns.prefix
|
@@ -882,14 +1136,14 @@ module Nokogiri
|
|
882
1136
|
type == DOCUMENT_NODE
|
883
1137
|
end
|
884
1138
|
|
885
|
-
# Returns true if this is an HTML4::Document node
|
1139
|
+
# Returns true if this is an HTML4::Document or HTML5::Document node
|
886
1140
|
def html?
|
887
1141
|
type == HTML_DOCUMENT_NODE
|
888
1142
|
end
|
889
1143
|
|
890
1144
|
# Returns true if this is a Document
|
891
1145
|
def document?
|
892
|
-
is_a?
|
1146
|
+
is_a?(XML::Document)
|
893
1147
|
end
|
894
1148
|
|
895
1149
|
# Returns true if this is a ProcessingInstruction node
|
@@ -912,6 +1166,7 @@ module Nokogiri
|
|
912
1166
|
# nil on XML documents and on unknown tags.
|
913
1167
|
def description
|
914
1168
|
return nil if document.xml?
|
1169
|
+
|
915
1170
|
Nokogiri::HTML4::ElementDescription[name]
|
916
1171
|
end
|
917
1172
|
|
@@ -927,7 +1182,7 @@ module Nokogiri
|
|
927
1182
|
type == ELEMENT_NODE
|
928
1183
|
end
|
929
1184
|
|
930
|
-
|
1185
|
+
alias_method :elem?, :element?
|
931
1186
|
|
932
1187
|
###
|
933
1188
|
# Turn this node in to a string. If the document is HTML, this method
|
@@ -943,9 +1198,9 @@ module Nokogiri
|
|
943
1198
|
|
944
1199
|
# Get the path to this node as a CSS expression
|
945
1200
|
def css_path
|
946
|
-
path.split(
|
947
|
-
part.
|
948
|
-
|
1201
|
+
path.split(%r{/}).filter_map do |part|
|
1202
|
+
part.empty? ? nil : part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
|
1203
|
+
end.join(" > ")
|
949
1204
|
end
|
950
1205
|
|
951
1206
|
###
|
@@ -958,7 +1213,8 @@ module Nokogiri
|
|
958
1213
|
parents = [parent]
|
959
1214
|
|
960
1215
|
while parents.last.respond_to?(:parent)
|
961
|
-
break unless ctx_parent = parents.last.parent
|
1216
|
+
break unless (ctx_parent = parents.last.parent)
|
1217
|
+
|
962
1218
|
parents << ctx_parent
|
963
1219
|
end
|
964
1220
|
|
@@ -967,16 +1223,16 @@ module Nokogiri
|
|
967
1223
|
root = parents.last
|
968
1224
|
search_results = root.search(selector)
|
969
1225
|
|
970
|
-
NodeSet.new(document, parents.find_all
|
1226
|
+
NodeSet.new(document, parents.find_all do |parent|
|
971
1227
|
search_results.include?(parent)
|
972
|
-
|
1228
|
+
end)
|
973
1229
|
end
|
974
1230
|
|
975
1231
|
####
|
976
1232
|
# Yields self and all children to +block+ recursively.
|
977
1233
|
def traverse(&block)
|
978
1234
|
children.each { |j| j.traverse(&block) }
|
979
|
-
|
1235
|
+
yield(self)
|
980
1236
|
end
|
981
1237
|
|
982
1238
|
###
|
@@ -990,6 +1246,7 @@ module Nokogiri
|
|
990
1246
|
def ==(other)
|
991
1247
|
return false unless other
|
992
1248
|
return false unless other.respond_to?(:pointer_id)
|
1249
|
+
|
993
1250
|
pointer_id == other.pointer_id
|
994
1251
|
end
|
995
1252
|
|
@@ -999,14 +1256,16 @@ module Nokogiri
|
|
999
1256
|
def <=>(other)
|
1000
1257
|
return nil unless other.is_a?(Nokogiri::XML::Node)
|
1001
1258
|
return nil unless document == other.document
|
1002
|
-
|
1259
|
+
|
1260
|
+
compare(other)
|
1003
1261
|
end
|
1004
1262
|
|
1005
|
-
#
|
1263
|
+
# :section: Serialization and Generating Output
|
1006
1264
|
|
1007
1265
|
###
|
1008
|
-
# Serialize Node using +options+.
|
1009
|
-
#
|
1266
|
+
# Serialize Node using +options+. Save options can also be set using a block.
|
1267
|
+
#
|
1268
|
+
# See also Nokogiri::XML::Node::SaveOptions and Node@Serialization+and+Generating+Output.
|
1010
1269
|
#
|
1011
1270
|
# These two statements are equivalent:
|
1012
1271
|
#
|
@@ -1019,18 +1278,21 @@ module Nokogiri
|
|
1019
1278
|
# end
|
1020
1279
|
#
|
1021
1280
|
def serialize(*args, &block)
|
1022
|
-
options = args.first.is_a?(Hash)
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1281
|
+
options = if args.first.is_a?(Hash)
|
1282
|
+
args.shift
|
1283
|
+
else
|
1284
|
+
{
|
1285
|
+
encoding: args[0],
|
1286
|
+
save_with: args[1],
|
1287
|
+
}
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
options[:encoding] ||= document.encoding
|
1291
|
+
encoding = Encoding.find(options[:encoding] || "UTF-8")
|
1292
|
+
|
1293
|
+
io = StringIO.new(String.new(encoding: encoding))
|
1294
|
+
|
1295
|
+
write_to(io, options, &block)
|
1034
1296
|
io.string
|
1035
1297
|
end
|
1036
1298
|
|
@@ -1042,7 +1304,7 @@ module Nokogiri
|
|
1042
1304
|
# See Node#write_to for a list of +options+. For formatted output,
|
1043
1305
|
# use Node#to_xhtml instead.
|
1044
1306
|
def to_html(options = {})
|
1045
|
-
to_format
|
1307
|
+
to_format(SaveOptions::DEFAULT_HTML, options)
|
1046
1308
|
end
|
1047
1309
|
|
1048
1310
|
###
|
@@ -1063,7 +1325,7 @@ module Nokogiri
|
|
1063
1325
|
#
|
1064
1326
|
# See Node#write_to for a list of +options+
|
1065
1327
|
def to_xhtml(options = {})
|
1066
|
-
to_format
|
1328
|
+
to_format(SaveOptions::DEFAULT_XHTML, options)
|
1067
1329
|
end
|
1068
1330
|
|
1069
1331
|
###
|
@@ -1111,7 +1373,7 @@ module Nokogiri
|
|
1111
1373
|
#
|
1112
1374
|
# See Node#write_to for a list of +options+
|
1113
1375
|
def write_html_to(io, options = {})
|
1114
|
-
write_format_to
|
1376
|
+
write_format_to(SaveOptions::DEFAULT_HTML, io, options)
|
1115
1377
|
end
|
1116
1378
|
|
1117
1379
|
###
|
@@ -1119,7 +1381,7 @@ module Nokogiri
|
|
1119
1381
|
#
|
1120
1382
|
# See Node#write_to for a list of +options+
|
1121
1383
|
def write_xhtml_to(io, options = {})
|
1122
|
-
write_format_to
|
1384
|
+
write_format_to(SaveOptions::DEFAULT_XHTML, io, options)
|
1123
1385
|
end
|
1124
1386
|
|
1125
1387
|
###
|
@@ -1130,7 +1392,7 @@ module Nokogiri
|
|
1130
1392
|
# See Node#write_to for a list of options
|
1131
1393
|
def write_xml_to(io, options = {})
|
1132
1394
|
options[:save_with] ||= SaveOptions::DEFAULT_XML
|
1133
|
-
write_to
|
1395
|
+
write_to(io, options)
|
1134
1396
|
end
|
1135
1397
|
|
1136
1398
|
def canonicalize(mode = XML::XML_C14N_1_0, inclusive_namespaces = nil, with_comments = false)
|
@@ -1141,7 +1403,70 @@ module Nokogiri
|
|
1141
1403
|
end
|
1142
1404
|
end
|
1143
1405
|
|
1144
|
-
#
|
1406
|
+
DECONSTRUCT_KEYS = [:name, :attributes, :children, :namespace, :content, :elements, :inner_html].freeze # :nodoc:
|
1407
|
+
DECONSTRUCT_METHODS = { attributes: :attribute_nodes }.freeze # :nodoc:
|
1408
|
+
|
1409
|
+
#
|
1410
|
+
# :call-seq: deconstruct_keys(array_of_names) → Hash
|
1411
|
+
#
|
1412
|
+
# Returns a hash describing the Node, to use in pattern matching.
|
1413
|
+
#
|
1414
|
+
# Valid keys and their values:
|
1415
|
+
# - +name+ → (String) The name of this node, or "text" if it is a Text node.
|
1416
|
+
# - +namespace+ → (Namespace, nil) The namespace of this node, or nil if there is no namespace.
|
1417
|
+
# - +attributes+ → (Array<Attr>) The attributes of this node.
|
1418
|
+
# - +children+ → (Array<Node>) The children of this node. 💡 Note this includes text nodes.
|
1419
|
+
# - +elements+ → (Array<Node>) The child elements of this node. 💡 Note this does not include text nodes.
|
1420
|
+
# - +content+ → (String) The contents of all the text nodes in this node's subtree. See #content.
|
1421
|
+
# - +inner_html+ → (String) The inner markup for the children of this node. See #inner_html.
|
1422
|
+
#
|
1423
|
+
# ⚡ This is an experimental feature, available since v1.14.0
|
1424
|
+
#
|
1425
|
+
# *Example*
|
1426
|
+
#
|
1427
|
+
# doc = Nokogiri::XML.parse(<<~XML)
|
1428
|
+
# <?xml version="1.0"?>
|
1429
|
+
# <parent xmlns="http://nokogiri.org/ns/default" xmlns:noko="http://nokogiri.org/ns/noko">
|
1430
|
+
# <child1 foo="abc" noko:bar="def">First</child1>
|
1431
|
+
# <noko:child2 foo="qwe" noko:bar="rty">Second</noko:child2>
|
1432
|
+
# </parent>
|
1433
|
+
# XML
|
1434
|
+
#
|
1435
|
+
# doc.root.deconstruct_keys([:name, :namespace])
|
1436
|
+
# # => {:name=>"parent",
|
1437
|
+
# # :namespace=>
|
1438
|
+
# # #(Namespace:0x35c { href = "http://nokogiri.org/ns/default" })}
|
1439
|
+
#
|
1440
|
+
# doc.root.deconstruct_keys([:inner_html, :content])
|
1441
|
+
# # => {:content=>"\n" + " First\n" + " Second\n",
|
1442
|
+
# # :inner_html=>
|
1443
|
+
# # "\n" +
|
1444
|
+
# # " <child1 foo=\"abc\" noko:bar=\"def\">First</child1>\n" +
|
1445
|
+
# # " <noko:child2 foo=\"qwe\" noko:bar=\"rty\">Second</noko:child2>\n"}
|
1446
|
+
#
|
1447
|
+
# doc.root.elements.first.deconstruct_keys([:attributes])
|
1448
|
+
# # => {:attributes=>
|
1449
|
+
# # [#(Attr:0x370 { name = "foo", value = "abc" }),
|
1450
|
+
# # #(Attr:0x384 {
|
1451
|
+
# # name = "bar",
|
1452
|
+
# # namespace = #(Namespace:0x398 {
|
1453
|
+
# # prefix = "noko",
|
1454
|
+
# # href = "http://nokogiri.org/ns/noko"
|
1455
|
+
# # }),
|
1456
|
+
# # value = "def"
|
1457
|
+
# # })]}
|
1458
|
+
#
|
1459
|
+
def deconstruct_keys(keys)
|
1460
|
+
requested_keys = DECONSTRUCT_KEYS & keys
|
1461
|
+
{}.tap do |values|
|
1462
|
+
requested_keys.each do |key|
|
1463
|
+
method = DECONSTRUCT_METHODS[key] || key
|
1464
|
+
values[key] = send(method)
|
1465
|
+
end
|
1466
|
+
end
|
1467
|
+
end
|
1468
|
+
|
1469
|
+
# :section:
|
1145
1470
|
|
1146
1471
|
protected
|
1147
1472
|
|
@@ -1159,9 +1484,9 @@ module Nokogiri
|
|
1159
1484
|
return data
|
1160
1485
|
end
|
1161
1486
|
|
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().)
|
1487
|
+
raise ArgumentError, <<~EOERR
|
1488
|
+
Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
1489
|
+
(You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
|
1165
1490
|
EOERR
|
1166
1491
|
end
|
1167
1492
|
|
@@ -1170,32 +1495,33 @@ Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
|
1170
1495
|
def keywordify(keywords)
|
1171
1496
|
case keywords
|
1172
1497
|
when Enumerable
|
1173
|
-
|
1498
|
+
keywords
|
1174
1499
|
when String
|
1175
|
-
|
1500
|
+
keywords.scan(/\S+/)
|
1176
1501
|
else
|
1177
|
-
raise ArgumentError
|
1502
|
+
raise ArgumentError,
|
1503
|
+
"Keyword attributes must be passed as either a String or an Enumerable, but received #{keywords.class}"
|
1178
1504
|
end
|
1179
1505
|
end
|
1180
1506
|
|
1181
1507
|
def add_sibling(next_or_previous, node_or_tags)
|
1182
1508
|
raise("Cannot add sibling to a node with no parent") unless parent
|
1183
1509
|
|
1184
|
-
impl =
|
1185
|
-
iter =
|
1510
|
+
impl = next_or_previous == :next ? :add_next_sibling_node : :add_previous_sibling_node
|
1511
|
+
iter = next_or_previous == :next ? :reverse_each : :each
|
1186
1512
|
|
1187
1513
|
node_or_tags = parent.coerce(node_or_tags)
|
1188
1514
|
if node_or_tags.is_a?(XML::NodeSet)
|
1189
1515
|
if text?
|
1190
|
-
pivot = Nokogiri::XML::Node.new
|
1191
|
-
send
|
1516
|
+
pivot = Nokogiri::XML::Node.new("dummy", document)
|
1517
|
+
send(impl, pivot)
|
1192
1518
|
else
|
1193
1519
|
pivot = self
|
1194
1520
|
end
|
1195
|
-
node_or_tags.send(iter) { |n| pivot.send
|
1521
|
+
node_or_tags.send(iter) { |n| pivot.send(impl, n) }
|
1196
1522
|
pivot.unlink if text?
|
1197
1523
|
else
|
1198
|
-
send
|
1524
|
+
send(impl, node_or_tags)
|
1199
1525
|
end
|
1200
1526
|
node_or_tags
|
1201
1527
|
end
|
@@ -1214,19 +1540,18 @@ Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
|
1214
1540
|
return (io << dump_html) if USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
1215
1541
|
|
1216
1542
|
options[:save_with] ||= save_option
|
1217
|
-
write_to
|
1543
|
+
write_to(io, options)
|
1218
1544
|
end
|
1219
1545
|
|
1220
1546
|
def inspect_attributes
|
1221
1547
|
[:name, :namespace, :attribute_nodes, :children]
|
1222
1548
|
end
|
1223
1549
|
|
1224
|
-
|
1225
|
-
IMPLIED_XPATH_CONTEXTS = [".//".freeze].freeze
|
1550
|
+
IMPLIED_XPATH_CONTEXTS = [".//"].freeze
|
1226
1551
|
|
1227
1552
|
def add_child_node_and_reparent_attrs(node)
|
1228
|
-
add_child_node
|
1229
|
-
node.attribute_nodes.find_all { |a| a.name
|
1553
|
+
add_child_node(node)
|
1554
|
+
node.attribute_nodes.find_all { |a| a.name.include?(":") }.each do |attr_node|
|
1230
1555
|
attr_node.remove
|
1231
1556
|
node[attr_node.name] = attr_node.value
|
1232
1557
|
end
|