axlsx 1.0.4 → 1.0.5
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.
- data/README.md +95 -72
- data/examples/example.rb +83 -0
- data/examples/example.rb~ +79 -0
- data/examples/multi_chart.xlsx +0 -0
- data/lib/axlsx/drawing/bar_3D_chart.rb +2 -2
- data/lib/axlsx/drawing/bar_series.rb +1 -0
- data/lib/axlsx/drawing/chart.rb +11 -0
- data/lib/axlsx/drawing/drawing.rb +5 -1
- data/lib/axlsx/drawing/line_3D_chart.rb +93 -0
- data/lib/axlsx/drawing/line_3D_chart.rb~ +138 -0
- data/lib/axlsx/drawing/line_series.rb +76 -0
- data/lib/axlsx/drawing/line_series.rb~ +91 -0
- data/lib/axlsx/drawing/pie_3D_chart.rb +1 -1
- data/lib/axlsx/drawing/ser_axis.rb +42 -0
- data/lib/axlsx/drawing/ser_axis.rb~ +48 -0
- data/lib/axlsx/drawing/series.rb +9 -7
- data/lib/axlsx/drawing/series_title.rb +22 -0
- data/lib/axlsx/drawing/series_title.rb~ +18 -0
- data/lib/axlsx/drawing/title.rb +1 -2
- data/lib/axlsx/util/constants.rb +1 -1
- data/test/drawing/tc_bar_series.rb +1 -1
- data/test/drawing/tc_chart.rb +7 -5
- data/test/drawing/tc_line_3d_chart.rb +48 -0
- data/test/drawing/tc_line_3d_chart.rb~ +48 -0
- data/test/drawing/tc_line_series.tc +34 -0
- data/test/drawing/tc_line_series.tc~ +34 -0
- data/test/drawing/tc_pie_series.rb +1 -1
- data/test/drawing/tc_ser_axis.rb +23 -0
- data/test/drawing/tc_ser_axis.rb~ +20 -0
- data/test/drawing/tc_series.rb +1 -1
- data/test/drawing/tc_series_title.rb +34 -0
- data/test/drawing/tc_series_title.rb~ +34 -0
- data/test/tc_package.rb +15 -13
- metadata +24 -5
@@ -0,0 +1,48 @@
|
|
1
|
+
module Axlsx
|
2
|
+
#A CatAxis object defines a chart category axis
|
3
|
+
class SerAxis < Axis
|
4
|
+
|
5
|
+
# @return [Boolean]
|
6
|
+
attr_accessor :tickLblSkip
|
7
|
+
|
8
|
+
# @return [Boolean]
|
9
|
+
attr_accessor :tickMarkSkip
|
10
|
+
|
11
|
+
# Creates a new SerAxis object
|
12
|
+
# @param [Integer] axId the id of this axis
|
13
|
+
# @param [Integer] crossAx the id of the perpendicular axis
|
14
|
+
# @option options [Symbol] axPos
|
15
|
+
# @option options [Symbol] tickLblPos
|
16
|
+
# @option options [Symbol] crosses
|
17
|
+
# @option options [Boolean] auto
|
18
|
+
# @option options [Symbol] lblAlgn
|
19
|
+
# @option options [Integer] lblOffset
|
20
|
+
def initialize(axId, crossAx, options={})
|
21
|
+
super(axId, crossAx, options)
|
22
|
+
self.auto = true
|
23
|
+
self.lblAlgn = :ctr
|
24
|
+
self.lblOffset = "100%"
|
25
|
+
options.each do |o|
|
26
|
+
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def auto=(v) Axlsx::validate_boolean(v); @auto = v; end
|
31
|
+
def lblAlgn=(v) RestrictionValidator.validate "#{self.class}.lblAlgn", [:ctr, :l, :r], v; @lblAlgn = v; end
|
32
|
+
def lblOffset=(v) RegexValidator.validate "#{self.class}.lblOffset", LBL_OFFSET_REGEX, v; @lblOffset = v; end
|
33
|
+
|
34
|
+
# Serializes the category axis
|
35
|
+
# @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
|
36
|
+
# @return [String]
|
37
|
+
def to_xml(xml)
|
38
|
+
xml.send('c:catAx') {
|
39
|
+
super(xml)
|
40
|
+
xml.send('c:auto', :val=>@auto)
|
41
|
+
xml.send('c:lblAlgn', :val=>@lblAlgn)
|
42
|
+
xml.send('c:lblOffset', :val=>@lblOffset)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
end
|
data/lib/axlsx/drawing/series.rb
CHANGED
@@ -18,7 +18,7 @@ module Axlsx
|
|
18
18
|
attr_accessor :order
|
19
19
|
|
20
20
|
# The title of the series
|
21
|
-
# @return [
|
21
|
+
# @return [SeriesTitle]
|
22
22
|
attr_accessor :title
|
23
23
|
|
24
24
|
# Creates a new series
|
@@ -44,8 +44,12 @@ module Axlsx
|
|
44
44
|
@order || index
|
45
45
|
end
|
46
46
|
|
47
|
-
def title=(v)
|
48
|
-
|
47
|
+
def title=(v)
|
48
|
+
v = SeriesTitle.new(v) if v.is_a?(String) || v.is_a?(Cell)
|
49
|
+
DataTypeValidator.validate "#{self.class}.title", SeriesTitle, v
|
50
|
+
@title = v
|
51
|
+
end
|
52
|
+
|
49
53
|
private
|
50
54
|
|
51
55
|
# assigns the chart for this series
|
@@ -57,10 +61,8 @@ module Axlsx
|
|
57
61
|
def to_xml(xml)
|
58
62
|
xml.send('c:ser') {
|
59
63
|
xml.send('c:idx', :val=>index)
|
60
|
-
xml.send('c:order', :val=>order || index)
|
61
|
-
|
62
|
-
xml.send('c:v', self.title)
|
63
|
-
}
|
64
|
+
xml.send('c:order', :val=>order || index)
|
65
|
+
title.to_xml(xml) unless title.nil?
|
64
66
|
yield xml if block_given?
|
65
67
|
}
|
66
68
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Axlsx
|
2
|
+
# A series title is a Title with a slightly different serialization
|
3
|
+
class SeriesTitle < Title
|
4
|
+
|
5
|
+
# Serializes the series title
|
6
|
+
# @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
|
7
|
+
# @return [String]
|
8
|
+
def to_xml(xml)
|
9
|
+
xml.send('c:tx') {
|
10
|
+
xml.send('c:strRef') {
|
11
|
+
xml.send('c:f', range)
|
12
|
+
xml.send('c:strCache') {
|
13
|
+
xml.send('c:ptCount', :val=>1)
|
14
|
+
xml.send('c:pt', :idx=>0) {
|
15
|
+
xml.send('c:v', @text)
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Axlsx
|
2
|
+
class SeriesTitle < Title
|
3
|
+
|
4
|
+
def to_xml(xml)
|
5
|
+
xml.send('c:tx') {
|
6
|
+
xml.send('c:strRef') {
|
7
|
+
xml.send('c:f', range)
|
8
|
+
xml.send('c:strCache') {
|
9
|
+
xml.send('c:ptCount', :val=>1)
|
10
|
+
xml.send('c:pt', :idx=>0) {
|
11
|
+
xml.send('c:v', @text)
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/axlsx/drawing/title.rb
CHANGED
@@ -32,7 +32,6 @@ module Axlsx
|
|
32
32
|
end
|
33
33
|
|
34
34
|
# Not implemented at this time.
|
35
|
-
#def tx=(v) DataTypeValidator.validate 'Title.tx', Tx, v; @tx=v; end
|
36
35
|
#def layout=(v) DataTypeValidator.validate 'Title.layout', Layout, v; @layout = v; end
|
37
36
|
#def overlay=(v) Axlsx::validate_boolean v; @overlay=v; end
|
38
37
|
#def spPr=(v) DataTypeValidator.validate 'Title.spPr', SpPr, v; @spPr = v; end
|
@@ -55,7 +54,7 @@ module Axlsx
|
|
55
54
|
}
|
56
55
|
}
|
57
56
|
end
|
58
|
-
|
57
|
+
|
59
58
|
private
|
60
59
|
|
61
60
|
# returns the excel style abslute reference for the title when title is a Cell object
|
data/lib/axlsx/util/constants.rb
CHANGED
@@ -11,7 +11,7 @@ class TestBarSeries < Test::Unit::TestCase
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_initialize
|
14
|
-
assert_equal(@series.title, "bob", "series title has been applied")
|
14
|
+
assert_equal(@series.title.text, "bob", "series title has been applied")
|
15
15
|
assert_equal(@series.data, [0,1,2], "data option applied")
|
16
16
|
assert_equal(@series.labels, ["zero", "one","two"], "labels option applied")
|
17
17
|
assert_equal(@series.shape, :box, "series shape has been applied")
|
data/test/drawing/tc_chart.rb
CHANGED
@@ -28,19 +28,21 @@ class TestChart < Test::Unit::TestCase
|
|
28
28
|
assert_equal(@chart.title.cell, @row.cells.first)
|
29
29
|
end
|
30
30
|
|
31
|
+
def test_style
|
32
|
+
assert_raise(ArgumentError) { @chart.style = 49 }
|
33
|
+
assert_nothing_raised { @chart.style = 2 }
|
34
|
+
assert_equal(@chart.style, 2)
|
35
|
+
end
|
36
|
+
|
31
37
|
|
32
38
|
def test_add_series
|
33
39
|
s = @chart.add_series :data=>[0,1,2,3], :labels => ["one", 1, "anything"], :title=>"bob"
|
34
40
|
assert_equal(@chart.series.last, s, "series has been added to chart series collection")
|
35
|
-
assert_equal(s.title, "bob", "series title has been applied")
|
41
|
+
assert_equal(s.title.text, "bob", "series title has been applied")
|
36
42
|
assert_equal(s.data, [0,1,2,3], "data option applied")
|
37
43
|
assert_equal(s.labels, ["one",1,"anything"], "labels option applied")
|
38
44
|
end
|
39
45
|
|
40
|
-
def test_create_range
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
46
|
def test_pn
|
45
47
|
assert_equal(@chart.pn, "charts/chart1.xml")
|
46
48
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'axlsx.rb'
|
3
|
+
|
4
|
+
class TestLine3DChart < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@p = Axlsx::Package.new
|
8
|
+
ws = @p.workbook.add_worksheet
|
9
|
+
@row = ws.add_row ["one", 1, Time.now]
|
10
|
+
@chart = ws.add_chart Axlsx::Line3DChart, :title => "fishery"
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_initialization
|
17
|
+
assert_equal(@chart.grouping, :standard, "grouping defualt incorrect")
|
18
|
+
assert_equal(@chart.series_type, Axlsx::LineSeries, "series type incorrect")
|
19
|
+
assert(@chart.catAxis.is_a?(Axlsx::CatAxis), "category axis not created")
|
20
|
+
assert(@chart.valAxis.is_a?(Axlsx::ValAxis), "value access not created")
|
21
|
+
assert(@chart.serAxis.is_a?(Axlsx::SerAxis), "value access not created")
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_grouping
|
25
|
+
assert_raise(ArgumentError, "require valid grouping") { @chart.grouping = :inverted }
|
26
|
+
assert_nothing_raised("allow valid grouping") { @chart.grouping = :stacked }
|
27
|
+
assert(@chart.grouping == :stacked)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_gapDepth
|
31
|
+
assert_raise(ArgumentError, "require valid gapDepth") { @chart.gapDepth = 200 }
|
32
|
+
assert_nothing_raised("allow valid gapDepth") { @chart.gapDepth = "200%" }
|
33
|
+
assert(@chart.gapDepth == "200%")
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def test_to_xml
|
38
|
+
schema = Nokogiri::XML::Schema(File.open(Axlsx::DRAWING_XSD))
|
39
|
+
doc = Nokogiri::XML(@chart.to_xml)
|
40
|
+
errors = []
|
41
|
+
schema.validate(doc).each do |error|
|
42
|
+
errors.push error
|
43
|
+
puts error.message
|
44
|
+
end
|
45
|
+
assert(errors.empty?, "error free validation")
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'axlsx.rb'
|
3
|
+
|
4
|
+
class TestBar3DChart < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@p = Axlsx::Package.new
|
8
|
+
ws = @p.workbook.add_worksheet
|
9
|
+
@row = ws.add_row ["one", 1, Time.now]
|
10
|
+
@chart = ws.add_chart Axlsx::Bar3DChart, :title => "fishery"
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_initialization
|
17
|
+
assert_equal(@chart.grouping, :clustered, "grouping defualt incorrect")
|
18
|
+
assert_equal(@chart.series_type, Axlsx::LineSeries, "series type incorrect")
|
19
|
+
assert(@chart.catAxis.is_a?(Axlsx::CatAxis), "category axis not created")
|
20
|
+
assert(@chart.valAxis.is_a?(Axlsx::ValAxis), "value access not created")
|
21
|
+
assert(@chart.serAxis.is_a?(Axlsx::ValAxis), "value access not created")
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_grouping
|
25
|
+
assert_raise(ArgumentError, "require valid grouping") { @chart.grouping = :inverted }
|
26
|
+
assert_nothing_raised("allow valid grouping") { @chart.grouping = :standard }
|
27
|
+
assert(@chart.grouping == :standard)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_gapDepth
|
31
|
+
assert_raise(ArgumentError, "require valid gapDepth") { @chart.gapDepth = 200 }
|
32
|
+
assert_nothing_raised("allow valid gapDepth") { @chart.gapDepth = "200%" }
|
33
|
+
assert(@chart.gapDepth == "200%")
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def test_to_xml
|
38
|
+
schema = Nokogiri::XML::Schema(File.open(Axlsx::DRAWING_XSD))
|
39
|
+
doc = Nokogiri::XML(@chart.to_xml)
|
40
|
+
errors = []
|
41
|
+
schema.validate(doc).each do |error|
|
42
|
+
errors.push error
|
43
|
+
puts error.message
|
44
|
+
end
|
45
|
+
assert(errors.empty?, "error free validation")
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'axlsx.rb'
|
3
|
+
|
4
|
+
class TestLineSeries < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
p = Axlsx::Package.new
|
8
|
+
@ws = p.workbook.add_worksheet :name=>"hmmm"
|
9
|
+
chart = @ws.drawing.add_chart Axlsx::Line3DChart, :title => "fishery"
|
10
|
+
@series = chart.add_series :data=>[0,1,2], :labels=>["zero", "one", "two"], :title=>"bob"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_initialize
|
14
|
+
assert_equal(@series.title.text, "bob", "series title has been applied")
|
15
|
+
assert_equal(@series.data, [0,1,2], "data option applied")
|
16
|
+
assert_equal(@series.labels, ["zero", "one","two"], "labels option applied")
|
17
|
+
assert_equal(@series.shape, :box, "series shape has been applied")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_data
|
21
|
+
assert_equal(@series.data, [0,1,2])
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_labels
|
25
|
+
assert_equal(@series.labels, ["zero", "one", "two"])
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_shape
|
29
|
+
assert_raise(ArgumentError, "require valid shape") { @series.shape = :teardropt }
|
30
|
+
assert_nothing_raised("allow valid shape") { @series.shape = :cone }
|
31
|
+
assert(@series.shape == :cone)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'axlsx.rb'
|
3
|
+
|
4
|
+
class TestLineSeries < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
p = Axlsx::Package.new
|
8
|
+
@ws = p.workbook.add_worksheet :name=>"hmmm"
|
9
|
+
chart = @ws.drawing.add_chart Axlsx::Bar3DChart, :title => "fishery"
|
10
|
+
@series = chart.add_series :data=>[0,1,2], :labels=>["zero", "one", "two"], :title=>"bob"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_initialize
|
14
|
+
assert_equal(@series.title, "bob", "series title has been applied")
|
15
|
+
assert_equal(@series.data, [0,1,2], "data option applied")
|
16
|
+
assert_equal(@series.labels, ["zero", "one","two"], "labels option applied")
|
17
|
+
assert_equal(@series.shape, :box, "series shape has been applied")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_data
|
21
|
+
assert_equal(@series.data, [0,1,2])
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_labels
|
25
|
+
assert_equal(@series.labels, ["zero", "one", "two"])
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_shape
|
29
|
+
assert_raise(ArgumentError, "require valid shape") { @series.shape = :teardropt }
|
30
|
+
assert_nothing_raised("allow valid shape") { @series.shape = :cone }
|
31
|
+
assert(@series.shape == :cone)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -11,7 +11,7 @@ class TestPieSeries < Test::Unit::TestCase
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_initialize
|
14
|
-
assert_equal(@series.title, "bob", "series title has been applied")
|
14
|
+
assert_equal(@series.title.text, "bob", "series title has been applied")
|
15
15
|
assert_equal(@series.data, [0,1,2], "data option applied")
|
16
16
|
assert_equal(@series.labels, ["zero", "one","two"], "labels option applied")
|
17
17
|
assert_equal(@series.explosion, nil, "series shape has been applied")
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'axlsx.rb'
|
3
|
+
|
4
|
+
class TestSerAxis < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@axis = Axlsx::SerAxis.new 12345, 54321
|
7
|
+
end
|
8
|
+
def teardown
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_tickLblSkip
|
12
|
+
assert_raise(ArgumentError, "requires valid tickLblSkip") { @axis.tickLblSkip = :my_eyes }
|
13
|
+
assert_nothing_raised("accepts valid tickLblSkip") { @axis.tickLblSkip = false }
|
14
|
+
assert_equal(@axis.tickLblSkip, false)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_tickMarkSkip
|
18
|
+
assert_raise(ArgumentError, "requires valid tickMarkSkip") { @axis.tickMarkSkip = :my_eyes }
|
19
|
+
assert_nothing_raised("accepts valid tickMarkSkip") { @axis.tickMarkSkip = false }
|
20
|
+
assert_equal(@axis.tickMarkSkip, false)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'axlsx.rb'
|
3
|
+
|
4
|
+
class TestSerAxis < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@axis = Axlsx::ValAxis.new 12345, 54321
|
7
|
+
end
|
8
|
+
def teardown
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_initialization
|
12
|
+
assert_equal(@axis.crossBetween, :between, "axis crossBetween default incorrect")
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_crossBetween
|
16
|
+
assert_raise(ArgumentError, "requires valid crossBetween") { @axis.crossBetween = :my_eyes }
|
17
|
+
assert_nothing_raised("accepts valid crossBetween") { @axis.crossBetween = :midCat }
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/test/drawing/tc_series.rb
CHANGED
@@ -11,7 +11,7 @@ class TestSeries < Test::Unit::TestCase
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_initialize
|
14
|
-
assert_equal(@series.title, "bob", "series title has been applied")
|
14
|
+
assert_equal(@series.title.text, "bob", "series title has been applied")
|
15
15
|
assert_equal(@series.order, @series.index, "order is index by default")
|
16
16
|
assert_equal(@series.index, @series.chart.series.index(@series), "index is applied")
|
17
17
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'axlsx.rb'
|
3
|
+
|
4
|
+
class TestSeriesTitle < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@p = Axlsx::Package.new
|
7
|
+
ws = @p.workbook.add_worksheet
|
8
|
+
@row = ws.add_row ["one", 1, Time.now]
|
9
|
+
@title = Axlsx::SeriesTitle.new
|
10
|
+
@chart = ws.add_chart Axlsx::Bar3DChart
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_initialization
|
17
|
+
assert(@title.text == "")
|
18
|
+
assert(@title.cell == nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_text
|
22
|
+
assert_raise(ArgumentError, "text must be a string") { @title.text = 123 }
|
23
|
+
@title.cell = @row.cells.first
|
24
|
+
@title.text = "bob"
|
25
|
+
assert(@title.cell == nil, "setting title with text clears the cell")
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_cell
|
29
|
+
assert_raise(ArgumentError, "cell must be a Cell") { @title.cell = "123" }
|
30
|
+
@title.cell = @row.cells.first
|
31
|
+
assert(@title.text == "one")
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|