nokolexbor 0.3.0-x64-mingw-ucrt
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/nokolexbor/3.1/nokolexbor.so +0 -0
- data/lib/nokolexbor/attribute.rb +18 -0
- data/lib/nokolexbor/document.rb +97 -0
- data/lib/nokolexbor/node.rb +348 -0
- data/lib/nokolexbor/node_set.rb +131 -0
- data/lib/nokolexbor/version.rb +5 -0
- data/lib/nokolexbor/xpath.rb +69 -0
- data/lib/nokolexbor/xpath_context.rb +14 -0
- data/lib/nokolexbor.rb +38 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 75ac569a01b9281d1392eb495d5358eef2ce76fe420c4b2fd768d46df47904c1
|
4
|
+
data.tar.gz: b4d46dff993b2b8bc73b63356bb6abd2ca9721297226887d56d5e5f453406b58
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8b4db936db5a609ad470033f1946cbaf495ed399d84b6a994ad51e86fd2a163e9c879103e5b2e5c51d23afc04ef5154896b68d2cc97e15b12c460b6c81588f2b
|
7
|
+
data.tar.gz: ec47117ce93ffe34e622df54cc981b0a52d2b8d344f17a730093becc46309d1650966872c222d00d076a13a014285dd2ace63ac996c67be75441ff47393e5ddd
|
Binary file
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nokolexbor
|
4
|
+
class Attribute
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :value
|
7
|
+
|
8
|
+
def initialize(name, value)
|
9
|
+
@name = name
|
10
|
+
@value = value
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :text, :value
|
14
|
+
alias_method :content, :value
|
15
|
+
alias_method :to_s, :value
|
16
|
+
alias_method :to_str, :value
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nokolexbor
|
4
|
+
class Document < Nokolexbor::Node
|
5
|
+
def create_element(name, *contents_or_attrs, &block)
|
6
|
+
elm = Nokolexbor::Element.new(name, self, &block)
|
7
|
+
contents_or_attrs.each do |arg|
|
8
|
+
case arg
|
9
|
+
when Hash
|
10
|
+
arg.each do |k, v|
|
11
|
+
elm[k.to_s] = v.to_s
|
12
|
+
end
|
13
|
+
else
|
14
|
+
elm.content = arg
|
15
|
+
end
|
16
|
+
end
|
17
|
+
elm
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create a Text Node with +string+
|
21
|
+
def create_text_node(string, &block)
|
22
|
+
Nokolexbor::Text.new(string.to_s, self, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create a CDATA Node containing +string+
|
26
|
+
def create_cdata(string, &block)
|
27
|
+
Nokolexbor::CDATA.new(string.to_s, self, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create a Comment Node containing +string+
|
31
|
+
def create_comment(string, &block)
|
32
|
+
Nokolexbor::Comment.new(string.to_s, self, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# A reference to +self+
|
36
|
+
def document
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def meta_encoding
|
41
|
+
if (meta = at_css("meta[charset]"))
|
42
|
+
meta[:charset]
|
43
|
+
elsif (meta = meta_content_type)
|
44
|
+
meta["content"][/charset\s*=\s*([\w-]+)/i, 1]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def meta_encoding=(encoding)
|
49
|
+
if (meta = meta_content_type)
|
50
|
+
meta["content"] = format("text/html; charset=%s", encoding)
|
51
|
+
encoding
|
52
|
+
elsif (meta = at_css("meta[charset]"))
|
53
|
+
meta["charset"] = encoding
|
54
|
+
else
|
55
|
+
meta = Nokolexbor::Node.new("meta", self)
|
56
|
+
meta["charset"] = encoding
|
57
|
+
|
58
|
+
if (head = at_css("head"))
|
59
|
+
head.prepend_child(meta)
|
60
|
+
else
|
61
|
+
set_metadata_element(meta)
|
62
|
+
end
|
63
|
+
encoding
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def meta_content_type
|
68
|
+
xpath("//meta[@http-equiv and boolean(@content)]").find do |node|
|
69
|
+
node["http-equiv"] =~ /\AContent-Type\z/i
|
70
|
+
end
|
71
|
+
end
|
72
|
+
private :meta_content_type
|
73
|
+
|
74
|
+
def set_metadata_element(element)
|
75
|
+
if (head = at_css("head"))
|
76
|
+
head << element
|
77
|
+
elsif (html = at_css("html"))
|
78
|
+
head = html.prepend_child(Nokolexbor::Node.new("head", self))
|
79
|
+
head.prepend_child(element)
|
80
|
+
elsif (first = children.find do |node|
|
81
|
+
case node
|
82
|
+
when Nokolexbor::Node
|
83
|
+
true
|
84
|
+
end
|
85
|
+
end)
|
86
|
+
# We reach here only if the underlying document model
|
87
|
+
# allows <html>/<head> elements to be omitted and does not
|
88
|
+
# automatically supply them.
|
89
|
+
first.add_previous_sibling(element)
|
90
|
+
else
|
91
|
+
html = add_child(Nokolexbor::Node.new("html", self))
|
92
|
+
head = html.add_child(Nokolexbor::Node.new("head", self))
|
93
|
+
head.prepend_child(element)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,348 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nokolexbor
|
4
|
+
class Node
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
ELEMENT_NODE = 1
|
8
|
+
ATTRIBUTE_NODE = 2
|
9
|
+
TEXT_NODE = 3
|
10
|
+
CDATA_SECTION_NODE = 4
|
11
|
+
ENTITY_REF_NODE = 5
|
12
|
+
ENTITY_NODE = 6
|
13
|
+
PI_NODE = 7
|
14
|
+
COMMENT_NODE = 8
|
15
|
+
DOCUMENT_NODE = 9
|
16
|
+
DOCUMENT_TYPE_NODE = 10
|
17
|
+
DOCUMENT_FRAG_NODE = 11
|
18
|
+
NOTATION_NODE = 12
|
19
|
+
|
20
|
+
attr_reader :document
|
21
|
+
|
22
|
+
LOOKS_LIKE_XPATH = %r{^(\./|/|\.\.|\.$)}
|
23
|
+
|
24
|
+
def comment?
|
25
|
+
type == COMMENT_NODE
|
26
|
+
end
|
27
|
+
|
28
|
+
def cdata?
|
29
|
+
type == CDATA_SECTION_NODE
|
30
|
+
end
|
31
|
+
|
32
|
+
def processing_instruction?
|
33
|
+
type == PI_NODE
|
34
|
+
end
|
35
|
+
|
36
|
+
def text?
|
37
|
+
type == TEXT_NODE
|
38
|
+
end
|
39
|
+
|
40
|
+
def fragment?
|
41
|
+
type == DOCUMENT_FRAG_NODE
|
42
|
+
end
|
43
|
+
|
44
|
+
def element?
|
45
|
+
type == ELEMENT_NODE
|
46
|
+
end
|
47
|
+
|
48
|
+
def document?
|
49
|
+
is_a?(Nokolexbor::Document)
|
50
|
+
end
|
51
|
+
|
52
|
+
def ancestors(selector = nil)
|
53
|
+
return NodeSet.new(@document) unless respond_to?(:parent)
|
54
|
+
return NodeSet.new(@document) unless parent
|
55
|
+
|
56
|
+
parents = [parent]
|
57
|
+
|
58
|
+
while parents.last.respond_to?(:parent)
|
59
|
+
break unless (ctx_parent = parents.last.parent)
|
60
|
+
|
61
|
+
parents << ctx_parent
|
62
|
+
end
|
63
|
+
|
64
|
+
return NodeSet.new(@document, parents) unless selector
|
65
|
+
|
66
|
+
root = parents.last
|
67
|
+
search_results = root.search(selector)
|
68
|
+
|
69
|
+
NodeSet.new(@document, parents.find_all do |parent|
|
70
|
+
search_results.include?(parent)
|
71
|
+
end)
|
72
|
+
end
|
73
|
+
|
74
|
+
def wrap(node)
|
75
|
+
case node
|
76
|
+
when String
|
77
|
+
new_parent = fragment(node).child
|
78
|
+
when Node
|
79
|
+
new_parent = node.dup
|
80
|
+
else
|
81
|
+
raise ArgumentError, "Requires a String or Node argument, and cannot accept a #{node.class}"
|
82
|
+
end
|
83
|
+
|
84
|
+
if parent
|
85
|
+
add_sibling(:next, new_parent)
|
86
|
+
else
|
87
|
+
new_parent.remove
|
88
|
+
end
|
89
|
+
new_parent.add_child(self)
|
90
|
+
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_previous_sibling(node_or_tags)
|
95
|
+
raise ArgumentError,
|
96
|
+
"A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
97
|
+
|
98
|
+
add_sibling(:previous, node_or_tags)
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_next_sibling(node_or_tags)
|
102
|
+
raise ArgumentError,
|
103
|
+
"A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
|
104
|
+
|
105
|
+
add_sibling(:next, node_or_tags)
|
106
|
+
end
|
107
|
+
|
108
|
+
def before(node_or_tags)
|
109
|
+
add_previous_sibling(node_or_tags)
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def after(node_or_tags)
|
114
|
+
add_next_sibling(node_or_tags)
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
alias_method :next_sibling, :next
|
119
|
+
alias_method :previous_sibling, :previous
|
120
|
+
alias_method :next=, :add_next_sibling
|
121
|
+
alias_method :previous=, :add_previous_sibling
|
122
|
+
|
123
|
+
def <<(node_or_tags)
|
124
|
+
add_child(node_or_tags)
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
def prepend_child(node)
|
129
|
+
if (first = children.first)
|
130
|
+
# Mimic the error add_child would raise.
|
131
|
+
raise "Document already has a root node" if document? && !(node.comment? || node.processing_instruction?)
|
132
|
+
|
133
|
+
first.add_sibling(:previous, node)
|
134
|
+
else
|
135
|
+
add_child(node)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def traverse(&block)
|
140
|
+
children.each { |j| j.traverse(&block) }
|
141
|
+
yield(self)
|
142
|
+
end
|
143
|
+
|
144
|
+
def matches?(selector)
|
145
|
+
ancestors.last.css(selector).any? { |node| node == self }
|
146
|
+
end
|
147
|
+
|
148
|
+
def attribute(name)
|
149
|
+
return nil unless key?(name)
|
150
|
+
Attribute.new(name, attr(name))
|
151
|
+
end
|
152
|
+
|
153
|
+
def attributes
|
154
|
+
attrs.map { |k, v| [k, Attribute.new(k, v)] }.to_h
|
155
|
+
end
|
156
|
+
|
157
|
+
def replace(node)
|
158
|
+
if node.is_a?(NodeSet)
|
159
|
+
node.each { |n| add_sibling(:previous, n) }
|
160
|
+
else
|
161
|
+
add_sibling(:previous, node)
|
162
|
+
end
|
163
|
+
remove
|
164
|
+
end
|
165
|
+
|
166
|
+
def children=(node)
|
167
|
+
children.remove
|
168
|
+
if node.is_a?(NodeSet)
|
169
|
+
node.each { |n| add_child(n) }
|
170
|
+
else
|
171
|
+
add_child(node)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def parent=(parent_node)
|
176
|
+
parent_node.add_child(self)
|
177
|
+
end
|
178
|
+
|
179
|
+
def each
|
180
|
+
attributes.each do |name, node|
|
181
|
+
yield [name, node.value]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
alias_method :inner_html=, :children=
|
186
|
+
|
187
|
+
def css(*args)
|
188
|
+
css_impl(args.join(', '))
|
189
|
+
end
|
190
|
+
|
191
|
+
def at_css(*args)
|
192
|
+
at_css_impl(args.join(', '))
|
193
|
+
end
|
194
|
+
|
195
|
+
def xpath(*args)
|
196
|
+
paths, handler, ns, binds = extract_params(args)
|
197
|
+
|
198
|
+
xpath_internal(self, paths, handler, ns, binds)
|
199
|
+
end
|
200
|
+
|
201
|
+
def at_xpath(*args)
|
202
|
+
xpath(*args).first
|
203
|
+
end
|
204
|
+
|
205
|
+
def search(*args)
|
206
|
+
paths, handler, ns, binds = extract_params(args)
|
207
|
+
|
208
|
+
if paths.size == 1 && !LOOKS_LIKE_XPATH.match?(paths.first)
|
209
|
+
return css(paths.first)
|
210
|
+
end
|
211
|
+
|
212
|
+
xpath(*(paths + [ns, handler, binds].compact))
|
213
|
+
end
|
214
|
+
|
215
|
+
alias_method :/, :search
|
216
|
+
|
217
|
+
def at(*args)
|
218
|
+
paths, handler, ns, binds = extract_params(args)
|
219
|
+
|
220
|
+
if paths.size == 1 && !LOOKS_LIKE_XPATH.match?(paths.first)
|
221
|
+
return at_css(paths.first)
|
222
|
+
end
|
223
|
+
|
224
|
+
at_xpath(*(paths + [ns, handler, binds].compact))
|
225
|
+
end
|
226
|
+
|
227
|
+
alias_method :%, :at
|
228
|
+
|
229
|
+
def classes
|
230
|
+
kwattr_values("class")
|
231
|
+
end
|
232
|
+
|
233
|
+
def add_class(names)
|
234
|
+
kwattr_add("class", names)
|
235
|
+
end
|
236
|
+
|
237
|
+
def append_class(names)
|
238
|
+
kwattr_append("class", names)
|
239
|
+
end
|
240
|
+
|
241
|
+
def remove_class(names = nil)
|
242
|
+
kwattr_remove("class", names)
|
243
|
+
end
|
244
|
+
|
245
|
+
def kwattr_values(attribute_name)
|
246
|
+
keywordify(attr(attribute_name) || [])
|
247
|
+
end
|
248
|
+
|
249
|
+
def kwattr_add(attribute_name, keywords)
|
250
|
+
keywords = keywordify(keywords)
|
251
|
+
current_kws = kwattr_values(attribute_name)
|
252
|
+
new_kws = (current_kws + (keywords - current_kws)).join(" ")
|
253
|
+
set_attr(attribute_name, new_kws)
|
254
|
+
self
|
255
|
+
end
|
256
|
+
|
257
|
+
def kwattr_append(attribute_name, keywords)
|
258
|
+
keywords = keywordify(keywords)
|
259
|
+
current_kws = kwattr_values(attribute_name)
|
260
|
+
new_kws = (current_kws + keywords).join(" ")
|
261
|
+
set_attr(attribute_name, new_kws)
|
262
|
+
self
|
263
|
+
end
|
264
|
+
|
265
|
+
def kwattr_remove(attribute_name, keywords)
|
266
|
+
if keywords.nil?
|
267
|
+
remove_attr(attribute_name)
|
268
|
+
return self
|
269
|
+
end
|
270
|
+
|
271
|
+
keywords = keywordify(keywords)
|
272
|
+
current_kws = kwattr_values(attribute_name)
|
273
|
+
new_kws = current_kws - keywords
|
274
|
+
if new_kws.empty?
|
275
|
+
remove_attr(attribute_name)
|
276
|
+
else
|
277
|
+
set_attr(attribute_name, new_kws.join(" "))
|
278
|
+
end
|
279
|
+
self
|
280
|
+
end
|
281
|
+
|
282
|
+
def keywordify(keywords)
|
283
|
+
case keywords
|
284
|
+
when Enumerable
|
285
|
+
keywords
|
286
|
+
when String
|
287
|
+
keywords.scan(/\S+/)
|
288
|
+
else
|
289
|
+
raise ArgumentError,
|
290
|
+
"Keyword attributes must be passed as either a String or an Enumerable, but received #{keywords.class}"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def write_to(io, *options)
|
295
|
+
io.write(to_html(*options))
|
296
|
+
end
|
297
|
+
|
298
|
+
alias_method :write_html_to, :write_to
|
299
|
+
|
300
|
+
private
|
301
|
+
|
302
|
+
def xpath_internal(node, paths, handler, ns, binds)
|
303
|
+
# document = node.document
|
304
|
+
# return NodeSet.new(document) unless document
|
305
|
+
|
306
|
+
if paths.length == 1
|
307
|
+
return xpath_impl(node, paths.first, handler, ns, binds)
|
308
|
+
end
|
309
|
+
|
310
|
+
NodeSet.new(@document) do |combined|
|
311
|
+
paths.each do |path|
|
312
|
+
xpath_impl(node, path, handler, ns, binds).each { |set| combined << set }
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def xpath_impl(node, path, handler, ns, binds)
|
318
|
+
ctx = XPathContext.new(node)
|
319
|
+
ctx.register_namespaces(ns)
|
320
|
+
# path = path.gsub(/xmlns:/, " :") unless Nokogiri.uses_libxml?
|
321
|
+
|
322
|
+
binds&.each do |key, value|
|
323
|
+
ctx.register_variable(key.to_s, value)
|
324
|
+
end
|
325
|
+
|
326
|
+
ctx.evaluate(path, handler)
|
327
|
+
end
|
328
|
+
|
329
|
+
def extract_params(params)
|
330
|
+
handler = params.find do |param|
|
331
|
+
![Hash, String, Symbol].include?(param.class)
|
332
|
+
end
|
333
|
+
params -= [handler] if handler
|
334
|
+
|
335
|
+
hashes = []
|
336
|
+
while Hash === params.last || params.last.nil?
|
337
|
+
hashes << params.pop
|
338
|
+
break if params.empty?
|
339
|
+
end
|
340
|
+
ns, binds = hashes.reverse
|
341
|
+
|
342
|
+
# ns ||= (document.root&.namespaces || {})
|
343
|
+
ns ||= {}
|
344
|
+
|
345
|
+
[params, handler, ns, binds]
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nokolexbor
|
4
|
+
class NodeSet < Nokolexbor::Node
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def self.new(document, list = [])
|
8
|
+
obj = allocate
|
9
|
+
obj.instance_variable_set(:@document, document)
|
10
|
+
list.each { |x| obj << x }
|
11
|
+
yield obj if block_given?
|
12
|
+
obj
|
13
|
+
end
|
14
|
+
|
15
|
+
def each
|
16
|
+
return to_enum unless block_given?
|
17
|
+
|
18
|
+
0.upto(length - 1) do |x|
|
19
|
+
yield self[x]
|
20
|
+
end
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def first(n = nil)
|
25
|
+
return self[0] unless n
|
26
|
+
|
27
|
+
list = []
|
28
|
+
[n, length].min.times { |i| list << self[i] }
|
29
|
+
list
|
30
|
+
end
|
31
|
+
|
32
|
+
def last
|
33
|
+
self[-1]
|
34
|
+
end
|
35
|
+
|
36
|
+
def empty?
|
37
|
+
length == 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def index(node = nil)
|
41
|
+
if node
|
42
|
+
each_with_index { |member, j| return j if member == node }
|
43
|
+
elsif block_given?
|
44
|
+
each_with_index { |member, j| return j if yield(member) }
|
45
|
+
end
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def content
|
50
|
+
self.map(&:content).join
|
51
|
+
end
|
52
|
+
|
53
|
+
alias_method :text, :content
|
54
|
+
alias_method :inner_text, :content
|
55
|
+
alias_method :to_str, :content
|
56
|
+
|
57
|
+
def inner_html(*args)
|
58
|
+
self.map { |n| n.inner_html(*args) }.join
|
59
|
+
end
|
60
|
+
|
61
|
+
def outer_html(*args)
|
62
|
+
self.map { |n| n.outer_html(*args) }.join
|
63
|
+
end
|
64
|
+
|
65
|
+
alias_method :to_s, :outer_html
|
66
|
+
alias_method :to_html, :outer_html
|
67
|
+
alias_method :serialize, :outer_html
|
68
|
+
|
69
|
+
def remove
|
70
|
+
self.each(&:remove)
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :unlink, :remove
|
74
|
+
alias_method :to_ary, :to_a
|
75
|
+
|
76
|
+
def destroy
|
77
|
+
self.each(&:destroy)
|
78
|
+
end
|
79
|
+
|
80
|
+
def pop
|
81
|
+
return nil if length == 0
|
82
|
+
|
83
|
+
delete(last)
|
84
|
+
end
|
85
|
+
|
86
|
+
def shift
|
87
|
+
return nil if length == 0
|
88
|
+
|
89
|
+
delete(first)
|
90
|
+
end
|
91
|
+
|
92
|
+
def ==(other)
|
93
|
+
return false unless other.is_a?(NodeSet)
|
94
|
+
return false unless length == other.length
|
95
|
+
|
96
|
+
each_with_index do |node, i|
|
97
|
+
return false unless node == other[i]
|
98
|
+
end
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def children
|
103
|
+
node_set = NodeSet.new(@document)
|
104
|
+
each do |node|
|
105
|
+
node.children.each { |n| node_set.push(n) }
|
106
|
+
end
|
107
|
+
node_set
|
108
|
+
end
|
109
|
+
|
110
|
+
def reverse
|
111
|
+
node_set = NodeSet.new(@document)
|
112
|
+
(length - 1).downto(0) do |x|
|
113
|
+
node_set.push(self[x])
|
114
|
+
end
|
115
|
+
node_set
|
116
|
+
end
|
117
|
+
|
118
|
+
def xpath(*args)
|
119
|
+
paths, handler, ns, binds = extract_params(args)
|
120
|
+
|
121
|
+
NodeSet.new(@document) do |set|
|
122
|
+
each do |node|
|
123
|
+
node.send(:xpath_internal, node, paths, handler, ns, binds).each do |inner_node|
|
124
|
+
set << inner_node
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nokolexbor
|
4
|
+
module XPath
|
5
|
+
class SyntaxError < StandardError
|
6
|
+
attr_reader :domain
|
7
|
+
attr_reader :code
|
8
|
+
attr_reader :level
|
9
|
+
attr_reader :file
|
10
|
+
attr_reader :line
|
11
|
+
attr_reader :str1
|
12
|
+
attr_reader :str2
|
13
|
+
attr_reader :str3
|
14
|
+
attr_reader :int1
|
15
|
+
attr_reader :column
|
16
|
+
|
17
|
+
###
|
18
|
+
# return true if this is a non error
|
19
|
+
def none?
|
20
|
+
level == 0
|
21
|
+
end
|
22
|
+
|
23
|
+
###
|
24
|
+
# return true if this is a warning
|
25
|
+
def warning?
|
26
|
+
level == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
###
|
30
|
+
# return true if this is an error
|
31
|
+
def error?
|
32
|
+
level == 2
|
33
|
+
end
|
34
|
+
|
35
|
+
###
|
36
|
+
# return true if this error is fatal
|
37
|
+
def fatal?
|
38
|
+
level == 3
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
message = super.chomp
|
43
|
+
[location_to_s, level_to_s, message]
|
44
|
+
.compact.join(": ")
|
45
|
+
.force_encoding(message.encoding)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def level_to_s
|
51
|
+
case level
|
52
|
+
when 3 then "FATAL"
|
53
|
+
when 2 then "ERROR"
|
54
|
+
when 1 then "WARNING"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def nil_or_zero?(attribute)
|
59
|
+
attribute.nil? || attribute.zero?
|
60
|
+
end
|
61
|
+
|
62
|
+
def location_to_s
|
63
|
+
return nil if nil_or_zero?(line) && nil_or_zero?(column)
|
64
|
+
|
65
|
+
"#{line}:#{column}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nokolexbor
|
4
|
+
class XPathContext
|
5
|
+
###
|
6
|
+
# Register namespaces in +namespaces+
|
7
|
+
def register_namespaces(namespaces)
|
8
|
+
namespaces.each do |k, v|
|
9
|
+
k = k.to_s.gsub(/.*:/, "") # strip off 'xmlns:' or 'xml:'
|
10
|
+
register_ns(k, v)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/nokolexbor.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
# pre-compiled extension by rake-compiler is located inside lib/nokolexbor/<ruby_version>/
|
5
|
+
RUBY_VERSION =~ /(\d+\.\d+)/
|
6
|
+
require "nokolexbor/#{Regexp.last_match(1)}/nokolexbor"
|
7
|
+
rescue LoadError => e
|
8
|
+
if /GLIBC/.match?(e.message)
|
9
|
+
warn(<<~EOM)
|
10
|
+
ERROR: It looks like you're trying to use Nokolexbor as a precompiled native gem on a system
|
11
|
+
with an unsupported version of glibc.
|
12
|
+
#{e.message}
|
13
|
+
If that's the case, then please install Nokolexbor via the `ruby` platform gem:
|
14
|
+
gem install nokolexbor --platform=ruby
|
15
|
+
or:
|
16
|
+
bundle config set force_ruby_platform true
|
17
|
+
EOM
|
18
|
+
raise e
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'nokolexbor/nokolexbor'
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'nokolexbor/version'
|
25
|
+
require 'nokolexbor/node'
|
26
|
+
require 'nokolexbor/document'
|
27
|
+
require 'nokolexbor/node_set'
|
28
|
+
require 'nokolexbor/attribute'
|
29
|
+
require 'nokolexbor/xpath'
|
30
|
+
require 'nokolexbor/xpath_context'
|
31
|
+
|
32
|
+
module Nokolexbor
|
33
|
+
class << self
|
34
|
+
def HTML(*args)
|
35
|
+
Document.parse(*args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nokolexbor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: x64-mingw-ucrt
|
6
|
+
authors:
|
7
|
+
- Yicheng Zhou
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-12-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake-compiler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.0'
|
41
|
+
description: Nokolexbor is a high-performance HTML5 parser, with support for both
|
42
|
+
CSS selectors and XPath. It's API is designed to be compatible with Nokogiri.
|
43
|
+
email: zyc9012@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/nokolexbor.rb
|
49
|
+
- lib/nokolexbor/3.1/nokolexbor.so
|
50
|
+
- lib/nokolexbor/attribute.rb
|
51
|
+
- lib/nokolexbor/document.rb
|
52
|
+
- lib/nokolexbor/node.rb
|
53
|
+
- lib/nokolexbor/node_set.rb
|
54
|
+
- lib/nokolexbor/version.rb
|
55
|
+
- lib/nokolexbor/xpath.rb
|
56
|
+
- lib/nokolexbor/xpath_context.rb
|
57
|
+
homepage: https://github.com/serpapi/nokolexbor
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.1'
|
70
|
+
- - "<"
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 3.2.dev
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubygems_version: 3.3.4
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: High-performance HTML5 parser, with support for both CSS selectors and XPath.
|
83
|
+
test_files: []
|