nokogiri 1.10.10 → 1.11.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 +3 -0
- data/LICENSE-DEPENDENCIES.md +1015 -947
- data/LICENSE.md +1 -1
- data/README.md +173 -94
- data/ext/nokogiri/depend +37 -358
- data/ext/nokogiri/extconf.rb +611 -391
- data/ext/nokogiri/html_document.c +78 -82
- data/ext/nokogiri/html_element_description.c +84 -71
- data/ext/nokogiri/html_entity_lookup.c +21 -16
- data/ext/nokogiri/html_sax_parser_context.c +69 -66
- data/ext/nokogiri/html_sax_push_parser.c +42 -34
- data/ext/nokogiri/libxml2_backwards_compat.c +121 -0
- data/ext/nokogiri/nokogiri.c +192 -87
- data/ext/nokogiri/nokogiri.h +181 -89
- data/ext/nokogiri/test_global_handlers.c +40 -0
- data/ext/nokogiri/xml_attr.c +15 -15
- data/ext/nokogiri/xml_attribute_decl.c +18 -18
- data/ext/nokogiri/xml_cdata.c +13 -18
- data/ext/nokogiri/xml_comment.c +19 -26
- data/ext/nokogiri/xml_document.c +255 -183
- data/ext/nokogiri/xml_document_fragment.c +13 -15
- data/ext/nokogiri/xml_dtd.c +54 -48
- data/ext/nokogiri/xml_element_content.c +30 -27
- data/ext/nokogiri/xml_element_decl.c +22 -22
- data/ext/nokogiri/xml_encoding_handler.c +17 -11
- data/ext/nokogiri/xml_entity_decl.c +32 -30
- data/ext/nokogiri/xml_entity_reference.c +16 -18
- data/ext/nokogiri/xml_namespace.c +56 -49
- data/ext/nokogiri/xml_node.c +387 -316
- data/ext/nokogiri/xml_node_set.c +168 -156
- data/ext/nokogiri/xml_processing_instruction.c +17 -19
- data/ext/nokogiri/xml_reader.c +195 -172
- data/ext/nokogiri/xml_relax_ng.c +52 -28
- data/ext/nokogiri/xml_sax_parser.c +118 -118
- data/ext/nokogiri/xml_sax_parser_context.c +103 -86
- data/ext/nokogiri/xml_sax_push_parser.c +36 -27
- data/ext/nokogiri/xml_schema.c +95 -47
- data/ext/nokogiri/xml_syntax_error.c +42 -21
- data/ext/nokogiri/xml_text.c +13 -17
- data/ext/nokogiri/xml_xpath_context.c +206 -123
- data/ext/nokogiri/xslt_stylesheet.c +158 -165
- data/lib/nokogiri.rb +6 -27
- data/lib/nokogiri/css.rb +1 -0
- data/lib/nokogiri/css/node.rb +1 -0
- data/lib/nokogiri/css/parser.rb +63 -62
- data/lib/nokogiri/css/parser.y +2 -2
- data/lib/nokogiri/css/parser_extras.rb +39 -36
- data/lib/nokogiri/css/syntax_error.rb +1 -0
- data/lib/nokogiri/css/tokenizer.rb +1 -0
- data/lib/nokogiri/css/xpath_visitor.rb +73 -43
- data/lib/nokogiri/decorators/slop.rb +1 -0
- data/lib/nokogiri/extension.rb +26 -0
- data/lib/nokogiri/html.rb +1 -0
- data/lib/nokogiri/html/builder.rb +1 -0
- data/lib/nokogiri/html/document.rb +13 -26
- data/lib/nokogiri/html/document_fragment.rb +16 -15
- data/lib/nokogiri/html/element_description.rb +1 -0
- data/lib/nokogiri/html/element_description_defaults.rb +1 -0
- data/lib/nokogiri/html/entity_lookup.rb +1 -0
- data/lib/nokogiri/html/sax/parser.rb +1 -0
- data/lib/nokogiri/html/sax/parser_context.rb +1 -0
- data/lib/nokogiri/html/sax/push_parser.rb +1 -0
- data/lib/nokogiri/jruby/dependencies.rb +20 -0
- data/lib/nokogiri/syntax_error.rb +1 -0
- data/lib/nokogiri/version.rb +3 -109
- data/lib/nokogiri/version/constant.rb +5 -0
- data/lib/nokogiri/version/info.rb +205 -0
- data/lib/nokogiri/xml.rb +1 -0
- data/lib/nokogiri/xml/attr.rb +1 -0
- data/lib/nokogiri/xml/attribute_decl.rb +1 -0
- data/lib/nokogiri/xml/builder.rb +3 -2
- data/lib/nokogiri/xml/cdata.rb +1 -0
- data/lib/nokogiri/xml/character_data.rb +1 -0
- data/lib/nokogiri/xml/document.rb +92 -41
- data/lib/nokogiri/xml/document_fragment.rb +5 -6
- data/lib/nokogiri/xml/dtd.rb +1 -0
- data/lib/nokogiri/xml/element_content.rb +1 -0
- data/lib/nokogiri/xml/element_decl.rb +1 -0
- data/lib/nokogiri/xml/entity_decl.rb +1 -0
- data/lib/nokogiri/xml/entity_reference.rb +1 -0
- data/lib/nokogiri/xml/namespace.rb +1 -0
- data/lib/nokogiri/xml/node.rb +625 -290
- data/lib/nokogiri/xml/node/save_options.rb +1 -0
- data/lib/nokogiri/xml/node_set.rb +1 -0
- data/lib/nokogiri/xml/notation.rb +1 -0
- data/lib/nokogiri/xml/parse_options.rb +10 -3
- data/lib/nokogiri/xml/pp.rb +1 -0
- data/lib/nokogiri/xml/pp/character_data.rb +1 -0
- data/lib/nokogiri/xml/pp/node.rb +1 -0
- data/lib/nokogiri/xml/processing_instruction.rb +1 -0
- data/lib/nokogiri/xml/reader.rb +9 -12
- data/lib/nokogiri/xml/relax_ng.rb +7 -2
- data/lib/nokogiri/xml/sax.rb +1 -0
- data/lib/nokogiri/xml/sax/document.rb +1 -0
- data/lib/nokogiri/xml/sax/parser.rb +1 -0
- data/lib/nokogiri/xml/sax/parser_context.rb +1 -0
- data/lib/nokogiri/xml/sax/push_parser.rb +1 -0
- data/lib/nokogiri/xml/schema.rb +13 -4
- data/lib/nokogiri/xml/searchable.rb +25 -16
- data/lib/nokogiri/xml/syntax_error.rb +1 -0
- data/lib/nokogiri/xml/text.rb +1 -0
- data/lib/nokogiri/xml/xpath.rb +2 -3
- data/lib/nokogiri/xml/xpath/syntax_error.rb +2 -1
- data/lib/nokogiri/xml/xpath_context.rb +1 -0
- data/lib/nokogiri/xslt.rb +1 -0
- data/lib/nokogiri/xslt/stylesheet.rb +1 -0
- data/lib/xsd/xmlparser/nokogiri.rb +1 -0
- data/patches/libxml2/0006-htmlParseComment-treat-as-if-it-closed-the-comment.patch +73 -0
- data/patches/libxml2/0007-use-new-htmlParseLookupCommentEnd-to-find-comment-en.patch +103 -0
- data/patches/libxml2/0008-use-glibc-strlen.patch +53 -0
- data/patches/libxml2/0009-avoid-isnan-isinf.patch +81 -0
- data/patches/libxml2/0010-parser.c-shrink-the-input-buffer-when-appropriate.patch +70 -0
- data/patches/libxml2/0011-update-automake-files-for-arm64.patch +2511 -0
- data/patches/libxslt/0001-update-automake-files-for-arm64.patch +2511 -0
- metadata +88 -149
- data/ext/nokogiri/html_document.h +0 -10
- data/ext/nokogiri/html_element_description.h +0 -10
- data/ext/nokogiri/html_entity_lookup.h +0 -8
- data/ext/nokogiri/html_sax_parser_context.h +0 -11
- data/ext/nokogiri/html_sax_push_parser.h +0 -9
- data/ext/nokogiri/xml_attr.h +0 -9
- data/ext/nokogiri/xml_attribute_decl.h +0 -9
- data/ext/nokogiri/xml_cdata.h +0 -9
- data/ext/nokogiri/xml_comment.h +0 -9
- data/ext/nokogiri/xml_document.h +0 -23
- data/ext/nokogiri/xml_document_fragment.h +0 -10
- data/ext/nokogiri/xml_dtd.h +0 -10
- data/ext/nokogiri/xml_element_content.h +0 -10
- data/ext/nokogiri/xml_element_decl.h +0 -9
- data/ext/nokogiri/xml_encoding_handler.h +0 -8
- data/ext/nokogiri/xml_entity_decl.h +0 -10
- data/ext/nokogiri/xml_entity_reference.h +0 -9
- data/ext/nokogiri/xml_io.c +0 -61
- data/ext/nokogiri/xml_io.h +0 -11
- data/ext/nokogiri/xml_libxml2_hacks.c +0 -112
- data/ext/nokogiri/xml_libxml2_hacks.h +0 -12
- data/ext/nokogiri/xml_namespace.h +0 -14
- data/ext/nokogiri/xml_node.h +0 -13
- data/ext/nokogiri/xml_node_set.h +0 -12
- data/ext/nokogiri/xml_processing_instruction.h +0 -9
- data/ext/nokogiri/xml_reader.h +0 -10
- data/ext/nokogiri/xml_relax_ng.h +0 -9
- data/ext/nokogiri/xml_sax_parser.h +0 -39
- data/ext/nokogiri/xml_sax_parser_context.h +0 -10
- data/ext/nokogiri/xml_sax_push_parser.h +0 -9
- data/ext/nokogiri/xml_schema.h +0 -9
- data/ext/nokogiri/xml_syntax_error.h +0 -13
- data/ext/nokogiri/xml_text.h +0 -9
- data/ext/nokogiri/xml_xpath_context.h +0 -10
- data/ext/nokogiri/xslt_stylesheet.h +0 -14
data/lib/nokogiri/xml.rb
CHANGED
data/lib/nokogiri/xml/attr.rb
CHANGED
data/lib/nokogiri/xml/builder.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Nokogiri
|
2
3
|
module XML
|
3
4
|
###
|
@@ -244,8 +245,8 @@ module Nokogiri
|
|
244
245
|
#
|
245
246
|
# For example:
|
246
247
|
#
|
247
|
-
# doc = Nokogiri::XML(
|
248
|
-
# Nokogiri::XML::Builder.with(doc.
|
248
|
+
# doc = Nokogiri::XML(File.read('somedoc.xml'))
|
249
|
+
# Nokogiri::XML::Builder.with(doc.at_css('some_tag')) do |xml|
|
249
250
|
# # ... Use normal builder methods here ...
|
250
251
|
# xml.awesome # add the "awesome" tag below "some_tag"
|
251
252
|
# end
|
data/lib/nokogiri/xml/cdata.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pathname'
|
5
|
+
|
1
6
|
module Nokogiri
|
2
7
|
module XML
|
3
8
|
##
|
@@ -9,11 +14,12 @@ module Nokogiri
|
|
9
14
|
# Nokogiri::XML::Searchable#xpath
|
10
15
|
#
|
11
16
|
class Document < Nokogiri::XML::Node
|
12
|
-
#
|
13
|
-
#
|
17
|
+
# See http://www.w3.org/TR/REC-xml-names/#ns-decl for more details. Note that we're not
|
18
|
+
# attempting to handle unicode characters partly because libxml2 doesn't handle unicode
|
19
|
+
# characters in NCNAMEs.
|
14
20
|
NCNAME_START_CHAR = "A-Za-z_"
|
15
|
-
NCNAME_CHAR = NCNAME_START_CHAR + "
|
16
|
-
NCNAME_RE = /^xmlns(
|
21
|
+
NCNAME_CHAR = NCNAME_START_CHAR + "\\-\\.0-9"
|
22
|
+
NCNAME_RE = /^xmlns(?::([#{NCNAME_START_CHAR}][#{NCNAME_CHAR}]*))?$/
|
17
23
|
|
18
24
|
##
|
19
25
|
# Parse an XML file.
|
@@ -43,9 +49,11 @@ module Nokogiri
|
|
43
49
|
#
|
44
50
|
def self.parse string_or_io, url = nil, encoding = nil, options = ParseOptions::DEFAULT_XML
|
45
51
|
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
46
|
-
|
52
|
+
|
47
53
|
yield options if block_given?
|
48
54
|
|
55
|
+
url ||= string_or_io.respond_to?(:path) ? string_or_io.path : nil
|
56
|
+
|
49
57
|
if empty_doc?(string_or_io)
|
50
58
|
if options.strict?
|
51
59
|
raise Nokogiri::XML::SyntaxError.new("Empty document")
|
@@ -55,12 +63,17 @@ module Nokogiri
|
|
55
63
|
end
|
56
64
|
|
57
65
|
doc = if string_or_io.respond_to?(:read)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
66
|
+
if string_or_io.is_a?(Pathname)
|
67
|
+
# resolve the Pathname to the file and open it as an IO object, see #2110
|
68
|
+
string_or_io = string_or_io.expand_path.open
|
69
|
+
url ||= string_or_io.path
|
70
|
+
end
|
71
|
+
|
72
|
+
read_io(string_or_io, url, encoding, options.to_i)
|
73
|
+
else
|
74
|
+
# read_memory pukes on empty docs
|
75
|
+
read_memory(string_or_io, url, encoding, options.to_i)
|
76
|
+
end
|
64
77
|
|
65
78
|
# do xinclude processing
|
66
79
|
doc.do_xinclude(options) if options.xinclude?
|
@@ -68,6 +81,35 @@ module Nokogiri
|
|
68
81
|
return doc
|
69
82
|
end
|
70
83
|
|
84
|
+
##
|
85
|
+
# @!method wrap(java_document)
|
86
|
+
# @!scope class
|
87
|
+
#
|
88
|
+
# Create a {Document} using an existing Java DOM document object.
|
89
|
+
#
|
90
|
+
# The returned {Document} shares the same underlying data structure as the Java object, so
|
91
|
+
# changes in one are reflected in the other.
|
92
|
+
#
|
93
|
+
# @param java_document [Java::OrgW3cDom::Document]
|
94
|
+
# @return [Nokogiri::XML::Document]
|
95
|
+
# @note This method is only available when running JRuby.
|
96
|
+
# @note The class +Java::OrgW3cDom::Document+ is also accessible as +org.w3c.dom.Document+.
|
97
|
+
# @see #to_java
|
98
|
+
|
99
|
+
##
|
100
|
+
# @!method to_java()
|
101
|
+
#
|
102
|
+
# Returns the underlying Java DOM document object for the {Document}.
|
103
|
+
#
|
104
|
+
# The returned Java object shares the same underlying data structure as the {Document}, so
|
105
|
+
# changes in one are reflected in the other.
|
106
|
+
#
|
107
|
+
# @return [Java::OrgW3cDom::Document]
|
108
|
+
# @note This method is only available when running JRuby.
|
109
|
+
# @note The class +Java::OrgW3cDom::Document+ is also accessible as +org.w3c.dom.Document+.
|
110
|
+
# @see .wrap
|
111
|
+
|
112
|
+
|
71
113
|
# A list of Nokogiri::XML::SyntaxError found when parsing a document
|
72
114
|
attr_accessor :errors
|
73
115
|
|
@@ -77,33 +119,58 @@ module Nokogiri
|
|
77
119
|
end
|
78
120
|
|
79
121
|
##
|
80
|
-
# Create
|
122
|
+
# Create a new +Element+ with +name+ sharing GC lifecycle with the document, optionally
|
123
|
+
# setting contents or attributes.
|
124
|
+
#
|
125
|
+
# Arguments may be passed to initialize the element:
|
126
|
+
# - a +Hash+ argument will be used to set attributes
|
127
|
+
# - a non-Hash object that responds to +#to_s+ will be used to set the new node's contents
|
128
|
+
#
|
129
|
+
# A block may be passed to mutate the node.
|
130
|
+
#
|
131
|
+
# @param name [String]
|
132
|
+
# @param contents_or_attrs [#to_s,Hash]
|
133
|
+
# @yieldparam node [Nokogiri::XML::Element]
|
134
|
+
# @return [Nokogiri::XML::Element]
|
135
|
+
#
|
136
|
+
# @example An empty element without attributes
|
137
|
+
# doc.create_element("div")
|
138
|
+
# # => <div></div>
|
139
|
+
#
|
140
|
+
# @example An element with contents
|
141
|
+
# doc.create_element("div", "contents")
|
142
|
+
# # => <div>contents</div>
|
143
|
+
#
|
144
|
+
# @example An element with attributes
|
145
|
+
# doc.create_element("div", {"class" => "container"})
|
146
|
+
# # => <div class='container'></div>
|
81
147
|
#
|
82
|
-
#
|
83
|
-
# doc.create_element
|
84
|
-
#
|
85
|
-
# doc.create_element "div", "contents", :class => "container" # <div class='container'>contents</div>
|
86
|
-
# doc.create_element "div" { |node| node['class'] = "container" } # <div class='container'></div>
|
148
|
+
# @example An element with contents and attributes
|
149
|
+
# doc.create_element("div", "contents", {"class" => "container"})
|
150
|
+
# # => <div class='container'>contents</div>
|
87
151
|
#
|
88
|
-
|
152
|
+
# @example Passing a block to mutate the element
|
153
|
+
# doc.create_element("div") { |node| node["class"] = "blue" if before_noon? }
|
154
|
+
#
|
155
|
+
def create_element(name, *contents_or_attrs, &block)
|
89
156
|
elm = Nokogiri::XML::Element.new(name, self, &block)
|
90
|
-
|
157
|
+
contents_or_attrs.each do |arg|
|
91
158
|
case arg
|
92
159
|
when Hash
|
93
|
-
arg.each
|
160
|
+
arg.each do |k, v|
|
94
161
|
key = k.to_s
|
95
162
|
if key =~ NCNAME_RE
|
96
|
-
ns_name =
|
97
|
-
elm.add_namespace_definition
|
163
|
+
ns_name = Regexp.last_match(1)
|
164
|
+
elm.add_namespace_definition(ns_name, v)
|
98
165
|
else
|
99
166
|
elm[k.to_s] = v.to_s
|
100
167
|
end
|
101
|
-
|
168
|
+
end
|
102
169
|
else
|
103
170
|
elm.content = arg
|
104
171
|
end
|
105
172
|
end
|
106
|
-
if ns = elm.namespace_definitions.find { |n| n.prefix.nil?
|
173
|
+
if ns = elm.namespace_definitions.find { |n| n.prefix.nil? || (n.prefix == '') }
|
107
174
|
elm.namespace = ns
|
108
175
|
end
|
109
176
|
elm
|
@@ -251,30 +318,14 @@ module Nokogiri
|
|
251
318
|
end
|
252
319
|
alias :<< :add_child
|
253
320
|
|
254
|
-
##
|
255
|
-
# +JRuby+
|
256
|
-
# Wraps Java's org.w3c.dom.document and returns Nokogiri::XML::Document
|
257
|
-
def self.wrap document
|
258
|
-
raise "JRuby only method" unless Nokogiri.jruby?
|
259
|
-
return wrapJavaDocument(document)
|
260
|
-
end
|
261
|
-
|
262
|
-
##
|
263
|
-
# +JRuby+
|
264
|
-
# Returns Java's org.w3c.dom.document of this Document.
|
265
|
-
def to_java
|
266
|
-
raise "JRuby only method" unless Nokogiri.jruby?
|
267
|
-
return toJavaDocument()
|
268
|
-
end
|
269
|
-
|
270
321
|
private
|
322
|
+
|
271
323
|
def self.empty_doc? string_or_io
|
272
324
|
string_or_io.nil? ||
|
273
325
|
(string_or_io.respond_to?(:empty?) && string_or_io.empty?) ||
|
274
326
|
(string_or_io.respond_to?(:eof?) && string_or_io.eof?)
|
275
327
|
end
|
276
328
|
|
277
|
-
# @private
|
278
329
|
IMPLIED_XPATH_CONTEXTS = [ '//'.freeze ].freeze # :nodoc:
|
279
330
|
|
280
331
|
def inspect_attributes
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Nokogiri
|
2
3
|
module XML
|
3
4
|
class DocumentFragment < Nokogiri::XML::Node
|
@@ -140,6 +141,10 @@ module Nokogiri
|
|
140
141
|
document.errors = things
|
141
142
|
end
|
142
143
|
|
144
|
+
def fragment(data)
|
145
|
+
document.fragment(data)
|
146
|
+
end
|
147
|
+
|
143
148
|
private
|
144
149
|
|
145
150
|
# fix for issue 770
|
@@ -149,12 +154,6 @@ module Nokogiri
|
|
149
154
|
%Q{xmlns#{prefix}="#{namespace.href}"}
|
150
155
|
end.join ' '
|
151
156
|
end
|
152
|
-
|
153
|
-
def coerce data
|
154
|
-
return super unless String === data
|
155
|
-
|
156
|
-
document.fragment(data).children
|
157
|
-
end
|
158
157
|
end
|
159
158
|
end
|
160
159
|
end
|
data/lib/nokogiri/xml/dtd.rb
CHANGED
data/lib/nokogiri/xml/node.rb
CHANGED
@@ -1,105 +1,103 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
|
3
|
-
require
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require "stringio"
|
4
|
+
require "nokogiri/xml/node/save_options"
|
4
5
|
|
5
6
|
module Nokogiri
|
6
7
|
module XML
|
7
|
-
|
8
|
-
# Nokogiri::XML::Node is your window to the fun filled world of dealing
|
9
|
-
#
|
10
|
-
#
|
8
|
+
##
|
9
|
+
# {Nokogiri::XML::Node} is your window to the fun filled world of dealing with XML and HTML
|
10
|
+
# tags. A {Nokogiri::XML::Node} may be treated similarly to a hash with regard to attributes. For
|
11
|
+
# example:
|
11
12
|
#
|
12
|
-
#
|
13
|
-
# => <a href
|
14
|
-
#
|
15
|
-
# => "
|
16
|
-
#
|
17
|
-
# =>
|
18
|
-
#
|
19
|
-
# => ["#foo", "link"]
|
20
|
-
# irb(main):008:0> node['class'] = 'green'
|
21
|
-
# => "green"
|
22
|
-
# irb(main):009:0> node
|
23
|
-
# => <a href="#foo" id="link" class="green">link</a>
|
24
|
-
# irb(main):010:0>
|
13
|
+
# node = Nokogiri::XML::DocumentFragment.parse("<a href='#foo' id='link'>link</a>").at_css("a")
|
14
|
+
# node.to_html # => "<a href=\"#foo\" id=\"link\">link</a>"
|
15
|
+
# node['href'] # => "#foo"
|
16
|
+
# node.keys # => ["href", "id"]
|
17
|
+
# node.values # => ["#foo", "link"]
|
18
|
+
# node['class'] = 'green' # => "green"
|
19
|
+
# node.to_html # => "<a href=\"#foo\" id=\"link\" class=\"green\">link</a>"
|
25
20
|
#
|
26
|
-
# See
|
21
|
+
# See the method group entitled "Working With Node Attributes" for the full set of methods.
|
27
22
|
#
|
28
|
-
# Nokogiri::XML::Node also has methods that let you move around your
|
23
|
+
# {Nokogiri::XML::Node} also has methods that let you move around your
|
29
24
|
# tree. For navigating your tree, see:
|
30
25
|
#
|
31
|
-
# *
|
32
|
-
# *
|
33
|
-
# *
|
34
|
-
# *
|
35
|
-
#
|
26
|
+
# * {#parent}
|
27
|
+
# * {#children}
|
28
|
+
# * {#next}
|
29
|
+
# * {#previous}
|
36
30
|
#
|
37
31
|
# When printing or otherwise emitting a document or a node (and
|
38
32
|
# its subtree), there are a few methods you might want to use:
|
39
33
|
#
|
40
|
-
# * content, text, inner_text, to_str: emit plaintext
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# document, meaning that entities will be replaced (e.g., "<"
|
44
|
-
# will be replaced with "<"), meaning that any sanitizing will
|
45
|
-
# likely be un-done in the output.
|
34
|
+
# * {#content}, {#text}, {#inner_text}, {#to_str}: These methods will all <b>emit plaintext</b>,
|
35
|
+
# meaning that entities will be replaced (e.g., "<" will be replaced with "<"), meaning
|
36
|
+
# that any sanitizing will likely be un-done in the output.
|
46
37
|
#
|
47
|
-
# * to_s, to_xml, to_html, inner_html:
|
38
|
+
# * {#to_s}, {#to_xml}, {#to_html}, {#inner_html}: These methods will all <b>emit
|
39
|
+
# properly-escaped markup</b>, meaning that it's suitable for consumption by browsers,
|
40
|
+
# parsers, etc.
|
48
41
|
#
|
49
|
-
#
|
50
|
-
# that it's suitable for consumption by browsers, parsers, etc.
|
42
|
+
# You may search this node's subtree using {#xpath} and {#css}
|
51
43
|
#
|
52
|
-
# You may search this node's subtree using Searchable#xpath and Searchable#css
|
53
44
|
class Node
|
54
45
|
include Nokogiri::XML::PP::Node
|
55
46
|
include Nokogiri::XML::Searchable
|
56
47
|
include Enumerable
|
57
48
|
|
58
|
-
# Element node type, see Nokogiri::XML::Node#element?
|
59
|
-
ELEMENT_NODE =
|
49
|
+
# Element node type, see {Nokogiri::XML::Node#element?}
|
50
|
+
ELEMENT_NODE = 1
|
60
51
|
# Attribute node type
|
61
|
-
ATTRIBUTE_NODE =
|
62
|
-
# Text node type, see Nokogiri::XML::Node#text?
|
63
|
-
TEXT_NODE =
|
64
|
-
# CDATA node type, see Nokogiri::XML::Node#cdata?
|
52
|
+
ATTRIBUTE_NODE = 2
|
53
|
+
# Text node type, see {Nokogiri::XML::Node#text?}
|
54
|
+
TEXT_NODE = 3
|
55
|
+
# CDATA node type, see {Nokogiri::XML::Node#cdata?}
|
65
56
|
CDATA_SECTION_NODE = 4
|
66
57
|
# Entity reference node type
|
67
|
-
ENTITY_REF_NODE =
|
58
|
+
ENTITY_REF_NODE = 5
|
68
59
|
# Entity node type
|
69
|
-
ENTITY_NODE =
|
60
|
+
ENTITY_NODE = 6
|
70
61
|
# PI node type
|
71
|
-
PI_NODE =
|
72
|
-
# Comment node type, see Nokogiri::XML::Node#comment?
|
73
|
-
COMMENT_NODE =
|
74
|
-
# Document node type, see Nokogiri::XML::Node#xml?
|
75
|
-
DOCUMENT_NODE =
|
62
|
+
PI_NODE = 7
|
63
|
+
# Comment node type, see {Nokogiri::XML::Node#comment?}
|
64
|
+
COMMENT_NODE = 8
|
65
|
+
# Document node type, see {Nokogiri::XML::Node#xml?}
|
66
|
+
DOCUMENT_NODE = 9
|
76
67
|
# Document type node type
|
77
68
|
DOCUMENT_TYPE_NODE = 10
|
78
69
|
# Document fragment node type
|
79
70
|
DOCUMENT_FRAG_NODE = 11
|
80
71
|
# Notation node type
|
81
|
-
NOTATION_NODE =
|
82
|
-
# HTML document node type, see Nokogiri::XML::Node#html?
|
72
|
+
NOTATION_NODE = 12
|
73
|
+
# HTML document node type, see {Nokogiri::XML::Node#html?}
|
83
74
|
HTML_DOCUMENT_NODE = 13
|
84
75
|
# DTD node type
|
85
|
-
DTD_NODE =
|
76
|
+
DTD_NODE = 14
|
86
77
|
# Element declaration type
|
87
|
-
ELEMENT_DECL =
|
78
|
+
ELEMENT_DECL = 15
|
88
79
|
# Attribute declaration type
|
89
|
-
ATTRIBUTE_DECL =
|
80
|
+
ATTRIBUTE_DECL = 16
|
90
81
|
# Entity declaration type
|
91
|
-
ENTITY_DECL =
|
82
|
+
ENTITY_DECL = 17
|
92
83
|
# Namespace declaration type
|
93
|
-
NAMESPACE_DECL =
|
84
|
+
NAMESPACE_DECL = 18
|
94
85
|
# XInclude start type
|
95
|
-
XINCLUDE_START =
|
86
|
+
XINCLUDE_START = 19
|
96
87
|
# XInclude end type
|
97
|
-
XINCLUDE_END =
|
88
|
+
XINCLUDE_END = 20
|
98
89
|
# DOCB document node type
|
99
90
|
DOCB_DOCUMENT_NODE = 21
|
100
91
|
|
101
|
-
|
102
|
-
|
92
|
+
##
|
93
|
+
# Create a new node with +name+ sharing GC lifecycle with +document+.
|
94
|
+
# @param name [String]
|
95
|
+
# @param document [Nokogiri::XML::Document]
|
96
|
+
# @yieldparam node [Nokogiri::XML::Node]
|
97
|
+
# @return [Nokogiri::XML::Node]
|
98
|
+
# @see Nokogiri::XML::Node.new
|
99
|
+
def initialize(name, document)
|
100
|
+
# This is intentionally empty.
|
103
101
|
end
|
104
102
|
|
105
103
|
###
|
@@ -108,24 +106,18 @@ module Nokogiri
|
|
108
106
|
document.decorate(self)
|
109
107
|
end
|
110
108
|
|
109
|
+
# @!group Searching via XPath or CSS Queries
|
110
|
+
|
111
111
|
###
|
112
112
|
# Search this node's immediate children using CSS selector +selector+
|
113
|
-
def >
|
113
|
+
def >(selector)
|
114
114
|
ns = document.root.namespaces
|
115
115
|
xpath CSS.xpath_for(selector, :prefix => "./", :ns => ns).first
|
116
116
|
end
|
117
117
|
|
118
|
-
|
119
|
-
# Get the attribute value for the attribute +name+
|
120
|
-
def [] name
|
121
|
-
get(name.to_s)
|
122
|
-
end
|
118
|
+
# @!endgroup
|
123
119
|
|
124
|
-
|
125
|
-
# Set the attribute value for the attribute +name+ to +value+
|
126
|
-
def []= name, value
|
127
|
-
set name.to_s, value.to_s
|
128
|
-
end
|
120
|
+
# @!group Manipulating Document Structure
|
129
121
|
|
130
122
|
###
|
131
123
|
# Add +node_or_tags+ as a child of this Node.
|
@@ -134,7 +126,7 @@ module Nokogiri
|
|
134
126
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
135
127
|
#
|
136
128
|
# Also see related method +<<+.
|
137
|
-
def add_child
|
129
|
+
def add_child(node_or_tags)
|
138
130
|
node_or_tags = coerce(node_or_tags)
|
139
131
|
if node_or_tags.is_a?(XML::NodeSet)
|
140
132
|
node_or_tags.each { |n| add_child_node_and_reparent_attrs n }
|
@@ -151,7 +143,7 @@ module Nokogiri
|
|
151
143
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
152
144
|
#
|
153
145
|
# Also see related method +add_child+.
|
154
|
-
def prepend_child
|
146
|
+
def prepend_child(node_or_tags)
|
155
147
|
if first = children.first
|
156
148
|
# Mimic the error add_child would raise.
|
157
149
|
raise RuntimeError, "Document already has a root node" if document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
@@ -161,7 +153,6 @@ module Nokogiri
|
|
161
153
|
end
|
162
154
|
end
|
163
155
|
|
164
|
-
|
165
156
|
###
|
166
157
|
# Add html around this node
|
167
158
|
#
|
@@ -180,7 +171,7 @@ module Nokogiri
|
|
180
171
|
# Returns self, to support chaining of calls (e.g., root << child1 << child2)
|
181
172
|
#
|
182
173
|
# Also see related method +add_child+.
|
183
|
-
def <<
|
174
|
+
def <<(node_or_tags)
|
184
175
|
add_child node_or_tags
|
185
176
|
self
|
186
177
|
end
|
@@ -192,7 +183,7 @@ module Nokogiri
|
|
192
183
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
193
184
|
#
|
194
185
|
# Also see related method +before+.
|
195
|
-
def add_previous_sibling
|
186
|
+
def add_previous_sibling(node_or_tags)
|
196
187
|
raise ArgumentError.new("A document may not have multiple root nodes.") if (parent && parent.document?) && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
197
188
|
|
198
189
|
add_sibling :previous, node_or_tags
|
@@ -205,7 +196,7 @@ module Nokogiri
|
|
205
196
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
206
197
|
#
|
207
198
|
# Also see related method +after+.
|
208
|
-
def add_next_sibling
|
199
|
+
def add_next_sibling(node_or_tags)
|
209
200
|
raise ArgumentError.new("A document may not have multiple root nodes.") if (parent && parent.document?) && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
210
201
|
|
211
202
|
add_sibling :next, node_or_tags
|
@@ -218,7 +209,7 @@ module Nokogiri
|
|
218
209
|
# Returns self, to support chaining of calls.
|
219
210
|
#
|
220
211
|
# Also see related method +add_previous_sibling+.
|
221
|
-
def before
|
212
|
+
def before(node_or_tags)
|
222
213
|
add_previous_sibling node_or_tags
|
223
214
|
self
|
224
215
|
end
|
@@ -230,7 +221,7 @@ module Nokogiri
|
|
230
221
|
# Returns self, to support chaining of calls.
|
231
222
|
#
|
232
223
|
# Also see related method +add_next_sibling+.
|
233
|
-
def after
|
224
|
+
def after(node_or_tags)
|
234
225
|
add_next_sibling node_or_tags
|
235
226
|
self
|
236
227
|
end
|
@@ -242,7 +233,7 @@ module Nokogiri
|
|
242
233
|
# Returns self.
|
243
234
|
#
|
244
235
|
# Also see related method +children=+
|
245
|
-
def inner_html=
|
236
|
+
def inner_html=(node_or_tags)
|
246
237
|
self.children = node_or_tags
|
247
238
|
self
|
248
239
|
end
|
@@ -254,7 +245,7 @@ module Nokogiri
|
|
254
245
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
255
246
|
#
|
256
247
|
# Also see related method +inner_html=+
|
257
|
-
def children=
|
248
|
+
def children=(node_or_tags)
|
258
249
|
node_or_tags = coerce(node_or_tags)
|
259
250
|
children.unlink
|
260
251
|
if node_or_tags.is_a?(XML::NodeSet)
|
@@ -272,19 +263,21 @@ module Nokogiri
|
|
272
263
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
273
264
|
#
|
274
265
|
# Also see related method +swap+.
|
275
|
-
def replace
|
266
|
+
def replace(node_or_tags)
|
267
|
+
raise("Cannot replace a node with no parent") unless parent
|
268
|
+
|
276
269
|
# We cannot replace a text node directly, otherwise libxml will return
|
277
270
|
# an internal error at parser.c:13031, I don't know exactly why
|
278
271
|
# libxml is trying to find a parent node that is an element or document
|
279
272
|
# so I can't tell if this is bug in libxml or not. issue #775.
|
280
273
|
if text?
|
281
|
-
replacee = Nokogiri::XML::Node.new
|
274
|
+
replacee = Nokogiri::XML::Node.new "dummy", document
|
282
275
|
add_previous_sibling_node replacee
|
283
276
|
unlink
|
284
277
|
return replacee.replace node_or_tags
|
285
278
|
end
|
286
279
|
|
287
|
-
node_or_tags = coerce(node_or_tags)
|
280
|
+
node_or_tags = parent.coerce(node_or_tags)
|
288
281
|
|
289
282
|
if node_or_tags.is_a?(XML::NodeSet)
|
290
283
|
node_or_tags.each { |n| add_previous_sibling n }
|
@@ -302,33 +295,98 @@ module Nokogiri
|
|
302
295
|
# Returns self, to support chaining of calls.
|
303
296
|
#
|
304
297
|
# Also see related method +replace+.
|
305
|
-
def swap
|
298
|
+
def swap(node_or_tags)
|
306
299
|
replace node_or_tags
|
307
300
|
self
|
308
301
|
end
|
309
302
|
|
310
|
-
|
311
|
-
|
303
|
+
####
|
304
|
+
# Set the Node's content to a Text node containing +string+. The string gets XML escaped, not interpreted as markup.
|
305
|
+
def content=(string)
|
306
|
+
self.native_content = encode_special_chars(string.to_s)
|
307
|
+
end
|
312
308
|
|
313
|
-
|
314
|
-
#
|
315
|
-
|
316
|
-
|
309
|
+
###
|
310
|
+
# Set the parent Node for this Node
|
311
|
+
def parent=(parent_node)
|
312
|
+
parent_node.add_child(self)
|
313
|
+
parent_node
|
314
|
+
end
|
317
315
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
316
|
+
###
|
317
|
+
# Adds a default namespace supplied as a string +url+ href, to self.
|
318
|
+
# The consequence is as an xmlns attribute with supplied argument were
|
319
|
+
# present in parsed XML. A default namespace set with this method will
|
320
|
+
# now show up in #attributes, but when this node is serialized to XML an
|
321
|
+
# "xmlns" attribute will appear. See also #namespace and #namespace=
|
322
|
+
def default_namespace=(url)
|
323
|
+
add_namespace_definition(nil, url)
|
324
|
+
end
|
325
|
+
|
326
|
+
###
|
327
|
+
# Set the default namespace on this node (as would be defined with an
|
328
|
+
# "xmlns=" attribute in XML source), as a Namespace object +ns+. Note that
|
329
|
+
# a Namespace added this way will NOT be serialized as an xmlns attribute
|
330
|
+
# for this node. You probably want #default_namespace= instead, or perhaps
|
331
|
+
# #add_namespace_definition with a nil prefix argument.
|
332
|
+
def namespace=(ns)
|
333
|
+
return set_namespace(ns) unless ns
|
334
|
+
|
335
|
+
unless Nokogiri::XML::Namespace === ns
|
336
|
+
raise TypeError, "#{ns.class} can't be coerced into Nokogiri::XML::Namespace"
|
337
|
+
end
|
338
|
+
if ns.document != document
|
339
|
+
raise ArgumentError, "namespace must be declared on the same document"
|
340
|
+
end
|
341
|
+
|
342
|
+
set_namespace ns
|
343
|
+
end
|
344
|
+
|
345
|
+
###
|
346
|
+
# Do xinclude substitution on the subtree below node. If given a block, a
|
347
|
+
# Nokogiri::XML::ParseOptions object initialized from +options+, will be
|
348
|
+
# passed to it, allowing more convenient modification of the parser options.
|
349
|
+
def do_xinclude(options = XML::ParseOptions::DEFAULT_XML)
|
350
|
+
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
351
|
+
|
352
|
+
# give options to user
|
353
|
+
yield options if block_given?
|
354
|
+
|
355
|
+
# call c extension
|
356
|
+
process_xincludes(options.to_i)
|
357
|
+
end
|
358
|
+
|
359
|
+
alias :next :next_sibling
|
360
|
+
alias :previous :previous_sibling
|
361
|
+
alias :next= :add_next_sibling
|
362
|
+
alias :previous= :add_previous_sibling
|
363
|
+
alias :remove :unlink
|
364
|
+
alias :name= :node_name=
|
365
|
+
alias :add_namespace :add_namespace_definition
|
366
|
+
|
367
|
+
# @!endgroup
|
368
|
+
|
369
|
+
alias :text :content
|
370
|
+
alias :inner_text :content
|
371
|
+
alias :name :node_name
|
372
|
+
alias :type :node_type
|
373
|
+
alias :to_str :text
|
374
|
+
alias :clone :dup
|
375
|
+
alias :elements :element_children
|
376
|
+
|
377
|
+
# @!group Working With Node Attributes
|
378
|
+
|
379
|
+
###
|
380
|
+
# Get the attribute value for the attribute +name+
|
381
|
+
def [](name)
|
382
|
+
get(name.to_s)
|
383
|
+
end
|
384
|
+
|
385
|
+
###
|
386
|
+
# Set the attribute value for the attribute +name+ to +value+
|
387
|
+
def []=(name, value)
|
388
|
+
set name.to_s, value.to_s
|
389
|
+
end
|
332
390
|
|
333
391
|
####
|
334
392
|
# Returns a hash containing the node's attributes. The key is
|
@@ -337,9 +395,9 @@ module Nokogiri
|
|
337
395
|
# If you need to distinguish attributes with the same name, with different namespaces
|
338
396
|
# use #attribute_nodes instead.
|
339
397
|
def attributes
|
340
|
-
|
341
|
-
[node.node_name
|
342
|
-
|
398
|
+
attribute_nodes.each_with_object({}) do |node, hash|
|
399
|
+
hash[node.node_name] = node
|
400
|
+
end
|
343
401
|
end
|
344
402
|
|
345
403
|
###
|
@@ -348,6 +406,12 @@ module Nokogiri
|
|
348
406
|
attribute_nodes.map(&:value)
|
349
407
|
end
|
350
408
|
|
409
|
+
###
|
410
|
+
# Does this Node's attributes include <value>
|
411
|
+
def value?(value)
|
412
|
+
values.include? value
|
413
|
+
end
|
414
|
+
|
351
415
|
###
|
352
416
|
# Get the attribute names for this Node.
|
353
417
|
def keys
|
@@ -363,82 +427,366 @@ module Nokogiri
|
|
363
427
|
end
|
364
428
|
|
365
429
|
###
|
366
|
-
#
|
367
|
-
|
430
|
+
# Remove the attribute named +name+
|
431
|
+
def remove_attribute(name)
|
432
|
+
attr = attributes[name].remove if key? name
|
433
|
+
clear_xpath_context if Nokogiri.jruby?
|
434
|
+
attr
|
435
|
+
end
|
436
|
+
|
437
|
+
# Get the CSS class names of a Node.
|
438
|
+
#
|
439
|
+
# This is a convenience function and is equivalent to:
|
440
|
+
# node.kwattr_values("class")
|
441
|
+
#
|
442
|
+
# @see #kwattr_values
|
443
|
+
# @see #add_class
|
444
|
+
# @see #append_class
|
445
|
+
# @see #remove_class
|
446
|
+
#
|
447
|
+
# @return [Array<String>]
|
448
|
+
#
|
449
|
+
# The CSS classes present in the Node's +class+ attribute. If
|
450
|
+
# the attribute is empty or non-existent, the return value is
|
451
|
+
# an empty array.
|
452
|
+
#
|
453
|
+
# @example
|
454
|
+
# node # => <div class="section title header"></div>
|
455
|
+
# node.classes # => ["section", "title", "header"]
|
456
|
+
#
|
368
457
|
def classes
|
369
|
-
|
458
|
+
kwattr_values("class")
|
370
459
|
end
|
371
460
|
|
372
|
-
|
373
|
-
#
|
374
|
-
#
|
375
|
-
#
|
376
|
-
#
|
461
|
+
# Ensure HTML CSS classes are present on a +Node+. Any CSS
|
462
|
+
# classes in +names+ that already exist in the +Node+'s +class+
|
463
|
+
# attribute are _not_ added. Note that any existing duplicates
|
464
|
+
# in the +class+ attribute are not removed. Compare with
|
465
|
+
# {#append_class}.
|
466
|
+
#
|
467
|
+
# This is a convenience function and is equivalent to:
|
468
|
+
# node.kwattr_add("class", names)
|
469
|
+
#
|
470
|
+
# @see #kwattr_add
|
471
|
+
# @see #classes
|
472
|
+
# @see #append_class
|
473
|
+
# @see #remove_class
|
474
|
+
#
|
475
|
+
# @param names [String, Array<String>]
|
476
|
+
#
|
477
|
+
# CSS class names to be added to the Node's +class+
|
478
|
+
# attribute. May be a string containing whitespace-delimited
|
479
|
+
# names, or an Array of String names. Any class names already
|
480
|
+
# present will not be added. Any class names not present will
|
481
|
+
# be added. If no +class+ attribute exists, one is created.
|
482
|
+
#
|
483
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
484
|
+
#
|
485
|
+
# @example Ensure that a +Node+ has CSS class "section"
|
486
|
+
# node # => <div></div>
|
487
|
+
# node.add_class("section") # => <div class="section"></div>
|
488
|
+
# node.add_class("section") # => <div class="section"></div> # duplicate not added
|
489
|
+
#
|
490
|
+
# @example Ensure that a +Node+ has CSS classes "section" and "header", via a String argument.
|
491
|
+
# node # => <div class="section section"></div>
|
492
|
+
# node.add_class("section header") # => <div class="section section header"></div>
|
493
|
+
# # Note that the CSS class "section" is not added because it is already present.
|
494
|
+
# # Note also that the pre-existing duplicate CSS class "section" is not removed.
|
495
|
+
#
|
496
|
+
# @example Ensure that a +Node+ has CSS classes "section" and "header", via an Array argument.
|
497
|
+
# node # => <div></div>
|
498
|
+
# node.add_class(["section", "header"]) # => <div class="section header"></div>
|
499
|
+
#
|
500
|
+
def add_class(names)
|
501
|
+
kwattr_add("class", names)
|
502
|
+
end
|
503
|
+
|
504
|
+
# Add HTML CSS classes to a +Node+, regardless of
|
505
|
+
# duplication. Compare with {#add_class}.
|
506
|
+
#
|
507
|
+
# This is a convenience function and is equivalent to:
|
508
|
+
# node.kwattr_append("class", names)
|
509
|
+
#
|
510
|
+
# @see #kwattr_append
|
511
|
+
# @see #classes
|
512
|
+
# @see #add_class
|
513
|
+
# @see #remove_class
|
514
|
+
#
|
515
|
+
# @param names [String, Array<String>]
|
516
|
+
#
|
517
|
+
# CSS class names to be appended to the Node's +class+
|
518
|
+
# attribute. May be a string containing whitespace-delimited
|
519
|
+
# names, or an Array of String names. All class names passed
|
520
|
+
# in will be appended to the +class+ attribute even if they
|
521
|
+
# are already present in the attribute value. If no +class+
|
522
|
+
# attribute exists, one is created.
|
523
|
+
#
|
524
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
525
|
+
#
|
526
|
+
# @example Append "section" to a +Node+'s CSS +class+ attriubute
|
527
|
+
# node # => <div></div>
|
528
|
+
# node.append_class("section") # => <div class="section"></div>
|
529
|
+
# node.append_class("section") # => <div class="section section"></div> # duplicate added!
|
530
|
+
#
|
531
|
+
# @example Append "section" and "header" to a +Node+'s CSS +class+ attribute, via a String argument.
|
532
|
+
# node # => <div class="section section"></div>
|
533
|
+
# node.append_class("section header") # => <div class="section section section header"></div>
|
534
|
+
# # Note that the CSS class "section" is appended even though it is already present.
|
377
535
|
#
|
378
|
-
#
|
379
|
-
#
|
380
|
-
|
381
|
-
|
382
|
-
|
536
|
+
# @example Append "section" and "header" to a +Node+'s CSS +class+ attribute, via an Array argument.
|
537
|
+
# node # => <div></div>
|
538
|
+
# node.append_class(["section", "header"]) # => <div class="section header"></div>
|
539
|
+
# node.append_class(["section", "header"]) # => <div class="section header section header"></div>
|
540
|
+
#
|
541
|
+
def append_class(names)
|
542
|
+
kwattr_append("class", names)
|
543
|
+
end
|
544
|
+
|
545
|
+
# Remove HTML CSS classes from a +Node+. Any CSS classes in +names+ that
|
546
|
+
# exist in the +Node+'s +class+ attribute are removed, including any
|
547
|
+
# multiple entries.
|
548
|
+
#
|
549
|
+
# If no CSS classes remain after this operation, or if +names+ is
|
550
|
+
# +nil+, the +class+ attribute is deleted from the node.
|
551
|
+
#
|
552
|
+
# This is a convenience function and is equivalent to:
|
553
|
+
# node.kwattr_remove("class", names)
|
554
|
+
#
|
555
|
+
# @see #kwattr_remove
|
556
|
+
# @see #classes
|
557
|
+
# @see #add_class
|
558
|
+
# @see #append_class
|
559
|
+
#
|
560
|
+
# @param names [String, Array<String>]
|
561
|
+
#
|
562
|
+
# CSS class names to be removed from the Node's +class+ attribute. May
|
563
|
+
# be a string containing whitespace-delimited names, or an Array of
|
564
|
+
# String names. Any class names already present will be removed. If no
|
565
|
+
# CSS classes remain, the +class+ attribute is deleted.
|
566
|
+
#
|
567
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
568
|
+
#
|
569
|
+
# @example
|
570
|
+
# node # => <div class="section header"></div>
|
571
|
+
# node.remove_class("section") # => <div class="header"></div>
|
572
|
+
# node.remove_class("header") # => <div></div> # attribute is deleted when empty
|
573
|
+
#
|
574
|
+
def remove_class(names = nil)
|
575
|
+
kwattr_remove("class", names)
|
576
|
+
end
|
577
|
+
|
578
|
+
# Retrieve values from a keyword attribute of a Node.
|
579
|
+
#
|
580
|
+
# A "keyword attribute" is a node attribute that contains a set
|
581
|
+
# of space-delimited values. Perhaps the most familiar example
|
582
|
+
# of this is the HTML +class+ attribute used to contain CSS
|
583
|
+
# classes. But other keyword attributes exist, for instance
|
584
|
+
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
585
|
+
#
|
586
|
+
# @see #classes
|
587
|
+
# @see #kwattr_add
|
588
|
+
# @see #kwattr_append
|
589
|
+
# @see #kwattr_remove
|
590
|
+
#
|
591
|
+
# @param attribute_name [String] The name of the keyword attribute to be inspected.
|
592
|
+
#
|
593
|
+
# @return [Array<String>]
|
594
|
+
#
|
595
|
+
# The values present in the Node's +attribute_name+
|
596
|
+
# attribute. If the attribute is empty or non-existent, the
|
597
|
+
# return value is an empty array.
|
598
|
+
#
|
599
|
+
# @example
|
600
|
+
# node # => <a rel="nofollow noopener external">link</a>
|
601
|
+
# node.kwattr_values("rel") # => ["nofollow", "noopener", "external"]
|
602
|
+
#
|
603
|
+
# @since v1.11.0
|
604
|
+
#
|
605
|
+
def kwattr_values(attribute_name)
|
606
|
+
keywordify(get_attribute(attribute_name) || [])
|
607
|
+
end
|
608
|
+
|
609
|
+
# Ensure that values are present in a keyword attribute.
|
610
|
+
#
|
611
|
+
# Any values in +keywords+ that already exist in the +Node+'s
|
612
|
+
# attribute values are _not_ added. Note that any existing
|
613
|
+
# duplicates in the attribute values are not removed. Compare
|
614
|
+
# with {#kwattr_append}.
|
615
|
+
#
|
616
|
+
# A "keyword attribute" is a node attribute that contains a set
|
617
|
+
# of space-delimited values. Perhaps the most familiar example
|
618
|
+
# of this is the HTML +class+ attribute used to contain CSS
|
619
|
+
# classes. But other keyword attributes exist, for instance
|
620
|
+
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
621
|
+
#
|
622
|
+
# @see #add_class
|
623
|
+
# @see #kwattr_values
|
624
|
+
# @see #kwattr_append
|
625
|
+
# @see #kwattr_remove
|
626
|
+
#
|
627
|
+
# @param attribute_name [String] The name of the keyword attribute to be modified.
|
628
|
+
#
|
629
|
+
# @param keywords [String, Array<String>]
|
630
|
+
#
|
631
|
+
# Keywords to be added to the attribute named
|
632
|
+
# +attribute_name+. May be a string containing
|
633
|
+
# whitespace-delimited values, or an Array of String
|
634
|
+
# values. Any values already present will not be added. Any
|
635
|
+
# values not present will be added. If the named attribute
|
636
|
+
# does not exist, it is created.
|
637
|
+
#
|
638
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
639
|
+
#
|
640
|
+
# @example Ensure that a +Node+ has "nofollow" in its +rel+ attribute.
|
641
|
+
# node # => <a></a>
|
642
|
+
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
|
643
|
+
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a> # duplicate not added
|
644
|
+
#
|
645
|
+
# @example Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via a String argument.
|
646
|
+
# node # => <a rel="nofollow nofollow"></a>
|
647
|
+
# node.kwattr_add("rel", "nofollow noreferrer") # => <a rel="nofollow nofollow noreferrer"></a>
|
648
|
+
# # Note that "nofollow" is not added because it is already present.
|
649
|
+
# # Note also that the pre-existing duplicate "nofollow" is not removed.
|
650
|
+
#
|
651
|
+
# @example Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via an Array argument.
|
652
|
+
# node # => <a></a>
|
653
|
+
# node.kwattr_add("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
|
654
|
+
#
|
655
|
+
# @since v1.11.0
|
656
|
+
#
|
657
|
+
def kwattr_add(attribute_name, keywords)
|
658
|
+
keywords = keywordify(keywords)
|
659
|
+
current_kws = kwattr_values(attribute_name)
|
660
|
+
new_kws = (current_kws + (keywords - current_kws)).join(" ")
|
661
|
+
set_attribute(attribute_name, new_kws)
|
383
662
|
self
|
384
663
|
end
|
385
664
|
|
386
|
-
|
387
|
-
#
|
388
|
-
#
|
389
|
-
#
|
390
|
-
#
|
665
|
+
# Add keywords to a Node's keyword attribute, regardless of
|
666
|
+
# duplication. Compare with {#kwattr_add}.
|
667
|
+
#
|
668
|
+
# A "keyword attribute" is a node attribute that contains a set
|
669
|
+
# of space-delimited values. Perhaps the most familiar example
|
670
|
+
# of this is the HTML +class+ attribute used to contain CSS
|
671
|
+
# classes. But other keyword attributes exist, for instance
|
672
|
+
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
391
673
|
#
|
392
|
-
#
|
393
|
-
#
|
394
|
-
|
395
|
-
|
674
|
+
# @see #append_class
|
675
|
+
# @see #kwattr_values
|
676
|
+
# @see #kwattr_add
|
677
|
+
# @see #kwattr_remove
|
678
|
+
#
|
679
|
+
# @param attribute_name [String] The name of the keyword attribute to be modified.
|
680
|
+
#
|
681
|
+
# @param keywords [String, Array<String>]
|
682
|
+
#
|
683
|
+
# Keywords to be added to the attribute named
|
684
|
+
# +attribute_name+. May be a string containing
|
685
|
+
# whitespace-delimited values, or an Array of String
|
686
|
+
# values. All values passed in will be appended to the named
|
687
|
+
# attribute even if they are already present in the
|
688
|
+
# attribute. If the named attribute does not exist, it is
|
689
|
+
# created.
|
690
|
+
#
|
691
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
692
|
+
#
|
693
|
+
# @example Append "nofollow" to the +rel+ attribute.
|
694
|
+
# node # => <a></a>
|
695
|
+
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow"></a>
|
696
|
+
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow nofollow"></a> # duplicate added!
|
697
|
+
#
|
698
|
+
# @example Append "nofollow" and "noreferrer" to the +rel+ attribute, via a String argument.
|
699
|
+
# node # => <a rel="nofollow"></a>
|
700
|
+
# node.kwattr_append("rel", "nofollow noreferrer") # => <a rel="nofollow nofollow noreferrer"></a>
|
701
|
+
# # Note that "nofollow" is appended even though it is already present.
|
702
|
+
#
|
703
|
+
# @example Append "nofollow" and "noreferrer" to the +rel+ attribute, via an Array argument.
|
704
|
+
# node # => <a></a>
|
705
|
+
# node.kwattr_append("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
|
706
|
+
#
|
707
|
+
# @since v1.11.0
|
708
|
+
#
|
709
|
+
def kwattr_append(attribute_name, keywords)
|
710
|
+
keywords = keywordify(keywords)
|
711
|
+
current_kws = kwattr_values(attribute_name)
|
712
|
+
new_kws = (current_kws + keywords).join(" ")
|
713
|
+
set_attribute(attribute_name, new_kws)
|
396
714
|
self
|
397
715
|
end
|
398
716
|
|
399
|
-
|
400
|
-
#
|
401
|
-
#
|
402
|
-
# they are all removed.
|
717
|
+
# Remove keywords from a keyword attribute. Any matching
|
718
|
+
# keywords that exist in the named attribute are removed,
|
719
|
+
# including any multiple entries.
|
403
720
|
#
|
404
|
-
#
|
405
|
-
#
|
721
|
+
# If no keywords remain after this operation, or if +keywords+
|
722
|
+
# is +nil+, the attribute is deleted from the node.
|
406
723
|
#
|
407
|
-
#
|
408
|
-
#
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
724
|
+
# A "keyword attribute" is a node attribute that contains a set
|
725
|
+
# of space-delimited values. Perhaps the most familiar example
|
726
|
+
# of this is the HTML +class+ attribute used to contain CSS
|
727
|
+
# classes. But other keyword attributes exist, for instance
|
728
|
+
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
729
|
+
#
|
730
|
+
# @see #remove_class
|
731
|
+
# @see #kwattr_values
|
732
|
+
# @see #kwattr_add
|
733
|
+
# @see #kwattr_append
|
734
|
+
#
|
735
|
+
# @param attribute_name [String] The name of the keyword attribute to be modified.
|
736
|
+
#
|
737
|
+
# @param keywords [String, Array<String>]
|
738
|
+
#
|
739
|
+
# Keywords to be removed from the attribute named
|
740
|
+
# +attribute_name+. May be a string containing
|
741
|
+
# whitespace-delimited values, or an Array of String
|
742
|
+
# values. Any keywords present in the named attribute will be
|
743
|
+
# removed. If no keywords remain, or if +keywords+ is nil, the
|
744
|
+
# attribute is deleted.
|
745
|
+
#
|
746
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
747
|
+
#
|
748
|
+
# @example
|
749
|
+
# node # => <a rel="nofollow noreferrer">link</a>
|
750
|
+
# node.kwattr_remove("rel", "nofollow") # => <a rel="noreferrer">link</a>
|
751
|
+
# node.kwattr_remove("rel", "noreferrer") # => <a>link</a> # attribute is deleted when empty
|
752
|
+
#
|
753
|
+
# @since v1.11.0
|
754
|
+
#
|
755
|
+
def kwattr_remove(attribute_name, keywords)
|
756
|
+
if keywords.nil?
|
757
|
+
remove_attribute(attribute_name)
|
758
|
+
return self
|
759
|
+
end
|
760
|
+
|
761
|
+
keywords = keywordify(keywords)
|
762
|
+
current_kws = kwattr_values(attribute_name)
|
763
|
+
new_kws = current_kws - keywords
|
764
|
+
if new_kws.empty?
|
765
|
+
remove_attribute(attribute_name)
|
417
766
|
else
|
418
|
-
|
767
|
+
set_attribute(attribute_name, new_kws.join(" "))
|
419
768
|
end
|
420
769
|
self
|
421
770
|
end
|
422
771
|
|
423
|
-
###
|
424
|
-
# Remove the attribute named +name+
|
425
|
-
def remove_attribute name
|
426
|
-
attr = attributes[name].remove if key? name
|
427
|
-
clear_xpath_context if Nokogiri.jruby?
|
428
|
-
attr
|
429
|
-
end
|
430
772
|
alias :delete :remove_attribute
|
773
|
+
alias :get_attribute :[]
|
774
|
+
alias :attr :[]
|
775
|
+
alias :set_attribute :[]=
|
776
|
+
alias :has_attribute? :key?
|
777
|
+
|
778
|
+
# @!endgroup
|
431
779
|
|
432
780
|
###
|
433
781
|
# Returns true if this Node matches +selector+
|
434
|
-
def matches?
|
782
|
+
def matches?(selector)
|
435
783
|
ancestors.last.search(selector).include?(self)
|
436
784
|
end
|
437
785
|
|
438
786
|
###
|
439
787
|
# Create a DocumentFragment containing +tags+ that is relative to _this_
|
440
788
|
# context node.
|
441
|
-
def fragment
|
789
|
+
def fragment(tags)
|
442
790
|
type = document.html? ? Nokogiri::HTML : Nokogiri::XML
|
443
791
|
type::DocumentFragment.new(document, tags, self)
|
444
792
|
end
|
@@ -447,7 +795,7 @@ module Nokogiri
|
|
447
795
|
# Parse +string_or_io+ as a document fragment within the context of
|
448
796
|
# *this* node. Returns a XML::NodeSet containing the nodes parsed from
|
449
797
|
# +string_or_io+.
|
450
|
-
def parse
|
798
|
+
def parse(string_or_io, options = nil)
|
451
799
|
##
|
452
800
|
# When the current node is unparented and not an element node, use the
|
453
801
|
# document as the parsing context instead. Otherwise, the in-context
|
@@ -470,30 +818,34 @@ module Nokogiri
|
|
470
818
|
|
471
819
|
return Nokogiri::XML::NodeSet.new(document) if contents.empty?
|
472
820
|
|
473
|
-
|
474
|
-
#
|
821
|
+
# libxml2 does not obey the `recover` option after encountering errors during `in_context`
|
822
|
+
# parsing, and so this horrible hack is here to try to emulate recovery behavior.
|
823
|
+
#
|
824
|
+
# Unfortunately, this means we're no longer parsing "in context" and so namespaces that
|
825
|
+
# would have been inherited from the context node won't be handled correctly. This hack was
|
826
|
+
# written in 2010, and I regret it, because it's silently degrading functionality in a way
|
827
|
+
# that's not easily prevented (or even detected).
|
828
|
+
#
|
829
|
+
# I think preferable behavior would be to either:
|
830
|
+
#
|
831
|
+
# a. add an error noting that we "fell back" and pointing the user to turning off the `recover` option
|
832
|
+
# b. don't recover, but raise a sensible exception
|
833
|
+
#
|
834
|
+
# For context and background: https://github.com/sparklemotion/nokogiri/issues/313
|
835
|
+
# FIXME bug report: https://github.com/sparklemotion/nokogiri/issues/2092
|
475
836
|
error_count = document.errors.length
|
476
837
|
node_set = in_context(contents, options.to_i)
|
477
|
-
if node_set.empty?
|
478
|
-
|
479
|
-
|
838
|
+
if (node_set.empty? && (document.errors.length > error_count))
|
839
|
+
if options.recover?
|
840
|
+
fragment = Nokogiri::HTML::DocumentFragment.parse contents
|
841
|
+
node_set = fragment.children
|
842
|
+
else
|
843
|
+
raise document.errors[error_count]
|
844
|
+
end
|
480
845
|
end
|
481
846
|
node_set
|
482
847
|
end
|
483
848
|
|
484
|
-
####
|
485
|
-
# Set the Node's content to a Text node containing +string+. The string gets XML escaped, not interpreted as markup.
|
486
|
-
def content= string
|
487
|
-
self.native_content = encode_special_chars(string.to_s)
|
488
|
-
end
|
489
|
-
|
490
|
-
###
|
491
|
-
# Set the parent Node for this Node
|
492
|
-
def parent= parent_node
|
493
|
-
parent_node.add_child(self)
|
494
|
-
parent_node
|
495
|
-
end
|
496
|
-
|
497
849
|
###
|
498
850
|
# Returns a Hash of +{prefix => value}+ for all namespaces on this
|
499
851
|
# node and its ancestors.
|
@@ -509,10 +861,11 @@ module Nokogiri
|
|
509
861
|
# default namespaces set on ancestor will NOT be, even if self
|
510
862
|
# has no explicit default namespace.
|
511
863
|
def namespaces
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
864
|
+
namespace_scopes.each_with_object({}) do |ns, hash|
|
865
|
+
prefix = ns.prefix
|
866
|
+
key = prefix ? "xmlns:#{prefix}" : "xmlns"
|
867
|
+
hash[key] = ns.href
|
868
|
+
end
|
516
869
|
end
|
517
870
|
|
518
871
|
# Returns true if this is a Comment
|
@@ -574,6 +927,7 @@ module Nokogiri
|
|
574
927
|
def element?
|
575
928
|
type == ELEMENT_NODE
|
576
929
|
end
|
930
|
+
|
577
931
|
alias :elem? :element?
|
578
932
|
|
579
933
|
###
|
@@ -584,7 +938,7 @@ module Nokogiri
|
|
584
938
|
end
|
585
939
|
|
586
940
|
# Get the inner_html for this node's Node#children
|
587
|
-
def inner_html
|
941
|
+
def inner_html(*args)
|
588
942
|
children.map { |x| x.to_html(*args) }.join
|
589
943
|
end
|
590
944
|
|
@@ -592,13 +946,13 @@ module Nokogiri
|
|
592
946
|
def css_path
|
593
947
|
path.split(/\//).map { |part|
|
594
948
|
part.length == 0 ? nil : part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
|
595
|
-
}.compact.join(
|
949
|
+
}.compact.join(" > ")
|
596
950
|
end
|
597
951
|
|
598
952
|
###
|
599
953
|
# Get a list of ancestor Node for this Node. If +selector+ is given,
|
600
954
|
# the ancestors must match +selector+
|
601
|
-
def ancestors
|
955
|
+
def ancestors(selector = nil)
|
602
956
|
return NodeSet.new(document) unless respond_to?(:parent)
|
603
957
|
return NodeSet.new(document) unless parent
|
604
958
|
|
@@ -619,57 +973,38 @@ module Nokogiri
|
|
619
973
|
})
|
620
974
|
end
|
621
975
|
|
622
|
-
###
|
623
|
-
# Adds a default namespace supplied as a string +url+ href, to self.
|
624
|
-
# The consequence is as an xmlns attribute with supplied argument were
|
625
|
-
# present in parsed XML. A default namespace set with this method will
|
626
|
-
# now show up in #attributes, but when this node is serialized to XML an
|
627
|
-
# "xmlns" attribute will appear. See also #namespace and #namespace=
|
628
|
-
def default_namespace= url
|
629
|
-
add_namespace_definition(nil, url)
|
630
|
-
end
|
631
|
-
alias :add_namespace :add_namespace_definition
|
632
|
-
|
633
|
-
###
|
634
|
-
# Set the default namespace on this node (as would be defined with an
|
635
|
-
# "xmlns=" attribute in XML source), as a Namespace object +ns+. Note that
|
636
|
-
# a Namespace added this way will NOT be serialized as an xmlns attribute
|
637
|
-
# for this node. You probably want #default_namespace= instead, or perhaps
|
638
|
-
# #add_namespace_definition with a nil prefix argument.
|
639
|
-
def namespace= ns
|
640
|
-
return set_namespace(ns) unless ns
|
641
|
-
|
642
|
-
unless Nokogiri::XML::Namespace === ns
|
643
|
-
raise TypeError, "#{ns.class} can't be coerced into Nokogiri::XML::Namespace"
|
644
|
-
end
|
645
|
-
if ns.document != document
|
646
|
-
raise ArgumentError, 'namespace must be declared on the same document'
|
647
|
-
end
|
648
|
-
|
649
|
-
set_namespace ns
|
650
|
-
end
|
651
|
-
|
652
976
|
####
|
653
977
|
# Yields self and all children to +block+ recursively.
|
654
|
-
def traverse
|
655
|
-
children.each{|j| j.traverse(&block) }
|
978
|
+
def traverse(&block)
|
979
|
+
children.each { |j| j.traverse(&block) }
|
656
980
|
block.call(self)
|
657
981
|
end
|
658
982
|
|
659
983
|
###
|
660
984
|
# Accept a visitor. This method calls "visit" on +visitor+ with self.
|
661
|
-
def accept
|
985
|
+
def accept(visitor)
|
662
986
|
visitor.visit(self)
|
663
987
|
end
|
664
988
|
|
665
989
|
###
|
666
990
|
# Test to see if this Node is equal to +other+
|
667
|
-
def ==
|
991
|
+
def ==(other)
|
668
992
|
return false unless other
|
669
993
|
return false unless other.respond_to?(:pointer_id)
|
670
994
|
pointer_id == other.pointer_id
|
671
995
|
end
|
672
996
|
|
997
|
+
###
|
998
|
+
# Compare two Node objects with respect to their Document. Nodes from
|
999
|
+
# different documents cannot be compared.
|
1000
|
+
def <=>(other)
|
1001
|
+
return nil unless other.is_a?(Nokogiri::XML::Node)
|
1002
|
+
return nil unless document == other.document
|
1003
|
+
compare other
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
# @!group Serialization and Generating Output
|
1007
|
+
|
673
1008
|
###
|
674
1009
|
# Serialize Node using +options+. Save options can also be set using a
|
675
1010
|
# block. See SaveOptions.
|
@@ -684,17 +1019,17 @@ module Nokogiri
|
|
684
1019
|
# config.format.as_xml
|
685
1020
|
# end
|
686
1021
|
#
|
687
|
-
def serialize
|
1022
|
+
def serialize(*args, &block)
|
688
1023
|
options = args.first.is_a?(Hash) ? args.shift : {
|
689
|
-
:encoding
|
690
|
-
:save_with
|
1024
|
+
:encoding => args[0],
|
1025
|
+
:save_with => args[1],
|
691
1026
|
}
|
692
1027
|
|
693
1028
|
encoding = options[:encoding] || document.encoding
|
694
1029
|
options[:encoding] = encoding
|
695
1030
|
|
696
1031
|
outstring = String.new
|
697
|
-
outstring.force_encoding(Encoding.find(encoding ||
|
1032
|
+
outstring.force_encoding(Encoding.find(encoding || "utf-8"))
|
698
1033
|
io = StringIO.new(outstring)
|
699
1034
|
write_to io, options, &block
|
700
1035
|
io.string
|
@@ -707,7 +1042,7 @@ module Nokogiri
|
|
707
1042
|
#
|
708
1043
|
# See Node#write_to for a list of +options+. For formatted output,
|
709
1044
|
# use Node#to_xhtml instead.
|
710
|
-
def to_html
|
1045
|
+
def to_html(options = {})
|
711
1046
|
to_format SaveOptions::DEFAULT_HTML, options
|
712
1047
|
end
|
713
1048
|
|
@@ -717,7 +1052,7 @@ module Nokogiri
|
|
717
1052
|
# doc.to_xml(:indent => 5, :encoding => 'UTF-8')
|
718
1053
|
#
|
719
1054
|
# See Node#write_to for a list of +options+
|
720
|
-
def to_xml
|
1055
|
+
def to_xml(options = {})
|
721
1056
|
options[:save_with] ||= SaveOptions::DEFAULT_XML
|
722
1057
|
serialize(options)
|
723
1058
|
end
|
@@ -728,7 +1063,7 @@ module Nokogiri
|
|
728
1063
|
# doc.to_xhtml(:indent => 5, :encoding => 'UTF-8')
|
729
1064
|
#
|
730
1065
|
# See Node#write_to for a list of +options+
|
731
|
-
def to_xhtml
|
1066
|
+
def to_xhtml(options = {})
|
732
1067
|
to_format SaveOptions::DEFAULT_XHTML, options
|
733
1068
|
end
|
734
1069
|
|
@@ -749,29 +1084,34 @@ module Nokogiri
|
|
749
1084
|
#
|
750
1085
|
# node.write_to(io, :indent_text => '-', :indent => 2)
|
751
1086
|
#
|
752
|
-
def write_to
|
753
|
-
options
|
754
|
-
encoding
|
1087
|
+
def write_to(io, *options)
|
1088
|
+
options = options.first.is_a?(Hash) ? options.shift : {}
|
1089
|
+
encoding = options[:encoding] || options[0]
|
755
1090
|
if Nokogiri.jruby?
|
756
|
-
save_options
|
757
|
-
indent_times
|
1091
|
+
save_options = options[:save_with] || options[1]
|
1092
|
+
indent_times = options[:indent] || 0
|
758
1093
|
else
|
759
|
-
save_options
|
760
|
-
indent_times
|
1094
|
+
save_options = options[:save_with] || options[1] || SaveOptions::FORMAT
|
1095
|
+
indent_times = options[:indent] || 2
|
761
1096
|
end
|
762
|
-
indent_text
|
1097
|
+
indent_text = options[:indent_text] || " "
|
1098
|
+
|
1099
|
+
# Any string times 0 returns an empty string. Therefore, use the same
|
1100
|
+
# string instead of generating a new empty string for every node with
|
1101
|
+
# zero indentation.
|
1102
|
+
indentation = indent_times.zero? ? "" : (indent_text * indent_times)
|
763
1103
|
|
764
1104
|
config = SaveOptions.new(save_options.to_i)
|
765
1105
|
yield config if block_given?
|
766
1106
|
|
767
|
-
native_write_to(io, encoding,
|
1107
|
+
native_write_to(io, encoding, indentation, config.options)
|
768
1108
|
end
|
769
1109
|
|
770
1110
|
###
|
771
1111
|
# Write Node as HTML to +io+ with +options+
|
772
1112
|
#
|
773
1113
|
# See Node#write_to for a list of +options+
|
774
|
-
def write_html_to
|
1114
|
+
def write_html_to(io, options = {})
|
775
1115
|
write_format_to SaveOptions::DEFAULT_HTML, io, options
|
776
1116
|
end
|
777
1117
|
|
@@ -779,7 +1119,7 @@ module Nokogiri
|
|
779
1119
|
# Write Node as XHTML to +io+ with +options+
|
780
1120
|
#
|
781
1121
|
# See Node#write_to for a list of +options+
|
782
|
-
def write_xhtml_to
|
1122
|
+
def write_xhtml_to(io, options = {})
|
783
1123
|
write_format_to SaveOptions::DEFAULT_XHTML, io, options
|
784
1124
|
end
|
785
1125
|
|
@@ -789,52 +1129,66 @@ module Nokogiri
|
|
789
1129
|
# doc.write_xml_to io, :encoding => 'UTF-8'
|
790
1130
|
#
|
791
1131
|
# See Node#write_to for a list of options
|
792
|
-
def write_xml_to
|
1132
|
+
def write_xml_to(io, options = {})
|
793
1133
|
options[:save_with] ||= SaveOptions::DEFAULT_XML
|
794
1134
|
write_to io, options
|
795
1135
|
end
|
796
1136
|
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
compare other
|
1137
|
+
def canonicalize(mode = XML::XML_C14N_1_0, inclusive_namespaces = nil, with_comments = false)
|
1138
|
+
c14n_root = self
|
1139
|
+
document.canonicalize(mode, inclusive_namespaces, with_comments) do |node, parent|
|
1140
|
+
tn = node.is_a?(XML::Node) ? node : parent
|
1141
|
+
tn == c14n_root || tn.ancestors.include?(c14n_root)
|
1142
|
+
end
|
804
1143
|
end
|
805
1144
|
|
806
|
-
|
807
|
-
# Do xinclude substitution on the subtree below node. If given a block, a
|
808
|
-
# Nokogiri::XML::ParseOptions object initialized from +options+, will be
|
809
|
-
# passed to it, allowing more convenient modification of the parser options.
|
810
|
-
def do_xinclude options = XML::ParseOptions::DEFAULT_XML
|
811
|
-
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
1145
|
+
# @!endgroup
|
812
1146
|
|
813
|
-
|
814
|
-
yield options if block_given?
|
1147
|
+
protected
|
815
1148
|
|
816
|
-
|
817
|
-
|
1149
|
+
def coerce(data)
|
1150
|
+
case data
|
1151
|
+
when XML::NodeSet
|
1152
|
+
return data
|
1153
|
+
when XML::DocumentFragment
|
1154
|
+
return data.children
|
1155
|
+
when String
|
1156
|
+
return fragment(data).children
|
1157
|
+
when Document, XML::Attr
|
1158
|
+
# unacceptable
|
1159
|
+
when XML::Node
|
1160
|
+
return data
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
raise ArgumentError, <<-EOERR
|
1164
|
+
Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
1165
|
+
(You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
|
1166
|
+
EOERR
|
818
1167
|
end
|
819
1168
|
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
1169
|
+
private
|
1170
|
+
|
1171
|
+
def keywordify(keywords)
|
1172
|
+
case keywords
|
1173
|
+
when Enumerable
|
1174
|
+
return keywords
|
1175
|
+
when String
|
1176
|
+
return keywords.scan(/\S+/)
|
1177
|
+
else
|
1178
|
+
raise ArgumentError.new("Keyword attributes must be passed as either a String or an Enumerable, but received #{keywords.class}")
|
825
1179
|
end
|
826
1180
|
end
|
827
1181
|
|
828
|
-
|
1182
|
+
def add_sibling(next_or_previous, node_or_tags)
|
1183
|
+
raise("Cannot add sibling to a node with no parent") unless parent
|
829
1184
|
|
830
|
-
def add_sibling next_or_previous, node_or_tags
|
831
1185
|
impl = (next_or_previous == :next) ? :add_next_sibling_node : :add_previous_sibling_node
|
832
|
-
iter = (next_or_previous == :next) ? :reverse_each
|
1186
|
+
iter = (next_or_previous == :next) ? :reverse_each : :each
|
833
1187
|
|
834
|
-
node_or_tags = coerce
|
1188
|
+
node_or_tags = parent.coerce(node_or_tags)
|
835
1189
|
if node_or_tags.is_a?(XML::NodeSet)
|
836
1190
|
if text?
|
837
|
-
pivot = Nokogiri::XML::Node.new
|
1191
|
+
pivot = Nokogiri::XML::Node.new "dummy", document
|
838
1192
|
send impl, pivot
|
839
1193
|
else
|
840
1194
|
pivot = self
|
@@ -847,17 +1201,18 @@ module Nokogiri
|
|
847
1201
|
node_or_tags
|
848
1202
|
end
|
849
1203
|
|
850
|
-
|
851
|
-
|
852
|
-
|
1204
|
+
USING_LIBXML_WITH_BROKEN_SERIALIZATION = Nokogiri.uses_libxml?("~> 2.6.0").freeze
|
1205
|
+
private_constant :USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
1206
|
+
|
1207
|
+
def to_format(save_option, options)
|
1208
|
+
return dump_html if USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
853
1209
|
|
854
1210
|
options[:save_with] = save_option unless options[:save_with]
|
855
1211
|
serialize(options)
|
856
1212
|
end
|
857
1213
|
|
858
|
-
def write_format_to
|
859
|
-
|
860
|
-
return (io << dump_html) if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
|
1214
|
+
def write_format_to(save_option, io, options)
|
1215
|
+
return (io << dump_html) if USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
861
1216
|
|
862
1217
|
options[:save_with] ||= save_option
|
863
1218
|
write_to io, options
|
@@ -867,30 +1222,10 @@ module Nokogiri
|
|
867
1222
|
[:name, :namespace, :attribute_nodes, :children]
|
868
1223
|
end
|
869
1224
|
|
870
|
-
def coerce data # :nodoc:
|
871
|
-
case data
|
872
|
-
when XML::NodeSet
|
873
|
-
return data
|
874
|
-
when XML::DocumentFragment
|
875
|
-
return data.children
|
876
|
-
when String
|
877
|
-
return fragment(data).children
|
878
|
-
when Document, XML::Attr
|
879
|
-
# unacceptable
|
880
|
-
when XML::Node
|
881
|
-
return data
|
882
|
-
end
|
883
|
-
|
884
|
-
raise ArgumentError, <<-EOERR
|
885
|
-
Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
886
|
-
(You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
|
887
|
-
EOERR
|
888
|
-
end
|
889
|
-
|
890
1225
|
# @private
|
891
|
-
IMPLIED_XPATH_CONTEXTS = [
|
1226
|
+
IMPLIED_XPATH_CONTEXTS = [".//".freeze].freeze
|
892
1227
|
|
893
|
-
def add_child_node_and_reparent_attrs
|
1228
|
+
def add_child_node_and_reparent_attrs(node)
|
894
1229
|
add_child_node node
|
895
1230
|
node.attribute_nodes.find_all { |a| a.name =~ /:/ }.each do |attr_node|
|
896
1231
|
attr_node.remove
|