nokogiri 1.10.8-java → 1.11.0.rc3-java
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/README.md +24 -22
- data/ext/java/nokogiri/HtmlDocument.java +34 -46
- data/ext/java/nokogiri/HtmlSaxParserContext.java +87 -57
- data/ext/java/nokogiri/NokogiriService.java +1 -1
- data/ext/java/nokogiri/XmlAttr.java +13 -20
- data/ext/java/nokogiri/XmlAttributeDecl.java +11 -12
- data/ext/java/nokogiri/XmlCdata.java +3 -4
- data/ext/java/nokogiri/XmlComment.java +1 -1
- data/ext/java/nokogiri/XmlDocument.java +148 -175
- data/ext/java/nokogiri/XmlDocumentFragment.java +13 -31
- data/ext/java/nokogiri/XmlDtd.java +5 -8
- data/ext/java/nokogiri/XmlElement.java +1 -20
- data/ext/java/nokogiri/XmlElementDecl.java +23 -28
- data/ext/java/nokogiri/XmlEntityDecl.java +23 -27
- data/ext/java/nokogiri/XmlEntityReference.java +2 -2
- data/ext/java/nokogiri/XmlNamespace.java +72 -89
- data/ext/java/nokogiri/XmlNode.java +300 -401
- data/ext/java/nokogiri/XmlNodeSet.java +72 -77
- data/ext/java/nokogiri/XmlReader.java +10 -11
- data/ext/java/nokogiri/XmlSaxParserContext.java +7 -7
- data/ext/java/nokogiri/XmlSchema.java +3 -3
- data/ext/java/nokogiri/XmlText.java +12 -9
- data/ext/java/nokogiri/XmlXpathContext.java +7 -7
- data/ext/java/nokogiri/XsltStylesheet.java +7 -15
- data/ext/java/nokogiri/internals/HtmlDomParserContext.java +4 -10
- data/ext/java/nokogiri/internals/NokogiriHelpers.java +71 -135
- data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +90 -58
- data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +5 -4
- data/ext/java/nokogiri/internals/ParserContext.java +27 -73
- data/ext/java/nokogiri/internals/ReaderNode.java +2 -4
- data/ext/java/nokogiri/internals/XmlDomParserContext.java +17 -32
- data/ext/nokogiri/extconf.rb +50 -37
- data/ext/nokogiri/nokogiri.c +12 -6
- data/ext/nokogiri/nokogiri.h +13 -0
- data/ext/nokogiri/xml_document.c +16 -2
- data/ext/nokogiri/xml_io.c +8 -6
- data/ext/nokogiri/xml_node.c +20 -0
- data/ext/nokogiri/xml_reader.c +6 -17
- data/ext/nokogiri/xml_schema.c +29 -0
- data/ext/nokogiri/xslt_stylesheet.c +0 -4
- data/lib/nokogiri.rb +3 -20
- data/lib/nokogiri/css.rb +1 -0
- data/lib/nokogiri/css/node.rb +1 -0
- data/lib/nokogiri/css/parser.rb +61 -60
- 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 +3 -1
- data/lib/nokogiri/decorators/slop.rb +1 -0
- data/lib/nokogiri/html.rb +1 -0
- data/lib/nokogiri/html/builder.rb +1 -0
- data/lib/nokogiri/html/document.rb +1 -0
- data/lib/nokogiri/html/document_fragment.rb +1 -0
- 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/nokogiri.jar +0 -0
- data/lib/nokogiri/syntax_error.rb +1 -0
- data/lib/nokogiri/version.rb +86 -45
- 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 +3 -8
- data/lib/nokogiri/xml/document_fragment.rb +1 -0
- 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 +539 -224
- 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 +4 -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 +7 -3
- data/lib/nokogiri/xml/relax_ng.rb +1 -0
- 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 +1 -0
- data/lib/nokogiri/xml/searchable.rb +22 -15
- data/lib/nokogiri/xml/syntax_error.rb +1 -0
- data/lib/nokogiri/xml/text.rb +1 -0
- data/lib/nokogiri/xml/xpath.rb +1 -0
- data/lib/nokogiri/xml/xpath/syntax_error.rb +1 -0
- 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
- metadata +53 -34
- data/ext/java/nokogiri/internals/NokogiriEncodingReaderWrapper.java +0 -107
- data/ext/java/nokogiri/internals/UncloseableInputStream.java +0 -102
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Nokogiri
|
2
3
|
module CSS
|
3
4
|
class XPathVisitor # :nodoc:
|
@@ -51,7 +52,8 @@ module Nokogiri
|
|
51
52
|
when /^comment\(/
|
52
53
|
"comment()"
|
53
54
|
when /^has\(/
|
54
|
-
|
55
|
+
is_direct = node.value[1].value[0].nil? # e.g. "has(> a)", "has(~ a)", "has(+ a)"
|
56
|
+
".#{"//" if !is_direct}#{node.value[1].accept(self)}"
|
55
57
|
else
|
56
58
|
args = ['.'] + node.value[1..-1]
|
57
59
|
"#{node.value.first}#{args.join(', ')})"
|
data/lib/nokogiri/html.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# The line below caused a problem on non-GAE rack environment.
|
3
|
+
# unless defined?(JRuby::Rack::VERSION) || defined?(AppEngine::ApiProxy)
|
4
|
+
#
|
5
|
+
# However, simply cutting defined?(JRuby::Rack::VERSION) off resulted in
|
6
|
+
# an unable-to-load-nokogiri problem. Thus, now, Nokogiri checks the presense
|
7
|
+
# of appengine-rack.jar in $LOAD_PATH. If Nokogiri is on GAE, Nokogiri
|
8
|
+
# should skip loading xml jars. This is because those are in WEB-INF/lib and
|
9
|
+
# already set in the classpath.
|
10
|
+
unless $LOAD_PATH.to_s.include?("appengine-rack")
|
11
|
+
require 'stringio'
|
12
|
+
require 'isorelax.jar'
|
13
|
+
require 'jing.jar'
|
14
|
+
require 'nekohtml.jar'
|
15
|
+
require 'nekodtd.jar'
|
16
|
+
require 'xercesImpl.jar'
|
17
|
+
require 'serializer.jar'
|
18
|
+
require 'xalan.jar'
|
19
|
+
require 'xml-apis.jar'
|
20
|
+
end
|
data/lib/nokogiri/nokogiri.jar
CHANGED
Binary file
|
data/lib/nokogiri/version.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Nokogiri
|
2
3
|
# The version of Nokogiri you are using
|
3
|
-
VERSION = "1.
|
4
|
+
VERSION = "1.11.0.rc3"
|
4
5
|
|
5
6
|
class VersionInfo # :nodoc:
|
6
7
|
def jruby?
|
@@ -11,19 +12,30 @@ module Nokogiri
|
|
11
12
|
defined?(RUBY_ENGINE) ? RUBY_ENGINE : "mri"
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
-
|
15
|
+
def loaded_libxml_version
|
16
|
+
Gem::Version.new(LIBXML_LOADED_VERSION.
|
16
17
|
scan(/^(\d+)(\d\d)(\d\d)(?!\d)/).first.
|
17
18
|
collect(&:to_i).
|
18
|
-
join(".")
|
19
|
+
join("."))
|
19
20
|
end
|
20
21
|
|
21
|
-
def
|
22
|
-
|
22
|
+
def compiled_libxml_version
|
23
|
+
Gem::Version.new LIBXML_COMPILED_VERSION
|
24
|
+
end
|
25
|
+
|
26
|
+
def loaded_libxslt_version
|
27
|
+
Gem::Version.new(LIBXSLT_LOADED_VERSION.
|
28
|
+
scan(/^(\d+)(\d\d)(\d\d)(?!\d)/).first.
|
29
|
+
collect(&:to_i).
|
30
|
+
join("."))
|
31
|
+
end
|
32
|
+
|
33
|
+
def compiled_libxslt_version
|
34
|
+
Gem::Version.new LIBXSLT_COMPILED_VERSION
|
23
35
|
end
|
24
36
|
|
25
37
|
def libxml2?
|
26
|
-
defined?(
|
38
|
+
defined?(LIBXML_COMPILED_VERSION)
|
27
39
|
end
|
28
40
|
|
29
41
|
def libxml2_using_system?
|
@@ -35,47 +47,63 @@ module Nokogiri
|
|
35
47
|
end
|
36
48
|
|
37
49
|
def warnings
|
38
|
-
|
50
|
+
warnings = []
|
39
51
|
|
40
|
-
if
|
41
|
-
|
42
|
-
|
43
|
-
|
52
|
+
if libxml2?
|
53
|
+
if compiled_libxml_version != loaded_libxml_version
|
54
|
+
warnings << "Nokogiri was built against libxml version #{compiled_libxml_version}, but has dynamically loaded #{loaded_libxml_version}"
|
55
|
+
end
|
56
|
+
|
57
|
+
if compiled_libxslt_version != loaded_libxslt_version
|
58
|
+
warnings << "Nokogiri was built against libxslt version #{compiled_libxslt_version}, but has dynamically loaded #{loaded_libxslt_version}"
|
59
|
+
end
|
44
60
|
end
|
61
|
+
|
62
|
+
warnings
|
45
63
|
end
|
46
64
|
|
47
65
|
def to_hash
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
66
|
+
{}.tap do |vi|
|
67
|
+
vi["warnings"] = []
|
68
|
+
vi["nokogiri"] = Nokogiri::VERSION
|
69
|
+
vi["ruby"] = {}.tap do |ruby|
|
70
|
+
ruby["version"] = ::RUBY_VERSION
|
71
|
+
ruby["platform"] = ::RUBY_PLATFORM
|
72
|
+
ruby["gem_platform"] = ::Gem::Platform.local.to_s
|
73
|
+
ruby["description"] = ::RUBY_DESCRIPTION
|
74
|
+
ruby["engine"] = engine
|
75
|
+
ruby["jruby"] = jruby? if jruby?
|
76
|
+
end
|
57
77
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
78
|
+
if libxml2?
|
79
|
+
vi["libxml"] = {}.tap do |libxml|
|
80
|
+
if libxml2_using_packaged?
|
81
|
+
libxml["source"] = "packaged"
|
82
|
+
libxml["patches"] = NOKOGIRI_LIBXML2_PATCHES
|
83
|
+
else
|
84
|
+
libxml["source"] = "system"
|
85
|
+
end
|
86
|
+
libxml["compiled"] = compiled_libxml_version.to_s
|
87
|
+
libxml["loaded"] = loaded_libxml_version.to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
vi["libxslt"] = {}.tap do |libxslt|
|
91
|
+
if libxml2_using_packaged?
|
92
|
+
libxslt["source"] = "packaged"
|
93
|
+
libxslt["patches"] = NOKOGIRI_LIBXSLT_PATCHES
|
94
|
+
else
|
95
|
+
libxslt["source"] = "system"
|
96
|
+
end
|
97
|
+
libxslt["compiled"] = compiled_libxslt_version.to_s
|
98
|
+
libxslt["loaded"] = loaded_libxslt_version.to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
vi["warnings"] = warnings
|
102
|
+
elsif jruby?
|
103
|
+
vi["xerces"] = Nokogiri::XERCES_VERSION
|
104
|
+
vi["nekohtml"] = Nokogiri::NEKO_VERSION
|
69
105
|
end
|
70
|
-
hash_info["libxml"]["compiled"] = compiled_parser_version
|
71
|
-
hash_info["libxml"]["loaded"] = loaded_parser_version
|
72
|
-
hash_info["warnings"] = warnings
|
73
|
-
elsif jruby?
|
74
|
-
hash_info["xerces"] = Nokogiri::XERCES_VERSION
|
75
|
-
hash_info["nekohtml"] = Nokogiri::NEKO_VERSION
|
76
106
|
end
|
77
|
-
|
78
|
-
hash_info
|
79
107
|
end
|
80
108
|
|
81
109
|
def to_markdown
|
@@ -96,14 +124,27 @@ module Nokogiri
|
|
96
124
|
def self.instance; @@instance; end
|
97
125
|
end
|
98
126
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
VersionInfo.instance.libxml2?
|
127
|
+
def self.uses_libxml?(requirement = nil) # :nodoc:
|
128
|
+
return false unless VersionInfo.instance.libxml2?
|
129
|
+
return true unless requirement
|
130
|
+
return Gem::Requirement.new(requirement).satisfied_by?(VersionInfo.instance.loaded_libxml_version)
|
104
131
|
end
|
105
132
|
|
106
133
|
def self.jruby? # :nodoc:
|
107
134
|
VersionInfo.instance.jruby?
|
108
135
|
end
|
136
|
+
|
137
|
+
# Ensure constants used in this file are loaded - see #1896
|
138
|
+
if Nokogiri.jruby?
|
139
|
+
require "nokogiri/jruby/dependencies"
|
140
|
+
end
|
141
|
+
begin
|
142
|
+
RUBY_VERSION =~ /(\d+\.\d+)/
|
143
|
+
require "nokogiri/#{$1}/nokogiri"
|
144
|
+
rescue LoadError
|
145
|
+
require "nokogiri/nokogiri"
|
146
|
+
end
|
147
|
+
|
148
|
+
# More complete version information about libxml
|
149
|
+
VERSION_INFO = VersionInfo.instance.to_hash
|
109
150
|
end
|
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,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Nokogiri
|
2
3
|
module XML
|
3
4
|
##
|
@@ -254,18 +255,12 @@ module Nokogiri
|
|
254
255
|
##
|
255
256
|
# +JRuby+
|
256
257
|
# Wraps Java's org.w3c.dom.document and returns Nokogiri::XML::Document
|
257
|
-
def self.wrap
|
258
|
-
raise "JRuby only method" unless Nokogiri.jruby?
|
259
|
-
return wrapJavaDocument(document)
|
260
|
-
end
|
258
|
+
def self.wrap(document) end if false # native-ext provides Document.wrap
|
261
259
|
|
262
260
|
##
|
263
261
|
# +JRuby+
|
264
262
|
# 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
|
263
|
+
def to_java; end if false # JRuby provides #to_java
|
269
264
|
|
270
265
|
private
|
271
266
|
def self.empty_doc? string_or_io
|
data/lib/nokogiri/xml/dtd.rb
CHANGED
data/lib/nokogiri/xml/node.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
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
|
@@ -56,49 +57,49 @@ module Nokogiri
|
|
56
57
|
include Enumerable
|
57
58
|
|
58
59
|
# Element node type, see Nokogiri::XML::Node#element?
|
59
|
-
ELEMENT_NODE =
|
60
|
+
ELEMENT_NODE = 1
|
60
61
|
# Attribute node type
|
61
|
-
ATTRIBUTE_NODE =
|
62
|
+
ATTRIBUTE_NODE = 2
|
62
63
|
# Text node type, see Nokogiri::XML::Node#text?
|
63
|
-
TEXT_NODE =
|
64
|
+
TEXT_NODE = 3
|
64
65
|
# CDATA node type, see Nokogiri::XML::Node#cdata?
|
65
66
|
CDATA_SECTION_NODE = 4
|
66
67
|
# Entity reference node type
|
67
|
-
ENTITY_REF_NODE =
|
68
|
+
ENTITY_REF_NODE = 5
|
68
69
|
# Entity node type
|
69
|
-
ENTITY_NODE =
|
70
|
+
ENTITY_NODE = 6
|
70
71
|
# PI node type
|
71
|
-
PI_NODE =
|
72
|
+
PI_NODE = 7
|
72
73
|
# Comment node type, see Nokogiri::XML::Node#comment?
|
73
|
-
COMMENT_NODE =
|
74
|
+
COMMENT_NODE = 8
|
74
75
|
# Document node type, see Nokogiri::XML::Node#xml?
|
75
|
-
DOCUMENT_NODE =
|
76
|
+
DOCUMENT_NODE = 9
|
76
77
|
# Document type node type
|
77
78
|
DOCUMENT_TYPE_NODE = 10
|
78
79
|
# Document fragment node type
|
79
80
|
DOCUMENT_FRAG_NODE = 11
|
80
81
|
# Notation node type
|
81
|
-
NOTATION_NODE =
|
82
|
+
NOTATION_NODE = 12
|
82
83
|
# HTML document node type, see Nokogiri::XML::Node#html?
|
83
84
|
HTML_DOCUMENT_NODE = 13
|
84
85
|
# DTD node type
|
85
|
-
DTD_NODE =
|
86
|
+
DTD_NODE = 14
|
86
87
|
# Element declaration type
|
87
|
-
ELEMENT_DECL =
|
88
|
+
ELEMENT_DECL = 15
|
88
89
|
# Attribute declaration type
|
89
|
-
ATTRIBUTE_DECL =
|
90
|
+
ATTRIBUTE_DECL = 16
|
90
91
|
# Entity declaration type
|
91
|
-
ENTITY_DECL =
|
92
|
+
ENTITY_DECL = 17
|
92
93
|
# Namespace declaration type
|
93
|
-
NAMESPACE_DECL =
|
94
|
+
NAMESPACE_DECL = 18
|
94
95
|
# XInclude start type
|
95
|
-
XINCLUDE_START =
|
96
|
+
XINCLUDE_START = 19
|
96
97
|
# XInclude end type
|
97
|
-
XINCLUDE_END =
|
98
|
+
XINCLUDE_END = 20
|
98
99
|
# DOCB document node type
|
99
100
|
DOCB_DOCUMENT_NODE = 21
|
100
101
|
|
101
|
-
def initialize
|
102
|
+
def initialize(name, document) # :nodoc:
|
102
103
|
# ... Ya. This is empty on purpose.
|
103
104
|
end
|
104
105
|
|
@@ -108,24 +109,18 @@ module Nokogiri
|
|
108
109
|
document.decorate(self)
|
109
110
|
end
|
110
111
|
|
112
|
+
# @!group Searching via XPath or CSS Queries
|
113
|
+
|
111
114
|
###
|
112
115
|
# Search this node's immediate children using CSS selector +selector+
|
113
|
-
def >
|
116
|
+
def >(selector)
|
114
117
|
ns = document.root.namespaces
|
115
118
|
xpath CSS.xpath_for(selector, :prefix => "./", :ns => ns).first
|
116
119
|
end
|
117
120
|
|
118
|
-
|
119
|
-
# Get the attribute value for the attribute +name+
|
120
|
-
def [] name
|
121
|
-
get(name.to_s)
|
122
|
-
end
|
121
|
+
# @!endgroup
|
123
122
|
|
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
|
123
|
+
# @!group Manipulating Document Structure
|
129
124
|
|
130
125
|
###
|
131
126
|
# Add +node_or_tags+ as a child of this Node.
|
@@ -134,7 +129,7 @@ module Nokogiri
|
|
134
129
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
135
130
|
#
|
136
131
|
# Also see related method +<<+.
|
137
|
-
def add_child
|
132
|
+
def add_child(node_or_tags)
|
138
133
|
node_or_tags = coerce(node_or_tags)
|
139
134
|
if node_or_tags.is_a?(XML::NodeSet)
|
140
135
|
node_or_tags.each { |n| add_child_node_and_reparent_attrs n }
|
@@ -151,7 +146,7 @@ module Nokogiri
|
|
151
146
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
152
147
|
#
|
153
148
|
# Also see related method +add_child+.
|
154
|
-
def prepend_child
|
149
|
+
def prepend_child(node_or_tags)
|
155
150
|
if first = children.first
|
156
151
|
# Mimic the error add_child would raise.
|
157
152
|
raise RuntimeError, "Document already has a root node" if document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
@@ -161,7 +156,6 @@ module Nokogiri
|
|
161
156
|
end
|
162
157
|
end
|
163
158
|
|
164
|
-
|
165
159
|
###
|
166
160
|
# Add html around this node
|
167
161
|
#
|
@@ -180,7 +174,7 @@ module Nokogiri
|
|
180
174
|
# Returns self, to support chaining of calls (e.g., root << child1 << child2)
|
181
175
|
#
|
182
176
|
# Also see related method +add_child+.
|
183
|
-
def <<
|
177
|
+
def <<(node_or_tags)
|
184
178
|
add_child node_or_tags
|
185
179
|
self
|
186
180
|
end
|
@@ -192,7 +186,7 @@ module Nokogiri
|
|
192
186
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
193
187
|
#
|
194
188
|
# Also see related method +before+.
|
195
|
-
def add_previous_sibling
|
189
|
+
def add_previous_sibling(node_or_tags)
|
196
190
|
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
191
|
|
198
192
|
add_sibling :previous, node_or_tags
|
@@ -205,7 +199,7 @@ module Nokogiri
|
|
205
199
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
206
200
|
#
|
207
201
|
# Also see related method +after+.
|
208
|
-
def add_next_sibling
|
202
|
+
def add_next_sibling(node_or_tags)
|
209
203
|
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
204
|
|
211
205
|
add_sibling :next, node_or_tags
|
@@ -218,7 +212,7 @@ module Nokogiri
|
|
218
212
|
# Returns self, to support chaining of calls.
|
219
213
|
#
|
220
214
|
# Also see related method +add_previous_sibling+.
|
221
|
-
def before
|
215
|
+
def before(node_or_tags)
|
222
216
|
add_previous_sibling node_or_tags
|
223
217
|
self
|
224
218
|
end
|
@@ -230,7 +224,7 @@ module Nokogiri
|
|
230
224
|
# Returns self, to support chaining of calls.
|
231
225
|
#
|
232
226
|
# Also see related method +add_next_sibling+.
|
233
|
-
def after
|
227
|
+
def after(node_or_tags)
|
234
228
|
add_next_sibling node_or_tags
|
235
229
|
self
|
236
230
|
end
|
@@ -242,7 +236,7 @@ module Nokogiri
|
|
242
236
|
# Returns self.
|
243
237
|
#
|
244
238
|
# Also see related method +children=+
|
245
|
-
def inner_html=
|
239
|
+
def inner_html=(node_or_tags)
|
246
240
|
self.children = node_or_tags
|
247
241
|
self
|
248
242
|
end
|
@@ -254,7 +248,7 @@ module Nokogiri
|
|
254
248
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
255
249
|
#
|
256
250
|
# Also see related method +inner_html=+
|
257
|
-
def children=
|
251
|
+
def children=(node_or_tags)
|
258
252
|
node_or_tags = coerce(node_or_tags)
|
259
253
|
children.unlink
|
260
254
|
if node_or_tags.is_a?(XML::NodeSet)
|
@@ -272,13 +266,13 @@ module Nokogiri
|
|
272
266
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
273
267
|
#
|
274
268
|
# Also see related method +swap+.
|
275
|
-
def replace
|
269
|
+
def replace(node_or_tags)
|
276
270
|
# We cannot replace a text node directly, otherwise libxml will return
|
277
271
|
# an internal error at parser.c:13031, I don't know exactly why
|
278
272
|
# libxml is trying to find a parent node that is an element or document
|
279
273
|
# so I can't tell if this is bug in libxml or not. issue #775.
|
280
274
|
if text?
|
281
|
-
replacee = Nokogiri::XML::Node.new
|
275
|
+
replacee = Nokogiri::XML::Node.new "dummy", document
|
282
276
|
add_previous_sibling_node replacee
|
283
277
|
unlink
|
284
278
|
return replacee.replace node_or_tags
|
@@ -302,33 +296,98 @@ module Nokogiri
|
|
302
296
|
# Returns self, to support chaining of calls.
|
303
297
|
#
|
304
298
|
# Also see related method +replace+.
|
305
|
-
def swap
|
299
|
+
def swap(node_or_tags)
|
306
300
|
replace node_or_tags
|
307
301
|
self
|
308
302
|
end
|
309
303
|
|
310
|
-
|
311
|
-
|
304
|
+
####
|
305
|
+
# Set the Node's content to a Text node containing +string+. The string gets XML escaped, not interpreted as markup.
|
306
|
+
def content=(string)
|
307
|
+
self.native_content = encode_special_chars(string.to_s)
|
308
|
+
end
|
309
|
+
|
310
|
+
###
|
311
|
+
# Set the parent Node for this Node
|
312
|
+
def parent=(parent_node)
|
313
|
+
parent_node.add_child(self)
|
314
|
+
parent_node
|
315
|
+
end
|
312
316
|
|
313
|
-
|
314
|
-
#
|
315
|
-
|
316
|
-
#
|
317
|
+
###
|
318
|
+
# Adds a default namespace supplied as a string +url+ href, to self.
|
319
|
+
# The consequence is as an xmlns attribute with supplied argument were
|
320
|
+
# present in parsed XML. A default namespace set with this method will
|
321
|
+
# now show up in #attributes, but when this node is serialized to XML an
|
322
|
+
# "xmlns" attribute will appear. See also #namespace and #namespace=
|
323
|
+
def default_namespace=(url)
|
324
|
+
add_namespace_definition(nil, url)
|
325
|
+
end
|
317
326
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
327
|
+
###
|
328
|
+
# Set the default namespace on this node (as would be defined with an
|
329
|
+
# "xmlns=" attribute in XML source), as a Namespace object +ns+. Note that
|
330
|
+
# a Namespace added this way will NOT be serialized as an xmlns attribute
|
331
|
+
# for this node. You probably want #default_namespace= instead, or perhaps
|
332
|
+
# #add_namespace_definition with a nil prefix argument.
|
333
|
+
def namespace=(ns)
|
334
|
+
return set_namespace(ns) unless ns
|
335
|
+
|
336
|
+
unless Nokogiri::XML::Namespace === ns
|
337
|
+
raise TypeError, "#{ns.class} can't be coerced into Nokogiri::XML::Namespace"
|
338
|
+
end
|
339
|
+
if ns.document != document
|
340
|
+
raise ArgumentError, "namespace must be declared on the same document"
|
341
|
+
end
|
342
|
+
|
343
|
+
set_namespace ns
|
344
|
+
end
|
345
|
+
|
346
|
+
###
|
347
|
+
# Do xinclude substitution on the subtree below node. If given a block, a
|
348
|
+
# Nokogiri::XML::ParseOptions object initialized from +options+, will be
|
349
|
+
# passed to it, allowing more convenient modification of the parser options.
|
350
|
+
def do_xinclude(options = XML::ParseOptions::DEFAULT_XML)
|
351
|
+
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
352
|
+
|
353
|
+
# give options to user
|
354
|
+
yield options if block_given?
|
355
|
+
|
356
|
+
# call c extension
|
357
|
+
process_xincludes(options.to_i)
|
358
|
+
end
|
359
|
+
|
360
|
+
alias :next :next_sibling
|
361
|
+
alias :previous :previous_sibling
|
362
|
+
alias :next= :add_next_sibling
|
363
|
+
alias :previous= :add_previous_sibling
|
364
|
+
alias :remove :unlink
|
365
|
+
alias :name= :node_name=
|
366
|
+
alias :add_namespace :add_namespace_definition
|
367
|
+
|
368
|
+
# @!endgroup
|
369
|
+
|
370
|
+
alias :text :content
|
371
|
+
alias :inner_text :content
|
372
|
+
alias :name :node_name
|
373
|
+
alias :type :node_type
|
374
|
+
alias :to_str :text
|
375
|
+
alias :clone :dup
|
376
|
+
alias :elements :element_children
|
377
|
+
|
378
|
+
# @!group Working With Node Attributes
|
379
|
+
|
380
|
+
###
|
381
|
+
# Get the attribute value for the attribute +name+
|
382
|
+
def [](name)
|
383
|
+
get(name.to_s)
|
384
|
+
end
|
385
|
+
|
386
|
+
###
|
387
|
+
# Set the attribute value for the attribute +name+ to +value+
|
388
|
+
def []=(name, value)
|
389
|
+
set name.to_s, value.to_s
|
390
|
+
end
|
332
391
|
|
333
392
|
####
|
334
393
|
# Returns a hash containing the node's attributes. The key is
|
@@ -337,9 +396,9 @@ module Nokogiri
|
|
337
396
|
# If you need to distinguish attributes with the same name, with different namespaces
|
338
397
|
# use #attribute_nodes instead.
|
339
398
|
def attributes
|
340
|
-
|
341
|
-
[node.node_name
|
342
|
-
|
399
|
+
attribute_nodes.each_with_object({}) do |node, hash|
|
400
|
+
hash[node.node_name] = node
|
401
|
+
end
|
343
402
|
end
|
344
403
|
|
345
404
|
###
|
@@ -348,6 +407,12 @@ module Nokogiri
|
|
348
407
|
attribute_nodes.map(&:value)
|
349
408
|
end
|
350
409
|
|
410
|
+
###
|
411
|
+
# Does this Node's attributes include <value>
|
412
|
+
def value?(value)
|
413
|
+
values.include? value
|
414
|
+
end
|
415
|
+
|
351
416
|
###
|
352
417
|
# Get the attribute names for this Node.
|
353
418
|
def keys
|
@@ -363,82 +428,366 @@ module Nokogiri
|
|
363
428
|
end
|
364
429
|
|
365
430
|
###
|
366
|
-
#
|
367
|
-
|
431
|
+
# Remove the attribute named +name+
|
432
|
+
def remove_attribute(name)
|
433
|
+
attr = attributes[name].remove if key? name
|
434
|
+
clear_xpath_context if Nokogiri.jruby?
|
435
|
+
attr
|
436
|
+
end
|
437
|
+
|
438
|
+
# Get the CSS class names of a Node.
|
439
|
+
#
|
440
|
+
# This is a convenience function and is equivalent to:
|
441
|
+
# node.kwattr_values("class")
|
442
|
+
#
|
443
|
+
# @see #kwattr_values
|
444
|
+
# @see #add_class
|
445
|
+
# @see #append_class
|
446
|
+
# @see #remove_class
|
447
|
+
#
|
448
|
+
# @return [Array<String>]
|
449
|
+
#
|
450
|
+
# The CSS classes present in the Node's +class+ attribute. If
|
451
|
+
# the attribute is empty or non-existent, the return value is
|
452
|
+
# an empty array.
|
453
|
+
#
|
454
|
+
# @example
|
455
|
+
# node # => <div class="section title header"></div>
|
456
|
+
# node.classes # => ["section", "title", "header"]
|
457
|
+
#
|
368
458
|
def classes
|
369
|
-
|
459
|
+
kwattr_values("class")
|
370
460
|
end
|
371
461
|
|
372
|
-
|
373
|
-
#
|
374
|
-
#
|
375
|
-
#
|
376
|
-
#
|
462
|
+
# Ensure HTML CSS classes are present on a +Node+. Any CSS
|
463
|
+
# classes in +names+ that already exist in the +Node+'s +class+
|
464
|
+
# attribute are _not_ added. Note that any existing duplicates
|
465
|
+
# in the +class+ attribute are not removed. Compare with
|
466
|
+
# {#append_class}.
|
467
|
+
#
|
468
|
+
# This is a convenience function and is equivalent to:
|
469
|
+
# node.kwattr_add("class", names)
|
470
|
+
#
|
471
|
+
# @see #kwattr_add
|
472
|
+
# @see #classes
|
473
|
+
# @see #append_class
|
474
|
+
# @see #remove_class
|
475
|
+
#
|
476
|
+
# @param names [String, Array<String>]
|
477
|
+
#
|
478
|
+
# CSS class names to be added to the Node's +class+
|
479
|
+
# attribute. May be a string containing whitespace-delimited
|
480
|
+
# names, or an Array of String names. Any class names already
|
481
|
+
# present will not be added. Any class names not present will
|
482
|
+
# be added. If no +class+ attribute exists, one is created.
|
483
|
+
#
|
484
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
485
|
+
#
|
486
|
+
# @example Ensure that a +Node+ has CSS class "section"
|
487
|
+
# node # => <div></div>
|
488
|
+
# node.add_class("section") # => <div class="section"></div>
|
489
|
+
# node.add_class("section") # => <div class="section"></div> # duplicate not added
|
490
|
+
#
|
491
|
+
# @example Ensure that a +Node+ has CSS classes "section" and "header", via a String argument.
|
492
|
+
# node # => <div class="section section"></div>
|
493
|
+
# node.add_class("section header") # => <div class="section section header"></div>
|
494
|
+
# # Note that the CSS class "section" is not added because it is already present.
|
495
|
+
# # Note also that the pre-existing duplicate CSS class "section" is not removed.
|
496
|
+
#
|
497
|
+
# @example Ensure that a +Node+ has CSS classes "section" and "header", via an Array argument.
|
498
|
+
# node # => <div></div>
|
499
|
+
# node.add_class(["section", "header"]) # => <div class="section header"></div>
|
500
|
+
#
|
501
|
+
def add_class(names)
|
502
|
+
kwattr_add("class", names)
|
503
|
+
end
|
504
|
+
|
505
|
+
# Add HTML CSS classes to a +Node+, regardless of
|
506
|
+
# duplication. Compare with {#add_class}.
|
507
|
+
#
|
508
|
+
# This is a convenience function and is equivalent to:
|
509
|
+
# node.kwattr_append("class", names)
|
510
|
+
#
|
511
|
+
# @see #kwattr_append
|
512
|
+
# @see #classes
|
513
|
+
# @see #add_class
|
514
|
+
# @see #remove_class
|
515
|
+
#
|
516
|
+
# @param names [String, Array<String>]
|
517
|
+
#
|
518
|
+
# CSS class names to be appended to the Node's +class+
|
519
|
+
# attribute. May be a string containing whitespace-delimited
|
520
|
+
# names, or an Array of String names. All class names passed
|
521
|
+
# in will be appended to the +class+ attribute even if they
|
522
|
+
# are already present in the attribute value. If no +class+
|
523
|
+
# attribute exists, one is created.
|
524
|
+
#
|
525
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
526
|
+
#
|
527
|
+
# @example Append "section" to a +Node+'s CSS +class+ attriubute
|
528
|
+
# node # => <div></div>
|
529
|
+
# node.append_class("section") # => <div class="section"></div>
|
530
|
+
# node.append_class("section") # => <div class="section section"></div> # duplicate added!
|
531
|
+
#
|
532
|
+
# @example Append "section" and "header" to a +Node+'s CSS +class+ attribute, via a String argument.
|
533
|
+
# node # => <div class="section section"></div>
|
534
|
+
# node.append_class("section header") # => <div class="section section section header"></div>
|
535
|
+
# # Note that the CSS class "section" is appended even though it is already present.
|
536
|
+
#
|
537
|
+
# @example Append "section" and "header" to a +Node+'s CSS +class+ attribute, via an Array argument.
|
538
|
+
# node # => <div></div>
|
539
|
+
# node.append_class(["section", "header"]) # => <div class="section header"></div>
|
540
|
+
# node.append_class(["section", "header"]) # => <div class="section header section header"></div>
|
541
|
+
#
|
542
|
+
def append_class(names)
|
543
|
+
kwattr_append("class", names)
|
544
|
+
end
|
545
|
+
|
546
|
+
# Remove HTML CSS classes from a +Node+. Any CSS classes in +names+ that
|
547
|
+
# exist in the +Node+'s +class+ attribute are removed, including any
|
548
|
+
# multiple entries.
|
549
|
+
#
|
550
|
+
# If no CSS classes remain after this operation, or if +names+ is
|
551
|
+
# +nil+, the +class+ attribute is deleted from the node.
|
552
|
+
#
|
553
|
+
# This is a convenience function and is equivalent to:
|
554
|
+
# node.kwattr_remove("class", names)
|
555
|
+
#
|
556
|
+
# @see #kwattr_remove
|
557
|
+
# @see #classes
|
558
|
+
# @see #add_class
|
559
|
+
# @see #append_class
|
560
|
+
#
|
561
|
+
# @param names [String, Array<String>]
|
562
|
+
#
|
563
|
+
# CSS class names to be removed from the Node's +class+ attribute. May
|
564
|
+
# be a string containing whitespace-delimited names, or an Array of
|
565
|
+
# String names. Any class names already present will be removed. If no
|
566
|
+
# CSS classes remain, the +class+ attribute is deleted.
|
567
|
+
#
|
568
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
569
|
+
#
|
570
|
+
# @example
|
571
|
+
# node # => <div class="section header"></div>
|
572
|
+
# node.remove_class("section") # => <div class="header"></div>
|
573
|
+
# node.remove_class("header") # => <div></div> # attribute is deleted when empty
|
574
|
+
#
|
575
|
+
def remove_class(names = nil)
|
576
|
+
kwattr_remove("class", names)
|
577
|
+
end
|
578
|
+
|
579
|
+
# Retrieve values from a keyword attribute of a Node.
|
580
|
+
#
|
581
|
+
# A "keyword attribute" is a node attribute that contains a set
|
582
|
+
# of space-delimited values. Perhaps the most familiar example
|
583
|
+
# of this is the HTML +class+ attribute used to contain CSS
|
584
|
+
# classes. But other keyword attributes exist, for instance
|
585
|
+
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
586
|
+
#
|
587
|
+
# @see #classes
|
588
|
+
# @see #kwattr_add
|
589
|
+
# @see #kwattr_append
|
590
|
+
# @see #kwattr_remove
|
591
|
+
#
|
592
|
+
# @param attribute_name [String] The name of the keyword attribute to be inspected.
|
593
|
+
#
|
594
|
+
# @return [Array<String>]
|
595
|
+
#
|
596
|
+
# The values present in the Node's +attribute_name+
|
597
|
+
# attribute. If the attribute is empty or non-existent, the
|
598
|
+
# return value is an empty array.
|
377
599
|
#
|
378
|
-
#
|
379
|
-
#
|
380
|
-
|
381
|
-
|
382
|
-
|
600
|
+
# @example
|
601
|
+
# node # => <a rel="nofollow noopener external">link</a>
|
602
|
+
# node.kwattr_values("rel") # => ["nofollow", "noopener", "external"]
|
603
|
+
#
|
604
|
+
# @since v1.11.0
|
605
|
+
#
|
606
|
+
def kwattr_values(attribute_name)
|
607
|
+
keywordify(get_attribute(attribute_name) || [])
|
608
|
+
end
|
609
|
+
|
610
|
+
# Ensure that values are present in a keyword attribute.
|
611
|
+
#
|
612
|
+
# Any values in +keywords+ that already exist in the +Node+'s
|
613
|
+
# attribute values are _not_ added. Note that any existing
|
614
|
+
# duplicates in the attribute values are not removed. Compare
|
615
|
+
# with {#kwattr_append}.
|
616
|
+
#
|
617
|
+
# A "keyword attribute" is a node attribute that contains a set
|
618
|
+
# of space-delimited values. Perhaps the most familiar example
|
619
|
+
# of this is the HTML +class+ attribute used to contain CSS
|
620
|
+
# classes. But other keyword attributes exist, for instance
|
621
|
+
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
622
|
+
#
|
623
|
+
# @see #add_class
|
624
|
+
# @see #kwattr_values
|
625
|
+
# @see #kwattr_append
|
626
|
+
# @see #kwattr_remove
|
627
|
+
#
|
628
|
+
# @param attribute_name [String] The name of the keyword attribute to be modified.
|
629
|
+
#
|
630
|
+
# @param keywords [String, Array<String>]
|
631
|
+
#
|
632
|
+
# Keywords to be added to the attribute named
|
633
|
+
# +attribute_name+. May be a string containing
|
634
|
+
# whitespace-delimited values, or an Array of String
|
635
|
+
# values. Any values already present will not be added. Any
|
636
|
+
# values not present will be added. If the named attribute
|
637
|
+
# does not exist, it is created.
|
638
|
+
#
|
639
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
640
|
+
#
|
641
|
+
# @example Ensure that a +Node+ has "nofollow" in its +rel+ attribute.
|
642
|
+
# node # => <a></a>
|
643
|
+
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
|
644
|
+
# node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a> # duplicate not added
|
645
|
+
#
|
646
|
+
# @example Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via a String argument.
|
647
|
+
# node # => <a rel="nofollow nofollow"></a>
|
648
|
+
# node.kwattr_add("rel", "nofollow noreferrer") # => <a rel="nofollow nofollow noreferrer"></a>
|
649
|
+
# # Note that "nofollow" is not added because it is already present.
|
650
|
+
# # Note also that the pre-existing duplicate "nofollow" is not removed.
|
651
|
+
#
|
652
|
+
# @example Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via an Array argument.
|
653
|
+
# node # => <a></a>
|
654
|
+
# node.kwattr_add("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
|
655
|
+
#
|
656
|
+
# @since v1.11.0
|
657
|
+
#
|
658
|
+
def kwattr_add(attribute_name, keywords)
|
659
|
+
keywords = keywordify(keywords)
|
660
|
+
current_kws = kwattr_values(attribute_name)
|
661
|
+
new_kws = (current_kws + (keywords - current_kws)).join(" ")
|
662
|
+
set_attribute(attribute_name, new_kws)
|
383
663
|
self
|
384
664
|
end
|
385
665
|
|
386
|
-
|
387
|
-
#
|
388
|
-
#
|
389
|
-
#
|
390
|
-
#
|
666
|
+
# Add keywords to a Node's keyword attribute, regardless of
|
667
|
+
# duplication. Compare with {#kwattr_add}.
|
668
|
+
#
|
669
|
+
# A "keyword attribute" is a node attribute that contains a set
|
670
|
+
# of space-delimited values. Perhaps the most familiar example
|
671
|
+
# of this is the HTML +class+ attribute used to contain CSS
|
672
|
+
# classes. But other keyword attributes exist, for instance
|
673
|
+
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
674
|
+
#
|
675
|
+
# @see #append_class
|
676
|
+
# @see #kwattr_values
|
677
|
+
# @see #kwattr_add
|
678
|
+
# @see #kwattr_remove
|
679
|
+
#
|
680
|
+
# @param attribute_name [String] The name of the keyword attribute to be modified.
|
681
|
+
#
|
682
|
+
# @param keywords [String, Array<String>]
|
683
|
+
#
|
684
|
+
# Keywords to be added to the attribute named
|
685
|
+
# +attribute_name+. May be a string containing
|
686
|
+
# whitespace-delimited values, or an Array of String
|
687
|
+
# values. All values passed in will be appended to the named
|
688
|
+
# attribute even if they are already present in the
|
689
|
+
# attribute. If the named attribute does not exist, it is
|
690
|
+
# created.
|
691
|
+
#
|
692
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
693
|
+
#
|
694
|
+
# @example Append "nofollow" to the +rel+ attribute.
|
695
|
+
# node # => <a></a>
|
696
|
+
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow"></a>
|
697
|
+
# node.kwattr_append("rel", "nofollow") # => <a rel="nofollow nofollow"></a> # duplicate added!
|
391
698
|
#
|
392
|
-
#
|
393
|
-
#
|
394
|
-
|
395
|
-
|
699
|
+
# @example Append "nofollow" and "noreferrer" to the +rel+ attribute, via a String argument.
|
700
|
+
# node # => <a rel="nofollow"></a>
|
701
|
+
# node.kwattr_append("rel", "nofollow noreferrer") # => <a rel="nofollow nofollow noreferrer"></a>
|
702
|
+
# # Note that "nofollow" is appended even though it is already present.
|
703
|
+
#
|
704
|
+
# @example Append "nofollow" and "noreferrer" to the +rel+ attribute, via an Array argument.
|
705
|
+
# node # => <a></a>
|
706
|
+
# node.kwattr_append("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
|
707
|
+
#
|
708
|
+
# @since v1.11.0
|
709
|
+
#
|
710
|
+
def kwattr_append(attribute_name, keywords)
|
711
|
+
keywords = keywordify(keywords)
|
712
|
+
current_kws = kwattr_values(attribute_name)
|
713
|
+
new_kws = (current_kws + keywords).join(" ")
|
714
|
+
set_attribute(attribute_name, new_kws)
|
396
715
|
self
|
397
716
|
end
|
398
717
|
|
399
|
-
|
400
|
-
#
|
401
|
-
#
|
402
|
-
# they are all removed.
|
718
|
+
# Remove keywords from a keyword attribute. Any matching
|
719
|
+
# keywords that exist in the named attribute are removed,
|
720
|
+
# including any multiple entries.
|
403
721
|
#
|
404
|
-
#
|
405
|
-
#
|
722
|
+
# If no keywords remain after this operation, or if +keywords+
|
723
|
+
# is +nil+, the attribute is deleted from the node.
|
406
724
|
#
|
407
|
-
#
|
408
|
-
#
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
725
|
+
# A "keyword attribute" is a node attribute that contains a set
|
726
|
+
# of space-delimited values. Perhaps the most familiar example
|
727
|
+
# of this is the HTML +class+ attribute used to contain CSS
|
728
|
+
# classes. But other keyword attributes exist, for instance
|
729
|
+
# [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
|
730
|
+
#
|
731
|
+
# @see #remove_class
|
732
|
+
# @see #kwattr_values
|
733
|
+
# @see #kwattr_add
|
734
|
+
# @see #kwattr_append
|
735
|
+
#
|
736
|
+
# @param attribute_name [String] The name of the keyword attribute to be modified.
|
737
|
+
#
|
738
|
+
# @param keywords [String, Array<String>]
|
739
|
+
#
|
740
|
+
# Keywords to be removed from the attribute named
|
741
|
+
# +attribute_name+. May be a string containing
|
742
|
+
# whitespace-delimited values, or an Array of String
|
743
|
+
# values. Any keywords present in the named attribute will be
|
744
|
+
# removed. If no keywords remain, or if +keywords+ is nil, the
|
745
|
+
# attribute is deleted.
|
746
|
+
#
|
747
|
+
# @return [Node] Returns +self+ for ease of chaining method calls.
|
748
|
+
#
|
749
|
+
# @example
|
750
|
+
# node # => <a rel="nofollow noreferrer">link</a>
|
751
|
+
# node.kwattr_remove("rel", "nofollow") # => <a rel="noreferrer">link</a>
|
752
|
+
# node.kwattr_remove("rel", "noreferrer") # => <a>link</a> # attribute is deleted when empty
|
753
|
+
#
|
754
|
+
# @since v1.11.0
|
755
|
+
#
|
756
|
+
def kwattr_remove(attribute_name, keywords)
|
757
|
+
if keywords.nil?
|
758
|
+
remove_attribute(attribute_name)
|
759
|
+
return self
|
760
|
+
end
|
761
|
+
|
762
|
+
keywords = keywordify(keywords)
|
763
|
+
current_kws = kwattr_values(attribute_name)
|
764
|
+
new_kws = current_kws - keywords
|
765
|
+
if new_kws.empty?
|
766
|
+
remove_attribute(attribute_name)
|
417
767
|
else
|
418
|
-
|
768
|
+
set_attribute(attribute_name, new_kws.join(" "))
|
419
769
|
end
|
420
770
|
self
|
421
771
|
end
|
422
772
|
|
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
773
|
alias :delete :remove_attribute
|
774
|
+
alias :get_attribute :[]
|
775
|
+
alias :attr :[]
|
776
|
+
alias :set_attribute :[]=
|
777
|
+
alias :has_attribute? :key?
|
778
|
+
|
779
|
+
# @!endgroup
|
431
780
|
|
432
781
|
###
|
433
782
|
# Returns true if this Node matches +selector+
|
434
|
-
def matches?
|
783
|
+
def matches?(selector)
|
435
784
|
ancestors.last.search(selector).include?(self)
|
436
785
|
end
|
437
786
|
|
438
787
|
###
|
439
788
|
# Create a DocumentFragment containing +tags+ that is relative to _this_
|
440
789
|
# context node.
|
441
|
-
def fragment
|
790
|
+
def fragment(tags)
|
442
791
|
type = document.html? ? Nokogiri::HTML : Nokogiri::XML
|
443
792
|
type::DocumentFragment.new(document, tags, self)
|
444
793
|
end
|
@@ -447,7 +796,7 @@ module Nokogiri
|
|
447
796
|
# Parse +string_or_io+ as a document fragment within the context of
|
448
797
|
# *this* node. Returns a XML::NodeSet containing the nodes parsed from
|
449
798
|
# +string_or_io+.
|
450
|
-
def parse
|
799
|
+
def parse(string_or_io, options = nil)
|
451
800
|
##
|
452
801
|
# When the current node is unparented and not an element node, use the
|
453
802
|
# document as the parsing context instead. Otherwise, the in-context
|
@@ -481,19 +830,6 @@ module Nokogiri
|
|
481
830
|
node_set
|
482
831
|
end
|
483
832
|
|
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
833
|
###
|
498
834
|
# Returns a Hash of +{prefix => value}+ for all namespaces on this
|
499
835
|
# node and its ancestors.
|
@@ -509,10 +845,11 @@ module Nokogiri
|
|
509
845
|
# default namespaces set on ancestor will NOT be, even if self
|
510
846
|
# has no explicit default namespace.
|
511
847
|
def namespaces
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
848
|
+
namespace_scopes.each_with_object({}) do |ns, hash|
|
849
|
+
prefix = ns.prefix
|
850
|
+
key = prefix ? "xmlns:#{prefix}" : "xmlns"
|
851
|
+
hash[key] = ns.href
|
852
|
+
end
|
516
853
|
end
|
517
854
|
|
518
855
|
# Returns true if this is a Comment
|
@@ -574,6 +911,7 @@ module Nokogiri
|
|
574
911
|
def element?
|
575
912
|
type == ELEMENT_NODE
|
576
913
|
end
|
914
|
+
|
577
915
|
alias :elem? :element?
|
578
916
|
|
579
917
|
###
|
@@ -584,7 +922,7 @@ module Nokogiri
|
|
584
922
|
end
|
585
923
|
|
586
924
|
# Get the inner_html for this node's Node#children
|
587
|
-
def inner_html
|
925
|
+
def inner_html(*args)
|
588
926
|
children.map { |x| x.to_html(*args) }.join
|
589
927
|
end
|
590
928
|
|
@@ -592,13 +930,13 @@ module Nokogiri
|
|
592
930
|
def css_path
|
593
931
|
path.split(/\//).map { |part|
|
594
932
|
part.length == 0 ? nil : part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
|
595
|
-
}.compact.join(
|
933
|
+
}.compact.join(" > ")
|
596
934
|
end
|
597
935
|
|
598
936
|
###
|
599
937
|
# Get a list of ancestor Node for this Node. If +selector+ is given,
|
600
938
|
# the ancestors must match +selector+
|
601
|
-
def ancestors
|
939
|
+
def ancestors(selector = nil)
|
602
940
|
return NodeSet.new(document) unless respond_to?(:parent)
|
603
941
|
return NodeSet.new(document) unless parent
|
604
942
|
|
@@ -619,57 +957,38 @@ module Nokogiri
|
|
619
957
|
})
|
620
958
|
end
|
621
959
|
|
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
960
|
####
|
653
961
|
# Yields self and all children to +block+ recursively.
|
654
|
-
def traverse
|
655
|
-
children.each{|j| j.traverse(&block) }
|
962
|
+
def traverse(&block)
|
963
|
+
children.each { |j| j.traverse(&block) }
|
656
964
|
block.call(self)
|
657
965
|
end
|
658
966
|
|
659
967
|
###
|
660
968
|
# Accept a visitor. This method calls "visit" on +visitor+ with self.
|
661
|
-
def accept
|
969
|
+
def accept(visitor)
|
662
970
|
visitor.visit(self)
|
663
971
|
end
|
664
972
|
|
665
973
|
###
|
666
974
|
# Test to see if this Node is equal to +other+
|
667
|
-
def ==
|
975
|
+
def ==(other)
|
668
976
|
return false unless other
|
669
977
|
return false unless other.respond_to?(:pointer_id)
|
670
978
|
pointer_id == other.pointer_id
|
671
979
|
end
|
672
980
|
|
981
|
+
###
|
982
|
+
# Compare two Node objects with respect to their Document. Nodes from
|
983
|
+
# different documents cannot be compared.
|
984
|
+
def <=>(other)
|
985
|
+
return nil unless other.is_a?(Nokogiri::XML::Node)
|
986
|
+
return nil unless document == other.document
|
987
|
+
compare other
|
988
|
+
end
|
989
|
+
|
990
|
+
# @!group Serialization and Generating Output
|
991
|
+
|
673
992
|
###
|
674
993
|
# Serialize Node using +options+. Save options can also be set using a
|
675
994
|
# block. See SaveOptions.
|
@@ -684,17 +1003,17 @@ module Nokogiri
|
|
684
1003
|
# config.format.as_xml
|
685
1004
|
# end
|
686
1005
|
#
|
687
|
-
def serialize
|
1006
|
+
def serialize(*args, &block)
|
688
1007
|
options = args.first.is_a?(Hash) ? args.shift : {
|
689
|
-
:encoding
|
690
|
-
:save_with
|
1008
|
+
:encoding => args[0],
|
1009
|
+
:save_with => args[1],
|
691
1010
|
}
|
692
1011
|
|
693
1012
|
encoding = options[:encoding] || document.encoding
|
694
1013
|
options[:encoding] = encoding
|
695
1014
|
|
696
1015
|
outstring = String.new
|
697
|
-
outstring.force_encoding(Encoding.find(encoding ||
|
1016
|
+
outstring.force_encoding(Encoding.find(encoding || "utf-8"))
|
698
1017
|
io = StringIO.new(outstring)
|
699
1018
|
write_to io, options, &block
|
700
1019
|
io.string
|
@@ -707,7 +1026,7 @@ module Nokogiri
|
|
707
1026
|
#
|
708
1027
|
# See Node#write_to for a list of +options+. For formatted output,
|
709
1028
|
# use Node#to_xhtml instead.
|
710
|
-
def to_html
|
1029
|
+
def to_html(options = {})
|
711
1030
|
to_format SaveOptions::DEFAULT_HTML, options
|
712
1031
|
end
|
713
1032
|
|
@@ -717,7 +1036,7 @@ module Nokogiri
|
|
717
1036
|
# doc.to_xml(:indent => 5, :encoding => 'UTF-8')
|
718
1037
|
#
|
719
1038
|
# See Node#write_to for a list of +options+
|
720
|
-
def to_xml
|
1039
|
+
def to_xml(options = {})
|
721
1040
|
options[:save_with] ||= SaveOptions::DEFAULT_XML
|
722
1041
|
serialize(options)
|
723
1042
|
end
|
@@ -728,7 +1047,7 @@ module Nokogiri
|
|
728
1047
|
# doc.to_xhtml(:indent => 5, :encoding => 'UTF-8')
|
729
1048
|
#
|
730
1049
|
# See Node#write_to for a list of +options+
|
731
|
-
def to_xhtml
|
1050
|
+
def to_xhtml(options = {})
|
732
1051
|
to_format SaveOptions::DEFAULT_XHTML, options
|
733
1052
|
end
|
734
1053
|
|
@@ -749,29 +1068,34 @@ module Nokogiri
|
|
749
1068
|
#
|
750
1069
|
# node.write_to(io, :indent_text => '-', :indent => 2)
|
751
1070
|
#
|
752
|
-
def write_to
|
753
|
-
options
|
754
|
-
encoding
|
1071
|
+
def write_to(io, *options)
|
1072
|
+
options = options.first.is_a?(Hash) ? options.shift : {}
|
1073
|
+
encoding = options[:encoding] || options[0]
|
755
1074
|
if Nokogiri.jruby?
|
756
|
-
save_options
|
757
|
-
indent_times
|
1075
|
+
save_options = options[:save_with] || options[1]
|
1076
|
+
indent_times = options[:indent] || 0
|
758
1077
|
else
|
759
|
-
save_options
|
760
|
-
indent_times
|
1078
|
+
save_options = options[:save_with] || options[1] || SaveOptions::FORMAT
|
1079
|
+
indent_times = options[:indent] || 2
|
761
1080
|
end
|
762
|
-
indent_text
|
1081
|
+
indent_text = options[:indent_text] || " "
|
1082
|
+
|
1083
|
+
# Any string times 0 returns an empty string. Therefore, use the same
|
1084
|
+
# string instead of generating a new empty string for every node with
|
1085
|
+
# zero indentation.
|
1086
|
+
indentation = indent_times.zero? ? "" : (indent_text * indent_times)
|
763
1087
|
|
764
1088
|
config = SaveOptions.new(save_options.to_i)
|
765
1089
|
yield config if block_given?
|
766
1090
|
|
767
|
-
native_write_to(io, encoding,
|
1091
|
+
native_write_to(io, encoding, indentation, config.options)
|
768
1092
|
end
|
769
1093
|
|
770
1094
|
###
|
771
1095
|
# Write Node as HTML to +io+ with +options+
|
772
1096
|
#
|
773
1097
|
# See Node#write_to for a list of +options+
|
774
|
-
def write_html_to
|
1098
|
+
def write_html_to(io, options = {})
|
775
1099
|
write_format_to SaveOptions::DEFAULT_HTML, io, options
|
776
1100
|
end
|
777
1101
|
|
@@ -779,7 +1103,7 @@ module Nokogiri
|
|
779
1103
|
# Write Node as XHTML to +io+ with +options+
|
780
1104
|
#
|
781
1105
|
# See Node#write_to for a list of +options+
|
782
|
-
def write_xhtml_to
|
1106
|
+
def write_xhtml_to(io, options = {})
|
783
1107
|
write_format_to SaveOptions::DEFAULT_XHTML, io, options
|
784
1108
|
end
|
785
1109
|
|
@@ -789,35 +1113,12 @@ module Nokogiri
|
|
789
1113
|
# doc.write_xml_to io, :encoding => 'UTF-8'
|
790
1114
|
#
|
791
1115
|
# See Node#write_to for a list of options
|
792
|
-
def write_xml_to
|
1116
|
+
def write_xml_to(io, options = {})
|
793
1117
|
options[:save_with] ||= SaveOptions::DEFAULT_XML
|
794
1118
|
write_to io, options
|
795
1119
|
end
|
796
1120
|
|
797
|
-
|
798
|
-
# Compare two Node objects with respect to their Document. Nodes from
|
799
|
-
# different documents cannot be compared.
|
800
|
-
def <=> other
|
801
|
-
return nil unless other.is_a?(Nokogiri::XML::Node)
|
802
|
-
return nil unless document == other.document
|
803
|
-
compare other
|
804
|
-
end
|
805
|
-
|
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
|
812
|
-
|
813
|
-
# give options to user
|
814
|
-
yield options if block_given?
|
815
|
-
|
816
|
-
# call c extension
|
817
|
-
process_xincludes(options.to_i)
|
818
|
-
end
|
819
|
-
|
820
|
-
def canonicalize(mode=XML::XML_C14N_1_0,inclusive_namespaces=nil,with_comments=false)
|
1121
|
+
def canonicalize(mode = XML::XML_C14N_1_0, inclusive_namespaces = nil, with_comments = false)
|
821
1122
|
c14n_root = self
|
822
1123
|
document.canonicalize(mode, inclusive_namespaces, with_comments) do |node, parent|
|
823
1124
|
tn = node.is_a?(XML::Node) ? node : parent
|
@@ -825,16 +1126,29 @@ module Nokogiri
|
|
825
1126
|
end
|
826
1127
|
end
|
827
1128
|
|
1129
|
+
# @!endgroup
|
1130
|
+
|
828
1131
|
private
|
829
1132
|
|
830
|
-
def
|
1133
|
+
def keywordify(keywords)
|
1134
|
+
case keywords
|
1135
|
+
when Enumerable
|
1136
|
+
return keywords
|
1137
|
+
when String
|
1138
|
+
return keywords.scan(/\S+/)
|
1139
|
+
else
|
1140
|
+
raise ArgumentError.new("Keyword attributes must be passed as either a String or an Enumerable, but received #{keywords.class}")
|
1141
|
+
end
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
def add_sibling(next_or_previous, node_or_tags)
|
831
1145
|
impl = (next_or_previous == :next) ? :add_next_sibling_node : :add_previous_sibling_node
|
832
|
-
iter = (next_or_previous == :next) ? :reverse_each
|
1146
|
+
iter = (next_or_previous == :next) ? :reverse_each : :each
|
833
1147
|
|
834
1148
|
node_or_tags = coerce node_or_tags
|
835
1149
|
if node_or_tags.is_a?(XML::NodeSet)
|
836
1150
|
if text?
|
837
|
-
pivot = Nokogiri::XML::Node.new
|
1151
|
+
pivot = Nokogiri::XML::Node.new "dummy", document
|
838
1152
|
send impl, pivot
|
839
1153
|
else
|
840
1154
|
pivot = self
|
@@ -847,17 +1161,18 @@ module Nokogiri
|
|
847
1161
|
node_or_tags
|
848
1162
|
end
|
849
1163
|
|
850
|
-
|
851
|
-
|
852
|
-
|
1164
|
+
USING_LIBXML_WITH_BROKEN_SERIALIZATION = Nokogiri.uses_libxml?("~> 2.6.0").freeze
|
1165
|
+
private_constant :USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
1166
|
+
|
1167
|
+
def to_format(save_option, options)
|
1168
|
+
return dump_html if USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
853
1169
|
|
854
1170
|
options[:save_with] = save_option unless options[:save_with]
|
855
1171
|
serialize(options)
|
856
1172
|
end
|
857
1173
|
|
858
|
-
def write_format_to
|
859
|
-
|
860
|
-
return (io << dump_html) if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
|
1174
|
+
def write_format_to(save_option, io, options)
|
1175
|
+
return (io << dump_html) if USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
861
1176
|
|
862
1177
|
options[:save_with] ||= save_option
|
863
1178
|
write_to io, options
|
@@ -867,7 +1182,7 @@ module Nokogiri
|
|
867
1182
|
[:name, :namespace, :attribute_nodes, :children]
|
868
1183
|
end
|
869
1184
|
|
870
|
-
def coerce
|
1185
|
+
def coerce(data)
|
871
1186
|
case data
|
872
1187
|
when XML::NodeSet
|
873
1188
|
return data
|
@@ -888,9 +1203,9 @@ Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
|
888
1203
|
end
|
889
1204
|
|
890
1205
|
# @private
|
891
|
-
IMPLIED_XPATH_CONTEXTS = [
|
1206
|
+
IMPLIED_XPATH_CONTEXTS = [".//".freeze].freeze
|
892
1207
|
|
893
|
-
def add_child_node_and_reparent_attrs
|
1208
|
+
def add_child_node_and_reparent_attrs(node)
|
894
1209
|
add_child_node node
|
895
1210
|
node.attribute_nodes.find_all { |a| a.name =~ /:/ }.each do |attr_node|
|
896
1211
|
attr_node.remove
|