nokogiri 1.1.1 → 1.2.0

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.

Files changed (88) hide show
  1. data/History.ja.txt +34 -0
  2. data/History.txt +36 -0
  3. data/Manifest.txt +21 -0
  4. data/README.ja.txt +1 -1
  5. data/README.txt +1 -1
  6. data/Rakefile +27 -89
  7. data/ext/nokogiri/extconf.rb +48 -63
  8. data/ext/nokogiri/html_document.c +90 -29
  9. data/ext/nokogiri/html_sax_parser.c +23 -2
  10. data/ext/nokogiri/native.c +18 -8
  11. data/ext/nokogiri/native.h +22 -0
  12. data/ext/nokogiri/xml_attr.c +83 -0
  13. data/ext/nokogiri/xml_attr.h +9 -0
  14. data/ext/nokogiri/xml_cdata.c +1 -1
  15. data/ext/nokogiri/xml_document.c +84 -18
  16. data/ext/nokogiri/xml_document_fragment.c +38 -0
  17. data/ext/nokogiri/xml_document_fragment.h +10 -0
  18. data/ext/nokogiri/xml_dtd.c +2 -22
  19. data/ext/nokogiri/xml_entity_reference.c +41 -0
  20. data/ext/nokogiri/xml_entity_reference.h +9 -0
  21. data/ext/nokogiri/xml_io.c +10 -3
  22. data/ext/nokogiri/xml_io.h +1 -0
  23. data/ext/nokogiri/xml_node.c +116 -66
  24. data/ext/nokogiri/xml_node_set.c +5 -1
  25. data/ext/nokogiri/xml_processing_instruction.c +44 -0
  26. data/ext/nokogiri/xml_processing_instruction.h +9 -0
  27. data/ext/nokogiri/xml_reader.c +20 -4
  28. data/ext/nokogiri/xml_sax_parser.c +51 -15
  29. data/ext/nokogiri/xml_sax_push_parser.c +85 -0
  30. data/ext/nokogiri/xml_sax_push_parser.h +9 -0
  31. data/ext/nokogiri/xml_syntax_error.c +12 -8
  32. data/ext/nokogiri/xml_syntax_error.h +2 -1
  33. data/ext/nokogiri/xml_xpath_context.c +11 -2
  34. data/ext/nokogiri/xslt_stylesheet.c +1 -6
  35. data/lib/nokogiri.rb +10 -13
  36. data/lib/nokogiri/css.rb +1 -1
  37. data/lib/nokogiri/css/generated_parser.rb +287 -295
  38. data/lib/nokogiri/css/generated_tokenizer.rb +36 -51
  39. data/lib/nokogiri/css/node.rb +1 -3
  40. data/lib/nokogiri/css/parser.rb +21 -12
  41. data/lib/nokogiri/css/parser.y +55 -44
  42. data/lib/nokogiri/css/syntax_error.rb +2 -1
  43. data/lib/nokogiri/css/tokenizer.rex +23 -32
  44. data/lib/nokogiri/decorators/hpricot/node_set.rb +1 -1
  45. data/lib/nokogiri/html.rb +10 -4
  46. data/lib/nokogiri/html/document.rb +6 -2
  47. data/lib/nokogiri/syntax_error.rb +4 -0
  48. data/lib/nokogiri/version.rb +2 -1
  49. data/lib/nokogiri/xml.rb +3 -1
  50. data/lib/nokogiri/xml/attr.rb +3 -4
  51. data/lib/nokogiri/xml/cdata.rb +1 -1
  52. data/lib/nokogiri/xml/document.rb +4 -7
  53. data/lib/nokogiri/xml/document_fragment.rb +9 -0
  54. data/lib/nokogiri/xml/dtd.rb +3 -0
  55. data/lib/nokogiri/xml/node.rb +144 -40
  56. data/lib/nokogiri/xml/node/save_options.rb +32 -0
  57. data/lib/nokogiri/xml/node_set.rb +11 -20
  58. data/lib/nokogiri/xml/processing_instruction.rb +6 -0
  59. data/lib/nokogiri/xml/reader.rb +5 -0
  60. data/lib/nokogiri/xml/sax.rb +1 -0
  61. data/lib/nokogiri/xml/sax/push_parser.rb +47 -0
  62. data/lib/nokogiri/xml/syntax_error.rb +3 -1
  63. data/lib/nokogiri/xml/xpath/syntax_error.rb +1 -1
  64. data/tasks/test.rb +136 -0
  65. data/test/css/test_parser.rb +4 -0
  66. data/test/css/test_tokenizer.rb +30 -17
  67. data/test/css/test_xpath_visitor.rb +11 -0
  68. data/test/helper.rb +11 -0
  69. data/test/hpricot/test_builder.rb +2 -9
  70. data/test/hpricot/test_parser.rb +4 -4
  71. data/test/html/test_builder.rb +7 -7
  72. data/test/html/test_document.rb +90 -4
  73. data/test/html/test_node.rb +1 -0
  74. data/test/test_css_cache.rb +1 -3
  75. data/test/test_reader.rb +19 -1
  76. data/test/test_xslt_transforms.rb +1 -1
  77. data/test/xml/node/test_save_options.rb +20 -0
  78. data/test/xml/sax/test_parser.rb +17 -0
  79. data/test/xml/sax/test_push_parser.rb +67 -0
  80. data/test/xml/test_attr.rb +16 -0
  81. data/test/xml/test_cdata.rb +1 -1
  82. data/test/xml/test_document.rb +45 -0
  83. data/test/xml/test_document_fragment.rb +18 -0
  84. data/test/xml/test_dtd.rb +2 -4
  85. data/test/xml/test_entity_reference.rb +16 -0
  86. data/test/xml/test_node.rb +149 -80
  87. data/test/xml/test_processing_instruction.rb +24 -0
  88. metadata +28 -2
@@ -11,7 +11,7 @@ module Nokogiri
11
11
  name = @xml.xpath('//employee[2]/name').first
12
12
  assert cdata = name.children[1]
13
13
  assert cdata.cdata?
14
- assert_equal 'cdata-section', cdata.name
14
+ assert_equal '#cdata-section', cdata.name
15
15
  end
16
16
  end
17
17
  end
@@ -1,5 +1,7 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
2
2
 
3
+ require 'uri'
4
+
3
5
  module Nokogiri
4
6
  module XML
5
7
  class TestDocument < Nokogiri::TestCase
@@ -7,11 +9,54 @@ module Nokogiri
7
9
  @xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
8
10
  end
9
11
 
12
+ def test_xmlns_is_automatically_registered
13
+ doc = Nokogiri::XML(<<-eoxml)
14
+ <root xmlns="http://tenderlovemaking.com/">
15
+ <foo>
16
+ bar
17
+ </foo>
18
+ </root>
19
+ eoxml
20
+ assert_equal 1, doc.css('xmlns|foo').length
21
+ assert_equal 1, doc.css('foo').length
22
+ assert_equal 0, doc.css('|foo').length
23
+ end
24
+
25
+ # wtf... osx's libxml sucks.
26
+ unless Nokogiri::LIBXML_VERSION =~ /^2\.6\./
27
+ def test_encoding
28
+ xml = Nokogiri::XML(File.read(XML_FILE), XML_FILE, 'UTF-8')
29
+ assert_equal 'UTF-8', xml.encoding
30
+ end
31
+ end
32
+
33
+ def test_document_has_errors
34
+ doc = Nokogiri::XML(<<-eoxml)
35
+ <foo><bar></foo>
36
+ eoxml
37
+ assert doc.errors.length > 0
38
+ doc.errors.each do |error|
39
+ assert_match error.message, error.inspect
40
+ assert_match error.message, error.to_s
41
+ end
42
+ end
43
+
44
+ def test_strict_document_throws_syntax_error
45
+ assert_raises(Nokogiri::XML::SyntaxError) {
46
+ Nokogiri::XML('<foo><bar></foo>', nil, nil, 0)
47
+ }
48
+ end
49
+
10
50
  def test_XML_function
11
51
  xml = Nokogiri::XML(File.read(XML_FILE), XML_FILE)
12
52
  assert xml.xml?
13
53
  end
14
54
 
55
+ def test_url
56
+ assert @xml.url
57
+ assert_equal XML_FILE, URI.unescape(@xml.url).sub('file:///', '')
58
+ end
59
+
15
60
  def test_document_parent
16
61
  xml = Nokogiri::XML(File.read(XML_FILE), XML_FILE)
17
62
  assert_raises(NoMethodError) {
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
2
+
3
+ module Nokogiri
4
+ module XML
5
+ class TestDocumentFragment < Nokogiri::TestCase
6
+ def test_new
7
+ @xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
8
+ fragment = Nokogiri::XML::DocumentFragment.new(@xml)
9
+ end
10
+
11
+ def test_name
12
+ @xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
13
+ fragment = Nokogiri::XML::DocumentFragment.new(@xml)
14
+ assert_equal '#document-fragment', fragment.name
15
+ end
16
+ end
17
+ end
18
+ end
@@ -4,7 +4,7 @@ module Nokogiri
4
4
  module HTML
5
5
  class TestDTD < Nokogiri::TestCase
6
6
  def setup
7
- @xml = Nokogiri::XML.parse(File.read(XML_FILE))
7
+ @xml = Nokogiri::XML(File.open(XML_FILE))
8
8
  assert @dtd = @xml.internal_subset
9
9
  end
10
10
 
@@ -19,9 +19,7 @@ module Nokogiri
19
19
  end
20
20
 
21
21
  def test_attributes
22
- assert attributes = @dtd.attributes
23
- assert_equal %w[ width ], attributes.keys
24
- assert_equal 'width', attributes['width'].name
22
+ assert_nil @dtd.attributes
25
23
  end
26
24
 
27
25
  def test_elements
@@ -0,0 +1,16 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
2
+
3
+ module Nokogiri
4
+ module XML
5
+ class TestEntityReference < Nokogiri::TestCase
6
+ def setup
7
+ @xml = Nokogiri::XML(File.open(XML_FILE), XML_FILE)
8
+ end
9
+
10
+ def test_new
11
+ assert ref = EntityReference.new(@xml, 'ent4')
12
+ assert_instance_of EntityReference, ref
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,44 +1,121 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
2
2
 
3
+ require 'stringio'
4
+
3
5
  module Nokogiri
4
6
  module XML
5
7
  class TestNode < Nokogiri::TestCase
8
+ def setup
9
+ @xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
10
+ end
11
+
12
+ def test_add_namespace
13
+ node = @xml.at('address')
14
+ node.add_namespace('foo', 'http://tenderlovemaking.com')
15
+ assert_equal 'http://tenderlovemaking.com', node.namespaces['xmlns:foo']
16
+ end
17
+
18
+ def test_write_to
19
+ io = StringIO.new
20
+ @xml.write_to io
21
+ io.rewind
22
+ assert_equal @xml.to_xml, io.read
23
+ end
24
+
25
+ def test_write_to_with_block
26
+ called = false
27
+ io = StringIO.new
28
+ conf = nil
29
+ @xml.write_to io do |config|
30
+ called = true
31
+ conf = config
32
+ config.format.as_html.no_empty_tags
33
+ end
34
+ io.rewind
35
+ assert called
36
+ assert_equal @xml.serialize(nil, conf.options), io.read
37
+ end
38
+
39
+ %w{ xml html xhtml }.each do |type|
40
+ define_method(:"test_write_#{type}_to") do
41
+ io = StringIO.new
42
+ assert @xml.send(:"write_#{type}_to", io)
43
+ io.rewind
44
+ assert_match @xml.send(:"to_#{type}"), io.read
45
+ end
46
+ end
47
+
48
+ def test_serialize_with_block
49
+ called = false
50
+ conf = nil
51
+ string = @xml.serialize do |config|
52
+ called = true
53
+ conf = config
54
+ config.format.as_html.no_empty_tags
55
+ end
56
+ assert called
57
+ assert_equal @xml.serialize(nil, conf.options), string
58
+ end
59
+
60
+ def test_values
61
+ assert_equal %w{ Yes Yes }, @xml.xpath('//address')[1].values
62
+ end
63
+
64
+ def test_keys
65
+ assert_equal %w{ domestic street }, @xml.xpath('//address')[1].keys
66
+ end
67
+
68
+ def test_each
69
+ attributes = []
70
+ @xml.xpath('//address')[1].each do |key, value|
71
+ attributes << [key, value]
72
+ end
73
+ assert_equal [['domestic', 'Yes'], ['street', 'Yes']], attributes
74
+ end
75
+
76
+ def test_new
77
+ assert node = Nokogiri::XML::Node.new('input', @xml)
78
+ assert_equal 1, node.node_type
79
+ end
80
+
81
+ def test_to_str
82
+ name = @xml.xpath('//name').first
83
+ assert_match(/Margaret/, '' + name)
84
+ assert_equal('Margaret Martin', '' + name.children.first)
85
+ end
86
+
6
87
  def test_ancestors
7
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
8
- address = xml.xpath('//address').first
88
+ address = @xml.xpath('//address').first
9
89
  assert_equal 3, address.ancestors.length
10
90
  assert_equal ['employee', 'staff', nil],
11
91
  address.ancestors.map { |x| x.name }
12
92
  end
13
93
 
14
94
  def test_read_only?
15
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
16
- assert entity_decl = xml.internal_subset.children.find { |x|
95
+ assert entity_decl = @xml.internal_subset.children.find { |x|
17
96
  x.type == Node::ENTITY_DECL
18
97
  }
19
98
  assert entity_decl.read_only?
20
99
  end
21
100
 
22
101
  def test_remove_attribute
23
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
24
- address = xml.xpath('/staff/employee/address').first
102
+ address = @xml.xpath('/staff/employee/address').first
25
103
  assert_equal 'Yes', address['domestic']
26
104
  address.remove_attribute 'domestic'
27
105
  assert_nil address['domestic']
28
106
  end
29
107
 
30
108
  def test_delete
31
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
32
- address = xml.xpath('/staff/employee/address').first
109
+ address = @xml.xpath('/staff/employee/address').first
33
110
  assert_equal 'Yes', address['domestic']
34
111
  address.delete 'domestic'
35
112
  assert_nil address['domestic']
36
113
  end
37
114
 
38
115
  def test_angry_add_child
39
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
40
- child = xml.css('employee').first
116
+ child = @xml.css('employee').first
41
117
 
118
+ assert previous_last_child = child.children.last
42
119
  assert new_child = child.children.first
43
120
 
44
121
  last = child.children.last
@@ -166,91 +243,48 @@ module Nokogiri
166
243
  assert_no_match(/Hello world/, xml.to_s)
167
244
  end
168
245
 
169
- def test_dup_shallow
170
- html = Nokogiri::HTML.parse(File.read(HTML_FILE), HTML_FILE)
171
- found = html.search('//div/a').first
172
- dup = found.dup(0)
173
- assert dup
174
- assert_equal '', dup.content
175
- end
176
-
177
- def test_dup
178
- html = Nokogiri::HTML.parse(File.read(HTML_FILE), HTML_FILE)
179
- found = html.search('//div/a').first
180
- dup = found.dup
181
- assert dup
182
- assert_equal found.content, dup.content
183
- end
184
-
185
- def test_search_can_handle_xpath_and_css
186
- html = Nokogiri::HTML.parse(File.read(HTML_FILE), HTML_FILE)
187
- found = html.search('//div/a', 'div > p')
188
- length = html.xpath('//div/a').length +
189
- html.css('div > p').length
190
- assert_equal length, found.length
191
- end
192
-
193
- def test_find_by_xpath
194
- html = Nokogiri::HTML.parse(File.read(HTML_FILE), HTML_FILE)
195
- found = html.xpath('//div/a')
196
- assert_equal 3, found.length
197
- end
198
-
199
- def test_find_by_css
200
- html = Nokogiri::HTML.parse(File.read(HTML_FILE), HTML_FILE)
201
- found = html.css('div > a')
202
- assert_equal 3, found.length
203
- end
204
-
205
246
  def test_next_sibling
206
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
207
- assert node = xml.root
247
+ assert node = @xml.root
208
248
  assert sibling = node.child.next_sibling
209
249
  assert_equal('employee', sibling.name)
210
250
  end
211
251
 
212
252
  def test_previous_sibling
213
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
214
- assert node = xml.root
253
+ assert node = @xml.root
215
254
  assert sibling = node.child.next_sibling
216
255
  assert_equal('employee', sibling.name)
217
256
  assert_equal(sibling.previous_sibling, node.child)
218
257
  end
219
258
 
220
259
  def test_name=
221
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
222
- assert node = xml.root
260
+ assert node = @xml.root
223
261
  node.name = 'awesome'
224
262
  assert_equal('awesome', node.name)
225
263
  end
226
264
 
227
265
  def test_child
228
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
229
- assert node = xml.root
266
+ assert node = @xml.root
230
267
  assert child = node.child
231
268
  assert_equal('text', child.name)
232
269
  end
233
270
 
234
271
  def test_key?
235
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
236
- assert node = xml.search('//address').first
272
+ assert node = @xml.search('//address').first
237
273
  assert(!node.key?('asdfasdf'))
238
274
  end
239
275
 
240
276
  def test_set_property
241
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
242
- assert node = xml.search('//address').first
277
+ assert node = @xml.search('//address').first
243
278
  node['foo'] = 'bar'
244
279
  assert_equal('bar', node['foo'])
245
280
  end
246
281
 
247
282
  def test_attributes
248
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
249
- assert node = xml.search('//address').first
283
+ assert node = @xml.search('//address').first
250
284
  assert_nil(node['asdfasdfasdf'])
251
285
  assert_equal('Yes', node['domestic'])
252
286
 
253
- assert node = xml.search('//address')[2]
287
+ assert node = @xml.search('//address')[2]
254
288
  attr = node.attributes
255
289
  assert_equal 2, attr.size
256
290
  assert_equal 'Yes', attr['domestic'].value
@@ -259,31 +293,27 @@ module Nokogiri
259
293
  end
260
294
 
261
295
  def test_path
262
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
263
- assert set = xml.search('//employee')
296
+ assert set = @xml.search('//employee')
264
297
  assert node = set.first
265
298
  assert_equal('/staff/employee[1]', node.path)
266
299
  end
267
300
 
268
301
  def test_search_by_symbol
269
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
270
- assert set = xml.search(:employee)
302
+ assert set = @xml.search(:employee)
271
303
  assert 5, set.length
272
304
 
273
- assert node = xml.at(:employee)
305
+ assert node = @xml.at(:employee)
274
306
  assert node.text =~ /EMP0001/
275
307
  end
276
308
 
277
309
  def test_new_node
278
- xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
279
- node = Nokogiri::XML::Node.new('form', xml)
310
+ node = Nokogiri::XML::Node.new('form', @xml)
280
311
  assert_equal('form', node.name)
281
312
  assert(node.document)
282
313
  end
283
314
 
284
315
  def test_content
285
- xml = Nokogiri::XML.parse(File.read(XML_FILE))
286
- node = Nokogiri::XML::Node.new('form', xml)
316
+ node = Nokogiri::XML::Node.new('form', @xml)
287
317
  assert_equal('', node.content)
288
318
 
289
319
  node.content = 'hello world!'
@@ -298,33 +328,42 @@ module Nokogiri
298
328
  end
299
329
 
300
330
  def test_replace
301
- xml = Nokogiri::XML.parse(File.read(XML_FILE))
302
- set = xml.search('//employee')
331
+ set = @xml.search('//employee')
303
332
  assert 5, set.length
304
- assert 0, xml.search('//form').length
333
+ assert 0, @xml.search('//form').length
305
334
 
306
335
  first = set[0]
307
336
  second = set[1]
308
337
 
309
- node = Nokogiri::XML::Node.new('form', xml)
338
+ node = Nokogiri::XML::Node.new('form', @xml)
310
339
  first.replace(node)
311
340
 
312
- assert set = xml.search('//employee')
341
+ assert set = @xml.search('//employee')
313
342
  assert_equal 4, set.length
314
- assert 1, xml.search('//form').length
343
+ assert 1, @xml.search('//form').length
315
344
 
316
345
  assert_equal set[0].to_xml, second.to_xml
317
- assert_equal set[0].to_xml(5), second.to_xml(5)
318
- assert_not_equal set[0].to_xml, set[0].to_xml(5)
319
346
  end
320
347
 
321
348
  def test_illegal_replace_of_node_with_doc
322
- xml = Nokogiri::XML.parse(File.read(XML_FILE))
323
349
  new_node = Nokogiri::XML.parse('<foo>bar</foo>')
324
- old_node = xml.at('//employee')
350
+ old_node = @xml.at('//employee')
325
351
  assert_raises(ArgumentError){ old_node.replace new_node }
326
352
  end
327
353
 
354
+ def test_node_equality
355
+ doc1 = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
356
+ doc2 = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
357
+
358
+ address1_1 = doc1.xpath('//address').first
359
+ address1_2 = doc1.xpath('//address').first
360
+
361
+ address2 = doc2.xpath('//address').first
362
+
363
+ assert_not_equal address1_1, address2 # two references to very, very similar nodes
364
+ assert_equal address1_1, address1_2 # two references to the exact same node
365
+ end
366
+
328
367
  def test_namespace_as_hash
329
368
  xml = Nokogiri::XML.parse(<<-eoxml)
330
369
  <root>
@@ -341,6 +380,22 @@ module Nokogiri
341
380
  assert_equal 1, tires.length
342
381
  end
343
382
 
383
+ def test_namespace_search_with_css
384
+ xml = Nokogiri::XML.parse(<<-eoxml)
385
+ <root>
386
+ <car xmlns:part="http://general-motors.com/">
387
+ <part:tire>Michelin Model XGV</part:tire>
388
+ </car>
389
+ <bicycle xmlns:part="http://schwinn.com/">
390
+ <part:tire>I'm a bicycle tire!</part:tire>
391
+ </bicycle>
392
+ </root>
393
+ eoxml
394
+
395
+ tires = xml.css('bike|tire', 'bike' => 'http://schwinn.com/')
396
+ assert_equal 1, tires.length
397
+ end
398
+
344
399
  def test_namespaces
345
400
  xml = Nokogiri::XML.parse(<<-EOF)
346
401
  <x xmlns:a='http://foo.com/' xmlns:b='http://bar.com/'>
@@ -389,6 +444,20 @@ EOF
389
444
  assert_equal nil, set[3].namespace
390
445
  end
391
446
 
447
+ def test_line
448
+ xml = Nokogiri::XML(<<-eoxml)
449
+ <root>
450
+ <a>
451
+ Hello world
452
+ </a>
453
+ </root>
454
+ eoxml
455
+
456
+ set = xml.search("//a")
457
+ node = set.first
458
+ assert_equal 2, node.line
459
+ end
460
+
392
461
  end
393
462
  end
394
463
  end