nokogiri 1.11.0.rc1-x86-mingw32 → 1.11.0.rc2-x86-mingw32
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 +1 -1
- data/ext/nokogiri/extconf.rb +6 -2
- data/ext/nokogiri/nokogiri.h +12 -0
- data/ext/nokogiri/xml_reader.c +6 -17
- data/ext/nokogiri/xml_schema.c +29 -0
- data/lib/nokogiri/2.4/nokogiri.so +0 -0
- data/lib/nokogiri/2.5/nokogiri.so +0 -0
- data/lib/nokogiri/2.6/nokogiri.so +0 -0
- data/lib/nokogiri/2.7/nokogiri.so +0 -0
- data/lib/nokogiri/css/parser_extras.rb +38 -36
- data/lib/nokogiri/version.rb +2 -2
- data/lib/nokogiri/xml/node.rb +514 -213
- data/lib/nokogiri/xml/searchable.rb +21 -15
- metadata +12 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 765a04a297fde5d6c1027af613b46e98d3051c7b28711d363507e3f07ab21016
|
4
|
+
data.tar.gz: e0a92fac5246b9850aca1059aaba99eb808d09e77dfa10c61383c6a36b0ad388
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92934990b6dca5153820f3600e30b1ae524933130fd21e4baffab39ad927cba7e69325473dfe16ae139dc3c6e5ed884ab38f8be7d9ab063aec76b77184967ef6
|
7
|
+
data.tar.gz: da67d95103ff37cbb006cbf45e9e70c3bcb855eac7695cef85554ad3e3500655c8a1ff89e05482f8b7bf176e75ff6286016372be2e21cb2caa224a97bdf1de47
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ or CSS3 selectors.
|
|
11
11
|
|
12
12
|
* https://nokogiri.org
|
13
13
|
* [Installation Help](https://nokogiri.org/tutorials/installing_nokogiri.html)
|
14
|
-
* [Tutorials](https://nokogiri.org)
|
14
|
+
* [Tutorials](https://nokogiri.org/tutorials/toc.html)
|
15
15
|
* [Cheat Sheet](https://github.com/sparklemotion/nokogiri/wiki/Cheat-sheet)
|
16
16
|
* [GitHub](https://github.com/sparklemotion/nokogiri)
|
17
17
|
* [Mailing List](https://groups.google.com/group/nokogiri-talk)
|
data/ext/nokogiri/extconf.rb
CHANGED
@@ -24,6 +24,10 @@ def openbsd?
|
|
24
24
|
RbConfig::CONFIG['target_os'] =~ /openbsd/
|
25
25
|
end
|
26
26
|
|
27
|
+
def aix?
|
28
|
+
RbConfig::CONFIG["target_os"] =~ /aix/
|
29
|
+
end
|
30
|
+
|
27
31
|
def nix?
|
28
32
|
! (windows? || solaris? || darwin?)
|
29
33
|
end
|
@@ -419,7 +423,7 @@ if windows?
|
|
419
423
|
$CFLAGS << " -DXP_WIN -DXP_WIN32 -DUSE_INCLUDED_VASPRINTF"
|
420
424
|
end
|
421
425
|
|
422
|
-
if solaris?
|
426
|
+
if solaris? || aix?
|
423
427
|
$CFLAGS << " -DUSE_INCLUDED_VASPRINTF"
|
424
428
|
end
|
425
429
|
|
@@ -466,7 +470,7 @@ else
|
|
466
470
|
# The gem version constraint in the Rakefile is not respected at install time.
|
467
471
|
# Keep this version in sync with the one in the Rakefile !
|
468
472
|
require 'rubygems'
|
469
|
-
gem 'mini_portile2', '~> 2.
|
473
|
+
gem 'mini_portile2', '~> 2.5.0'
|
470
474
|
require 'mini_portile2'
|
471
475
|
message "Using mini_portile version #{MiniPortile::VERSION}\n"
|
472
476
|
|
data/ext/nokogiri/nokogiri.h
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
#ifndef NOKOGIRI_NATIVE
|
2
2
|
#define NOKOGIRI_NATIVE
|
3
3
|
|
4
|
+
#if _MSC_VER
|
5
|
+
#ifndef WIN32_LEAN_AND_MEAN
|
6
|
+
#define WIN32_LEAN_AND_MEAN
|
7
|
+
#endif /* WIN32_LEAN_AND_MEAN */
|
8
|
+
#ifndef WIN32
|
9
|
+
#define WIN32
|
10
|
+
#endif /* WIN32 */
|
11
|
+
#include <winsock2.h>
|
12
|
+
#include <ws2tcpip.h>
|
13
|
+
#include <windows.h>
|
14
|
+
#endif
|
15
|
+
|
4
16
|
#include <stdlib.h>
|
5
17
|
#include <string.h>
|
6
18
|
#include <assert.h>
|
data/ext/nokogiri/xml_reader.c
CHANGED
@@ -28,35 +28,24 @@ static int has_attributes(xmlTextReaderPtr reader)
|
|
28
28
|
static void Nokogiri_xml_node_namespaces(xmlNodePtr node, VALUE attr_hash)
|
29
29
|
{
|
30
30
|
xmlNsPtr ns;
|
31
|
-
|
32
|
-
char *key ;
|
33
|
-
size_t keylen ;
|
31
|
+
VALUE key;
|
34
32
|
|
35
33
|
if (node->type != XML_ELEMENT_NODE) return ;
|
36
34
|
|
37
35
|
ns = node->nsDef;
|
38
36
|
while (ns != NULL) {
|
39
37
|
|
40
|
-
|
41
|
-
if (keylen > XMLNS_BUFFER_LEN) {
|
42
|
-
key = (char*)malloc(keylen) ;
|
43
|
-
} else {
|
44
|
-
key = buffer ;
|
45
|
-
}
|
46
|
-
|
38
|
+
key = rb_enc_str_new_cstr(XMLNS_PREFIX, rb_utf8_encoding());
|
47
39
|
if (ns->prefix) {
|
48
|
-
|
49
|
-
|
50
|
-
sprintf(key, "%s", XMLNS_PREFIX);
|
40
|
+
rb_str_cat_cstr(key, ":");
|
41
|
+
rb_str_cat_cstr(key, (const char*)ns->prefix);
|
51
42
|
}
|
52
43
|
|
44
|
+
key = rb_str_conv_enc(key, rb_utf8_encoding(), rb_default_internal_encoding());
|
53
45
|
rb_hash_aset(attr_hash,
|
54
|
-
|
46
|
+
key,
|
55
47
|
(ns->href ? NOKOGIRI_STR_NEW2(ns->href) : Qnil)
|
56
48
|
);
|
57
|
-
if (key != buffer) {
|
58
|
-
free(key);
|
59
|
-
}
|
60
49
|
ns = ns->next ;
|
61
50
|
}
|
62
51
|
}
|
data/ext/nokogiri/xml_schema.c
CHANGED
@@ -133,6 +133,31 @@ static VALUE read_memory(VALUE klass, VALUE content)
|
|
133
133
|
return rb_schema;
|
134
134
|
}
|
135
135
|
|
136
|
+
/* Schema creation will remove and deallocate "blank" nodes.
|
137
|
+
* If those blank nodes have been exposed to Ruby, they could get freed
|
138
|
+
* out from under the VALUE pointer. This function checks to see if any of
|
139
|
+
* those nodes have been exposed to Ruby, and if so we should raise an exception.
|
140
|
+
*/
|
141
|
+
static int has_blank_nodes_p(VALUE cache)
|
142
|
+
{
|
143
|
+
long i;
|
144
|
+
|
145
|
+
if (NIL_P(cache)) {
|
146
|
+
return 0;
|
147
|
+
}
|
148
|
+
|
149
|
+
for (i = 0; i < RARRAY_LEN(cache); i++) {
|
150
|
+
xmlNodePtr node;
|
151
|
+
VALUE element = rb_ary_entry(cache, i);
|
152
|
+
Data_Get_Struct(element, xmlNode, node);
|
153
|
+
if (xmlIsBlankNode(node)) {
|
154
|
+
return 1;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
return 0;
|
159
|
+
}
|
160
|
+
|
136
161
|
/*
|
137
162
|
* call-seq:
|
138
163
|
* from_document(doc)
|
@@ -152,6 +177,10 @@ static VALUE from_document(VALUE klass, VALUE document)
|
|
152
177
|
/* In case someone passes us a node. ugh. */
|
153
178
|
doc = doc->doc;
|
154
179
|
|
180
|
+
if (has_blank_nodes_p(DOC_NODE_CACHE(doc))) {
|
181
|
+
rb_raise(rb_eArgError, "Creating a schema from a document that has blank nodes exposed to Ruby is dangerous");
|
182
|
+
}
|
183
|
+
|
155
184
|
ctx = xmlSchemaNewDocParserCtxt(doc);
|
156
185
|
|
157
186
|
errors = rb_ary_new();
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,64 +1,66 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require
|
2
|
+
require "thread"
|
3
3
|
|
4
4
|
module Nokogiri
|
5
5
|
module CSS
|
6
6
|
class Parser < Racc::Parser
|
7
|
-
|
8
|
-
|
9
|
-
@
|
7
|
+
CACHE_SWITCH_NAME = :nokogiri_css_parser_cache_is_off
|
8
|
+
|
9
|
+
@cache = {}
|
10
|
+
@mutex = Mutex.new
|
10
11
|
|
11
12
|
class << self
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
# Return a thread-local boolean indicating whether the CSS-to-XPath cache is active. (Default is `true`.)
|
14
|
+
def cache_on?
|
15
|
+
!Thread.current[CACHE_SWITCH_NAME]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Set a thread-local boolean to turn cacheing on and off. Truthy values turn the cache on, falsey values turn the cache off.
|
19
|
+
def set_cache(value)
|
20
|
+
Thread.current[CACHE_SWITCH_NAME] = !value
|
21
|
+
end
|
16
22
|
|
17
23
|
# Get the css selector in +string+ from the cache
|
18
|
-
def []
|
19
|
-
return unless
|
24
|
+
def [](string)
|
25
|
+
return unless cache_on?
|
20
26
|
@mutex.synchronize { @cache[string] }
|
21
27
|
end
|
22
28
|
|
23
29
|
# Set the css selector in +string+ in the cache to +value+
|
24
|
-
def []=
|
25
|
-
return value unless
|
30
|
+
def []=(string, value)
|
31
|
+
return value unless cache_on?
|
26
32
|
@mutex.synchronize { @cache[string] = value }
|
27
33
|
end
|
28
34
|
|
29
35
|
# Clear the cache
|
30
|
-
def clear_cache
|
31
|
-
@mutex.synchronize
|
36
|
+
def clear_cache(create_new_object = false)
|
37
|
+
@mutex.synchronize do
|
38
|
+
if create_new_object
|
39
|
+
@cache = {}
|
40
|
+
else
|
41
|
+
@cache.clear
|
42
|
+
end
|
43
|
+
end
|
32
44
|
end
|
33
45
|
|
34
46
|
# Execute +block+ without cache
|
35
|
-
def without_cache
|
36
|
-
|
37
|
-
|
47
|
+
def without_cache(&block)
|
48
|
+
original_cache_setting = cache_on?
|
49
|
+
set_cache false
|
38
50
|
block.call
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
###
|
43
|
-
# Parse this CSS selector in +selector+. Returns an AST.
|
44
|
-
def parse selector
|
45
|
-
@warned ||= false
|
46
|
-
unless @warned
|
47
|
-
$stderr.puts('Nokogiri::CSS::Parser.parse is deprecated, call Nokogiri::CSS.parse(), this will be removed August 1st or version 1.4.0 (whichever is first)')
|
48
|
-
@warned = true
|
49
|
-
end
|
50
|
-
new.parse selector
|
51
|
+
ensure
|
52
|
+
set_cache original_cache_setting
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
56
|
# Create a new CSS parser with respect to +namespaces+
|
55
|
-
def initialize
|
56
|
-
@tokenizer
|
57
|
+
def initialize(namespaces = {})
|
58
|
+
@tokenizer = Tokenizer.new
|
57
59
|
@namespaces = namespaces
|
58
60
|
super()
|
59
61
|
end
|
60
62
|
|
61
|
-
def parse
|
63
|
+
def parse(string)
|
62
64
|
@tokenizer.scan_setup string
|
63
65
|
do_parse
|
64
66
|
end
|
@@ -68,14 +70,14 @@ module Nokogiri
|
|
68
70
|
end
|
69
71
|
|
70
72
|
# Get the xpath for +string+ using +options+
|
71
|
-
def xpath_for
|
73
|
+
def xpath_for(string, options = {})
|
72
74
|
key = "#{string}#{options[:ns]}#{options[:prefix]}"
|
73
75
|
v = self.class[key]
|
74
76
|
return v if v
|
75
77
|
|
76
78
|
args = [
|
77
|
-
options[:prefix] ||
|
78
|
-
options[:visitor] || XPathVisitor.new
|
79
|
+
options[:prefix] || "//",
|
80
|
+
options[:visitor] || XPathVisitor.new,
|
79
81
|
]
|
80
82
|
self.class[key] = parse(string).map { |ast|
|
81
83
|
ast.to_xpath(*args)
|
@@ -83,7 +85,7 @@ module Nokogiri
|
|
83
85
|
end
|
84
86
|
|
85
87
|
# On CSS parser error, raise an exception
|
86
|
-
def on_error
|
88
|
+
def on_error(error_token_id, error_value, value_stack)
|
87
89
|
after = value_stack.compact.last
|
88
90
|
raise SyntaxError.new("unexpected '#{error_value}' after '#{after}'")
|
89
91
|
end
|
data/lib/nokogiri/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Nokogiri
|
3
3
|
# The version of Nokogiri you are using
|
4
|
-
VERSION = "1.11.0.
|
4
|
+
VERSION = "1.11.0.rc2"
|
5
5
|
|
6
6
|
class VersionInfo # :nodoc:
|
7
7
|
def jruby?
|
@@ -133,7 +133,7 @@ module Nokogiri
|
|
133
133
|
VersionInfo.instance.jruby?
|
134
134
|
end
|
135
135
|
|
136
|
-
# Ensure constants used in this file are loaded
|
136
|
+
# Ensure constants used in this file are loaded - see #1896
|
137
137
|
if Nokogiri.jruby?
|
138
138
|
require "nokogiri/jruby/dependencies"
|
139
139
|
end
|
data/lib/nokogiri/xml/node.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
# frozen_string_literal: true
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "stringio"
|
4
|
+
require "nokogiri/xml/node/save_options"
|
5
5
|
|
6
6
|
module Nokogiri
|
7
7
|
module XML
|
@@ -57,49 +57,49 @@ module Nokogiri
|
|
57
57
|
include Enumerable
|
58
58
|
|
59
59
|
# Element node type, see Nokogiri::XML::Node#element?
|
60
|
-
ELEMENT_NODE =
|
60
|
+
ELEMENT_NODE = 1
|
61
61
|
# Attribute node type
|
62
|
-
ATTRIBUTE_NODE =
|
62
|
+
ATTRIBUTE_NODE = 2
|
63
63
|
# Text node type, see Nokogiri::XML::Node#text?
|
64
|
-
TEXT_NODE =
|
64
|
+
TEXT_NODE = 3
|
65
65
|
# CDATA node type, see Nokogiri::XML::Node#cdata?
|
66
66
|
CDATA_SECTION_NODE = 4
|
67
67
|
# Entity reference node type
|
68
|
-
ENTITY_REF_NODE =
|
68
|
+
ENTITY_REF_NODE = 5
|
69
69
|
# Entity node type
|
70
|
-
ENTITY_NODE =
|
70
|
+
ENTITY_NODE = 6
|
71
71
|
# PI node type
|
72
|
-
PI_NODE =
|
72
|
+
PI_NODE = 7
|
73
73
|
# Comment node type, see Nokogiri::XML::Node#comment?
|
74
|
-
COMMENT_NODE =
|
74
|
+
COMMENT_NODE = 8
|
75
75
|
# Document node type, see Nokogiri::XML::Node#xml?
|
76
|
-
DOCUMENT_NODE =
|
76
|
+
DOCUMENT_NODE = 9
|
77
77
|
# Document type node type
|
78
78
|
DOCUMENT_TYPE_NODE = 10
|
79
79
|
# Document fragment node type
|
80
80
|
DOCUMENT_FRAG_NODE = 11
|
81
81
|
# Notation node type
|
82
|
-
NOTATION_NODE =
|
82
|
+
NOTATION_NODE = 12
|
83
83
|
# HTML document node type, see Nokogiri::XML::Node#html?
|
84
84
|
HTML_DOCUMENT_NODE = 13
|
85
85
|
# DTD node type
|
86
|
-
DTD_NODE =
|
86
|
+
DTD_NODE = 14
|
87
87
|
# Element declaration type
|
88
|
-
ELEMENT_DECL =
|
88
|
+
ELEMENT_DECL = 15
|
89
89
|
# Attribute declaration type
|
90
|
-
ATTRIBUTE_DECL =
|
90
|
+
ATTRIBUTE_DECL = 16
|
91
91
|
# Entity declaration type
|
92
|
-
ENTITY_DECL =
|
92
|
+
ENTITY_DECL = 17
|
93
93
|
# Namespace declaration type
|
94
|
-
NAMESPACE_DECL =
|
94
|
+
NAMESPACE_DECL = 18
|
95
95
|
# XInclude start type
|
96
|
-
XINCLUDE_START =
|
96
|
+
XINCLUDE_START = 19
|
97
97
|
# XInclude end type
|
98
|
-
XINCLUDE_END =
|
98
|
+
XINCLUDE_END = 20
|
99
99
|
# DOCB document node type
|
100
100
|
DOCB_DOCUMENT_NODE = 21
|
101
101
|
|
102
|
-
def initialize
|
102
|
+
def initialize(name, document) # :nodoc:
|
103
103
|
# ... Ya. This is empty on purpose.
|
104
104
|
end
|
105
105
|
|
@@ -109,24 +109,18 @@ module Nokogiri
|
|
109
109
|
document.decorate(self)
|
110
110
|
end
|
111
111
|
|
112
|
+
# @!group Searching via XPath or CSS Queries
|
113
|
+
|
112
114
|
###
|
113
115
|
# Search this node's immediate children using CSS selector +selector+
|
114
|
-
def >
|
116
|
+
def >(selector)
|
115
117
|
ns = document.root.namespaces
|
116
118
|
xpath CSS.xpath_for(selector, :prefix => "./", :ns => ns).first
|
117
119
|
end
|
118
120
|
|
119
|
-
|
120
|
-
# Get the attribute value for the attribute +name+
|
121
|
-
def [] name
|
122
|
-
get(name.to_s)
|
123
|
-
end
|
121
|
+
# @!endgroup
|
124
122
|
|
125
|
-
|
126
|
-
# Set the attribute value for the attribute +name+ to +value+
|
127
|
-
def []= name, value
|
128
|
-
set name.to_s, value.to_s
|
129
|
-
end
|
123
|
+
# @!group Manipulating Document Structure
|
130
124
|
|
131
125
|
###
|
132
126
|
# Add +node_or_tags+ as a child of this Node.
|
@@ -135,7 +129,7 @@ module Nokogiri
|
|
135
129
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
136
130
|
#
|
137
131
|
# Also see related method +<<+.
|
138
|
-
def add_child
|
132
|
+
def add_child(node_or_tags)
|
139
133
|
node_or_tags = coerce(node_or_tags)
|
140
134
|
if node_or_tags.is_a?(XML::NodeSet)
|
141
135
|
node_or_tags.each { |n| add_child_node_and_reparent_attrs n }
|
@@ -152,7 +146,7 @@ module Nokogiri
|
|
152
146
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
153
147
|
#
|
154
148
|
# Also see related method +add_child+.
|
155
|
-
def prepend_child
|
149
|
+
def prepend_child(node_or_tags)
|
156
150
|
if first = children.first
|
157
151
|
# Mimic the error add_child would raise.
|
158
152
|
raise RuntimeError, "Document already has a root node" if document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
@@ -162,7 +156,6 @@ module Nokogiri
|
|
162
156
|
end
|
163
157
|
end
|
164
158
|
|
165
|
-
|
166
159
|
###
|
167
160
|
# Add html around this node
|
168
161
|
#
|
@@ -181,7 +174,7 @@ module Nokogiri
|
|
181
174
|
# Returns self, to support chaining of calls (e.g., root << child1 << child2)
|
182
175
|
#
|
183
176
|
# Also see related method +add_child+.
|
184
|
-
def <<
|
177
|
+
def <<(node_or_tags)
|
185
178
|
add_child node_or_tags
|
186
179
|
self
|
187
180
|
end
|
@@ -193,7 +186,7 @@ module Nokogiri
|
|
193
186
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
194
187
|
#
|
195
188
|
# Also see related method +before+.
|
196
|
-
def add_previous_sibling
|
189
|
+
def add_previous_sibling(node_or_tags)
|
197
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?)
|
198
191
|
|
199
192
|
add_sibling :previous, node_or_tags
|
@@ -206,7 +199,7 @@ module Nokogiri
|
|
206
199
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
207
200
|
#
|
208
201
|
# Also see related method +after+.
|
209
|
-
def add_next_sibling
|
202
|
+
def add_next_sibling(node_or_tags)
|
210
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?)
|
211
204
|
|
212
205
|
add_sibling :next, node_or_tags
|
@@ -219,7 +212,7 @@ module Nokogiri
|
|
219
212
|
# Returns self, to support chaining of calls.
|
220
213
|
#
|
221
214
|
# Also see related method +add_previous_sibling+.
|
222
|
-
def before
|
215
|
+
def before(node_or_tags)
|
223
216
|
add_previous_sibling node_or_tags
|
224
217
|
self
|
225
218
|
end
|
@@ -231,7 +224,7 @@ module Nokogiri
|
|
231
224
|
# Returns self, to support chaining of calls.
|
232
225
|
#
|
233
226
|
# Also see related method +add_next_sibling+.
|
234
|
-
def after
|
227
|
+
def after(node_or_tags)
|
235
228
|
add_next_sibling node_or_tags
|
236
229
|
self
|
237
230
|
end
|
@@ -243,7 +236,7 @@ module Nokogiri
|
|
243
236
|
# Returns self.
|
244
237
|
#
|
245
238
|
# Also see related method +children=+
|
246
|
-
def inner_html=
|
239
|
+
def inner_html=(node_or_tags)
|
247
240
|
self.children = node_or_tags
|
248
241
|
self
|
249
242
|
end
|
@@ -255,7 +248,7 @@ module Nokogiri
|
|
255
248
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
256
249
|
#
|
257
250
|
# Also see related method +inner_html=+
|
258
|
-
def children=
|
251
|
+
def children=(node_or_tags)
|
259
252
|
node_or_tags = coerce(node_or_tags)
|
260
253
|
children.unlink
|
261
254
|
if node_or_tags.is_a?(XML::NodeSet)
|
@@ -273,13 +266,13 @@ module Nokogiri
|
|
273
266
|
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
|
274
267
|
#
|
275
268
|
# Also see related method +swap+.
|
276
|
-
def replace
|
269
|
+
def replace(node_or_tags)
|
277
270
|
# We cannot replace a text node directly, otherwise libxml will return
|
278
271
|
# an internal error at parser.c:13031, I don't know exactly why
|
279
272
|
# libxml is trying to find a parent node that is an element or document
|
280
273
|
# so I can't tell if this is bug in libxml or not. issue #775.
|
281
274
|
if text?
|
282
|
-
replacee = Nokogiri::XML::Node.new
|
275
|
+
replacee = Nokogiri::XML::Node.new "dummy", document
|
283
276
|
add_previous_sibling_node replacee
|
284
277
|
unlink
|
285
278
|
return replacee.replace node_or_tags
|
@@ -303,33 +296,98 @@ module Nokogiri
|
|
303
296
|
# Returns self, to support chaining of calls.
|
304
297
|
#
|
305
298
|
# Also see related method +replace+.
|
306
|
-
def swap
|
299
|
+
def swap(node_or_tags)
|
307
300
|
replace node_or_tags
|
308
301
|
self
|
309
302
|
end
|
310
303
|
|
311
|
-
|
312
|
-
|
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
|
313
309
|
|
314
|
-
|
315
|
-
#
|
316
|
-
|
317
|
-
|
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
|
318
316
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
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
|
326
|
+
|
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
|
333
391
|
|
334
392
|
####
|
335
393
|
# Returns a hash containing the node's attributes. The key is
|
@@ -370,82 +428,366 @@ module Nokogiri
|
|
370
428
|
end
|
371
429
|
|
372
430
|
###
|
373
|
-
#
|
374
|
-
|
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
|
+
#
|
375
458
|
def classes
|
376
|
-
|
459
|
+
kwattr_values("class")
|
377
460
|
end
|
378
461
|
|
379
|
-
|
380
|
-
#
|
381
|
-
#
|
382
|
-
#
|
383
|
-
#
|
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.
|
384
599
|
#
|
385
|
-
#
|
386
|
-
#
|
387
|
-
|
388
|
-
|
389
|
-
|
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)
|
390
663
|
self
|
391
664
|
end
|
392
665
|
|
393
|
-
|
394
|
-
#
|
395
|
-
#
|
396
|
-
#
|
397
|
-
#
|
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!
|
398
698
|
#
|
399
|
-
#
|
400
|
-
#
|
401
|
-
|
402
|
-
|
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)
|
403
715
|
self
|
404
716
|
end
|
405
717
|
|
406
|
-
|
407
|
-
#
|
408
|
-
#
|
409
|
-
# 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.
|
410
721
|
#
|
411
|
-
#
|
412
|
-
#
|
722
|
+
# If no keywords remain after this operation, or if +keywords+
|
723
|
+
# is +nil+, the attribute is deleted from the node.
|
413
724
|
#
|
414
|
-
#
|
415
|
-
#
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
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)
|
424
767
|
else
|
425
|
-
|
768
|
+
set_attribute(attribute_name, new_kws.join(" "))
|
426
769
|
end
|
427
770
|
self
|
428
771
|
end
|
429
772
|
|
430
|
-
###
|
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
773
|
alias :delete :remove_attribute
|
774
|
+
alias :get_attribute :[]
|
775
|
+
alias :attr :[]
|
776
|
+
alias :set_attribute :[]=
|
777
|
+
alias :has_attribute? :key?
|
778
|
+
|
779
|
+
# @!endgroup
|
438
780
|
|
439
781
|
###
|
440
782
|
# Returns true if this Node matches +selector+
|
441
|
-
def matches?
|
783
|
+
def matches?(selector)
|
442
784
|
ancestors.last.search(selector).include?(self)
|
443
785
|
end
|
444
786
|
|
445
787
|
###
|
446
788
|
# Create a DocumentFragment containing +tags+ that is relative to _this_
|
447
789
|
# context node.
|
448
|
-
def fragment
|
790
|
+
def fragment(tags)
|
449
791
|
type = document.html? ? Nokogiri::HTML : Nokogiri::XML
|
450
792
|
type::DocumentFragment.new(document, tags, self)
|
451
793
|
end
|
@@ -454,7 +796,7 @@ module Nokogiri
|
|
454
796
|
# Parse +string_or_io+ as a document fragment within the context of
|
455
797
|
# *this* node. Returns a XML::NodeSet containing the nodes parsed from
|
456
798
|
# +string_or_io+.
|
457
|
-
def parse
|
799
|
+
def parse(string_or_io, options = nil)
|
458
800
|
##
|
459
801
|
# When the current node is unparented and not an element node, use the
|
460
802
|
# document as the parsing context instead. Otherwise, the in-context
|
@@ -488,19 +830,6 @@ module Nokogiri
|
|
488
830
|
node_set
|
489
831
|
end
|
490
832
|
|
491
|
-
####
|
492
|
-
# Set the Node's content to a Text node containing +string+. The string gets XML escaped, not interpreted as markup.
|
493
|
-
def content= string
|
494
|
-
self.native_content = encode_special_chars(string.to_s)
|
495
|
-
end
|
496
|
-
|
497
|
-
###
|
498
|
-
# Set the parent Node for this Node
|
499
|
-
def parent= parent_node
|
500
|
-
parent_node.add_child(self)
|
501
|
-
parent_node
|
502
|
-
end
|
503
|
-
|
504
833
|
###
|
505
834
|
# Returns a Hash of +{prefix => value}+ for all namespaces on this
|
506
835
|
# node and its ancestors.
|
@@ -582,6 +911,7 @@ module Nokogiri
|
|
582
911
|
def element?
|
583
912
|
type == ELEMENT_NODE
|
584
913
|
end
|
914
|
+
|
585
915
|
alias :elem? :element?
|
586
916
|
|
587
917
|
###
|
@@ -592,7 +922,7 @@ module Nokogiri
|
|
592
922
|
end
|
593
923
|
|
594
924
|
# Get the inner_html for this node's Node#children
|
595
|
-
def inner_html
|
925
|
+
def inner_html(*args)
|
596
926
|
children.map { |x| x.to_html(*args) }.join
|
597
927
|
end
|
598
928
|
|
@@ -600,13 +930,13 @@ module Nokogiri
|
|
600
930
|
def css_path
|
601
931
|
path.split(/\//).map { |part|
|
602
932
|
part.length == 0 ? nil : part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
|
603
|
-
}.compact.join(
|
933
|
+
}.compact.join(" > ")
|
604
934
|
end
|
605
935
|
|
606
936
|
###
|
607
937
|
# Get a list of ancestor Node for this Node. If +selector+ is given,
|
608
938
|
# the ancestors must match +selector+
|
609
|
-
def ancestors
|
939
|
+
def ancestors(selector = nil)
|
610
940
|
return NodeSet.new(document) unless respond_to?(:parent)
|
611
941
|
return NodeSet.new(document) unless parent
|
612
942
|
|
@@ -627,57 +957,38 @@ module Nokogiri
|
|
627
957
|
})
|
628
958
|
end
|
629
959
|
|
630
|
-
###
|
631
|
-
# Adds a default namespace supplied as a string +url+ href, to self.
|
632
|
-
# The consequence is as an xmlns attribute with supplied argument were
|
633
|
-
# present in parsed XML. A default namespace set with this method will
|
634
|
-
# now show up in #attributes, but when this node is serialized to XML an
|
635
|
-
# "xmlns" attribute will appear. See also #namespace and #namespace=
|
636
|
-
def default_namespace= url
|
637
|
-
add_namespace_definition(nil, url)
|
638
|
-
end
|
639
|
-
alias :add_namespace :add_namespace_definition
|
640
|
-
|
641
|
-
###
|
642
|
-
# Set the default namespace on this node (as would be defined with an
|
643
|
-
# "xmlns=" attribute in XML source), as a Namespace object +ns+. Note that
|
644
|
-
# a Namespace added this way will NOT be serialized as an xmlns attribute
|
645
|
-
# for this node. You probably want #default_namespace= instead, or perhaps
|
646
|
-
# #add_namespace_definition with a nil prefix argument.
|
647
|
-
def namespace= ns
|
648
|
-
return set_namespace(ns) unless ns
|
649
|
-
|
650
|
-
unless Nokogiri::XML::Namespace === ns
|
651
|
-
raise TypeError, "#{ns.class} can't be coerced into Nokogiri::XML::Namespace"
|
652
|
-
end
|
653
|
-
if ns.document != document
|
654
|
-
raise ArgumentError, 'namespace must be declared on the same document'
|
655
|
-
end
|
656
|
-
|
657
|
-
set_namespace ns
|
658
|
-
end
|
659
|
-
|
660
960
|
####
|
661
961
|
# Yields self and all children to +block+ recursively.
|
662
|
-
def traverse
|
663
|
-
children.each{|j| j.traverse(&block) }
|
962
|
+
def traverse(&block)
|
963
|
+
children.each { |j| j.traverse(&block) }
|
664
964
|
block.call(self)
|
665
965
|
end
|
666
966
|
|
667
967
|
###
|
668
968
|
# Accept a visitor. This method calls "visit" on +visitor+ with self.
|
669
|
-
def accept
|
969
|
+
def accept(visitor)
|
670
970
|
visitor.visit(self)
|
671
971
|
end
|
672
972
|
|
673
973
|
###
|
674
974
|
# Test to see if this Node is equal to +other+
|
675
|
-
def ==
|
975
|
+
def ==(other)
|
676
976
|
return false unless other
|
677
977
|
return false unless other.respond_to?(:pointer_id)
|
678
978
|
pointer_id == other.pointer_id
|
679
979
|
end
|
680
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
|
+
|
681
992
|
###
|
682
993
|
# Serialize Node using +options+. Save options can also be set using a
|
683
994
|
# block. See SaveOptions.
|
@@ -692,17 +1003,17 @@ module Nokogiri
|
|
692
1003
|
# config.format.as_xml
|
693
1004
|
# end
|
694
1005
|
#
|
695
|
-
def serialize
|
1006
|
+
def serialize(*args, &block)
|
696
1007
|
options = args.first.is_a?(Hash) ? args.shift : {
|
697
|
-
:encoding
|
698
|
-
:save_with
|
1008
|
+
:encoding => args[0],
|
1009
|
+
:save_with => args[1],
|
699
1010
|
}
|
700
1011
|
|
701
1012
|
encoding = options[:encoding] || document.encoding
|
702
1013
|
options[:encoding] = encoding
|
703
1014
|
|
704
1015
|
outstring = String.new
|
705
|
-
outstring.force_encoding(Encoding.find(encoding ||
|
1016
|
+
outstring.force_encoding(Encoding.find(encoding || "utf-8"))
|
706
1017
|
io = StringIO.new(outstring)
|
707
1018
|
write_to io, options, &block
|
708
1019
|
io.string
|
@@ -715,7 +1026,7 @@ module Nokogiri
|
|
715
1026
|
#
|
716
1027
|
# See Node#write_to for a list of +options+. For formatted output,
|
717
1028
|
# use Node#to_xhtml instead.
|
718
|
-
def to_html
|
1029
|
+
def to_html(options = {})
|
719
1030
|
to_format SaveOptions::DEFAULT_HTML, options
|
720
1031
|
end
|
721
1032
|
|
@@ -725,7 +1036,7 @@ module Nokogiri
|
|
725
1036
|
# doc.to_xml(:indent => 5, :encoding => 'UTF-8')
|
726
1037
|
#
|
727
1038
|
# See Node#write_to for a list of +options+
|
728
|
-
def to_xml
|
1039
|
+
def to_xml(options = {})
|
729
1040
|
options[:save_with] ||= SaveOptions::DEFAULT_XML
|
730
1041
|
serialize(options)
|
731
1042
|
end
|
@@ -736,7 +1047,7 @@ module Nokogiri
|
|
736
1047
|
# doc.to_xhtml(:indent => 5, :encoding => 'UTF-8')
|
737
1048
|
#
|
738
1049
|
# See Node#write_to for a list of +options+
|
739
|
-
def to_xhtml
|
1050
|
+
def to_xhtml(options = {})
|
740
1051
|
to_format SaveOptions::DEFAULT_XHTML, options
|
741
1052
|
end
|
742
1053
|
|
@@ -757,22 +1068,22 @@ module Nokogiri
|
|
757
1068
|
#
|
758
1069
|
# node.write_to(io, :indent_text => '-', :indent => 2)
|
759
1070
|
#
|
760
|
-
def write_to
|
761
|
-
options
|
762
|
-
encoding
|
1071
|
+
def write_to(io, *options)
|
1072
|
+
options = options.first.is_a?(Hash) ? options.shift : {}
|
1073
|
+
encoding = options[:encoding] || options[0]
|
763
1074
|
if Nokogiri.jruby?
|
764
|
-
save_options
|
765
|
-
indent_times
|
1075
|
+
save_options = options[:save_with] || options[1]
|
1076
|
+
indent_times = options[:indent] || 0
|
766
1077
|
else
|
767
|
-
save_options
|
768
|
-
indent_times
|
1078
|
+
save_options = options[:save_with] || options[1] || SaveOptions::FORMAT
|
1079
|
+
indent_times = options[:indent] || 2
|
769
1080
|
end
|
770
|
-
indent_text
|
1081
|
+
indent_text = options[:indent_text] || " "
|
771
1082
|
|
772
1083
|
# Any string times 0 returns an empty string. Therefore, use the same
|
773
1084
|
# string instead of generating a new empty string for every node with
|
774
1085
|
# zero indentation.
|
775
|
-
indentation = indent_times.zero? ?
|
1086
|
+
indentation = indent_times.zero? ? "" : (indent_text * indent_times)
|
776
1087
|
|
777
1088
|
config = SaveOptions.new(save_options.to_i)
|
778
1089
|
yield config if block_given?
|
@@ -784,7 +1095,7 @@ module Nokogiri
|
|
784
1095
|
# Write Node as HTML to +io+ with +options+
|
785
1096
|
#
|
786
1097
|
# See Node#write_to for a list of +options+
|
787
|
-
def write_html_to
|
1098
|
+
def write_html_to(io, options = {})
|
788
1099
|
write_format_to SaveOptions::DEFAULT_HTML, io, options
|
789
1100
|
end
|
790
1101
|
|
@@ -792,7 +1103,7 @@ module Nokogiri
|
|
792
1103
|
# Write Node as XHTML to +io+ with +options+
|
793
1104
|
#
|
794
1105
|
# See Node#write_to for a list of +options+
|
795
|
-
def write_xhtml_to
|
1106
|
+
def write_xhtml_to(io, options = {})
|
796
1107
|
write_format_to SaveOptions::DEFAULT_XHTML, io, options
|
797
1108
|
end
|
798
1109
|
|
@@ -802,35 +1113,12 @@ module Nokogiri
|
|
802
1113
|
# doc.write_xml_to io, :encoding => 'UTF-8'
|
803
1114
|
#
|
804
1115
|
# See Node#write_to for a list of options
|
805
|
-
def write_xml_to
|
1116
|
+
def write_xml_to(io, options = {})
|
806
1117
|
options[:save_with] ||= SaveOptions::DEFAULT_XML
|
807
1118
|
write_to io, options
|
808
1119
|
end
|
809
1120
|
|
810
|
-
|
811
|
-
# Compare two Node objects with respect to their Document. Nodes from
|
812
|
-
# different documents cannot be compared.
|
813
|
-
def <=> other
|
814
|
-
return nil unless other.is_a?(Nokogiri::XML::Node)
|
815
|
-
return nil unless document == other.document
|
816
|
-
compare other
|
817
|
-
end
|
818
|
-
|
819
|
-
###
|
820
|
-
# Do xinclude substitution on the subtree below node. If given a block, a
|
821
|
-
# Nokogiri::XML::ParseOptions object initialized from +options+, will be
|
822
|
-
# passed to it, allowing more convenient modification of the parser options.
|
823
|
-
def do_xinclude options = XML::ParseOptions::DEFAULT_XML
|
824
|
-
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
825
|
-
|
826
|
-
# give options to user
|
827
|
-
yield options if block_given?
|
828
|
-
|
829
|
-
# call c extension
|
830
|
-
process_xincludes(options.to_i)
|
831
|
-
end
|
832
|
-
|
833
|
-
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)
|
834
1122
|
c14n_root = self
|
835
1123
|
document.canonicalize(mode, inclusive_namespaces, with_comments) do |node, parent|
|
836
1124
|
tn = node.is_a?(XML::Node) ? node : parent
|
@@ -838,16 +1126,29 @@ module Nokogiri
|
|
838
1126
|
end
|
839
1127
|
end
|
840
1128
|
|
1129
|
+
# @!endgroup
|
1130
|
+
|
841
1131
|
private
|
842
1132
|
|
843
|
-
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)
|
844
1145
|
impl = (next_or_previous == :next) ? :add_next_sibling_node : :add_previous_sibling_node
|
845
|
-
iter = (next_or_previous == :next) ? :reverse_each
|
1146
|
+
iter = (next_or_previous == :next) ? :reverse_each : :each
|
846
1147
|
|
847
1148
|
node_or_tags = coerce node_or_tags
|
848
1149
|
if node_or_tags.is_a?(XML::NodeSet)
|
849
1150
|
if text?
|
850
|
-
pivot = Nokogiri::XML::Node.new
|
1151
|
+
pivot = Nokogiri::XML::Node.new "dummy", document
|
851
1152
|
send impl, pivot
|
852
1153
|
else
|
853
1154
|
pivot = self
|
@@ -863,14 +1164,14 @@ module Nokogiri
|
|
863
1164
|
USING_LIBXML_WITH_BROKEN_SERIALIZATION = Nokogiri.uses_libxml?("~> 2.6.0").freeze
|
864
1165
|
private_constant :USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
865
1166
|
|
866
|
-
def to_format
|
1167
|
+
def to_format(save_option, options)
|
867
1168
|
return dump_html if USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
868
1169
|
|
869
1170
|
options[:save_with] = save_option unless options[:save_with]
|
870
1171
|
serialize(options)
|
871
1172
|
end
|
872
1173
|
|
873
|
-
def write_format_to
|
1174
|
+
def write_format_to(save_option, io, options)
|
874
1175
|
return (io << dump_html) if USING_LIBXML_WITH_BROKEN_SERIALIZATION
|
875
1176
|
|
876
1177
|
options[:save_with] ||= save_option
|
@@ -881,7 +1182,7 @@ module Nokogiri
|
|
881
1182
|
[:name, :namespace, :attribute_nodes, :children]
|
882
1183
|
end
|
883
1184
|
|
884
|
-
def coerce
|
1185
|
+
def coerce(data)
|
885
1186
|
case data
|
886
1187
|
when XML::NodeSet
|
887
1188
|
return data
|
@@ -902,9 +1203,9 @@ Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
|
902
1203
|
end
|
903
1204
|
|
904
1205
|
# @private
|
905
|
-
IMPLIED_XPATH_CONTEXTS = [
|
1206
|
+
IMPLIED_XPATH_CONTEXTS = [".//".freeze].freeze
|
906
1207
|
|
907
|
-
def add_child_node_and_reparent_attrs
|
1208
|
+
def add_child_node_and_reparent_attrs(node)
|
908
1209
|
add_child_node node
|
909
1210
|
node.attribute_nodes.find_all { |a| a.name =~ /:/ }.each do |attr_node|
|
910
1211
|
attr_node.remove
|