gruff 0.3.4 → 0.3.6
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/Manifest.txt +2 -0
- data/Rakefile +1 -0
- data/lib/gruff.rb +2 -1
- data/lib/gruff/bar.rb +7 -3
- data/lib/gruff/base.rb +102 -91
- data/lib/gruff/dot.rb +113 -0
- data/lib/gruff/line.rb +41 -11
- data/lib/gruff/mini/bar.rb +6 -1
- data/lib/gruff/mini/legend.rb +7 -2
- data/lib/gruff/net.rb +10 -12
- data/lib/gruff/side_bar.rb +4 -3
- data/lib/gruff/side_stacked_bar.rb +5 -4
- data/lib/gruff/stacked_bar.rb +5 -4
- data/test/test_bar.rb +23 -0
- data/test/test_dot.rb +273 -0
- data/test/test_line.rb +47 -4
- data/test/test_mini_bar.rb +1 -0
- metadata +7 -4
data/History.txt
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
== 0.3.6
|
2
|
+
|
3
|
+
* Fixed manifest to list dot graph [theirishpenguin]
|
4
|
+
* Fixed color cycling error [Gunnar Wolf]
|
5
|
+
* Handle case where a line graph data set only has one value [Ron Colwill]
|
6
|
+
|
7
|
+
== 0.3.5
|
8
|
+
|
9
|
+
* Added dot graph from Erik Andrejko
|
10
|
+
|
1
11
|
== 0.3.4
|
2
12
|
|
3
13
|
* Reverted DEBUG=true. Will add a check in the release process so this doesn't happen again.
|
data/Manifest.txt
CHANGED
@@ -39,6 +39,7 @@ lib/gruff/bar_conversion.rb
|
|
39
39
|
lib/gruff/base.rb
|
40
40
|
lib/gruff/bullet.rb
|
41
41
|
lib/gruff/deprecated.rb
|
42
|
+
lib/gruff/dot.rb
|
42
43
|
lib/gruff/line.rb
|
43
44
|
lib/gruff/mini/bar.rb
|
44
45
|
lib/gruff/mini/legend.rb
|
@@ -63,6 +64,7 @@ test/test_area.rb
|
|
63
64
|
test/test_bar.rb
|
64
65
|
test/test_base.rb
|
65
66
|
test/test_bullet.rb
|
67
|
+
test/test_dot.rb
|
66
68
|
test/test_legend.rb
|
67
69
|
test/test_line.rb
|
68
70
|
test/test_mini_bar.rb
|
data/Rakefile
CHANGED
@@ -21,6 +21,7 @@ task :verify => :package do
|
|
21
21
|
if system %(ruby -e "require 'pkg/gruff-#{Gruff::VERSION}/lib/gruff'")
|
22
22
|
puts "\nThe library files are present"
|
23
23
|
end
|
24
|
+
raise "\n*** Gruff::Base::DEBUG must be set to false for releases ***\n\n" if Gruff::Base::DEBUG
|
24
25
|
end
|
25
26
|
|
26
27
|
task :release => :verify
|
data/lib/gruff.rb
CHANGED
data/lib/gruff/bar.rb
CHANGED
@@ -3,6 +3,9 @@ require File.dirname(__FILE__) + '/bar_conversion'
|
|
3
3
|
|
4
4
|
class Gruff::Bar < Gruff::Base
|
5
5
|
|
6
|
+
# Spacing factor applied between bars
|
7
|
+
attr_accessor :bar_spacing
|
8
|
+
|
6
9
|
def draw
|
7
10
|
# Labels will be centered over the left of the bar if
|
8
11
|
# there are more labels than columns. This is basically the same
|
@@ -21,8 +24,9 @@ protected
|
|
21
24
|
# Setup spacing.
|
22
25
|
#
|
23
26
|
# Columns sit side-by-side.
|
24
|
-
|
27
|
+
@bar_spacing ||= 0.9 # space between the bars
|
25
28
|
@bar_width = @graph_width / (@column_count * @data.length).to_f
|
29
|
+
padding = (@bar_width * (1 - @bar_spacing)) / 2
|
26
30
|
|
27
31
|
@d = @d.stroke_opacity 0.0
|
28
32
|
|
@@ -54,8 +58,8 @@ protected
|
|
54
58
|
data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
|
55
59
|
# Use incremented x and scaled y
|
56
60
|
# x
|
57
|
-
left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index)))
|
58
|
-
right_x = left_x + @bar_width *
|
61
|
+
left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index))) + padding
|
62
|
+
right_x = left_x + @bar_width * @bar_spacing
|
59
63
|
# y
|
60
64
|
conv = []
|
61
65
|
conversion.getLeftYRightYscaled( data_point, conv )
|
data/lib/gruff/base.rb
CHANGED
@@ -20,7 +20,7 @@ require File.dirname(__FILE__) + '/deprecated'
|
|
20
20
|
module Gruff
|
21
21
|
|
22
22
|
# This is the version of Gruff you are using.
|
23
|
-
VERSION = '0.3.
|
23
|
+
VERSION = '0.3.6'
|
24
24
|
|
25
25
|
class Base
|
26
26
|
|
@@ -36,9 +36,13 @@ module Gruff
|
|
36
36
|
DATA_COLOR_INDEX = 2
|
37
37
|
|
38
38
|
# Space around text elements. Mostly used for vertical spacing
|
39
|
-
LEGEND_MARGIN = TITLE_MARGIN =
|
39
|
+
LEGEND_MARGIN = TITLE_MARGIN = 20.0
|
40
|
+
LABEL_MARGIN = 10.0
|
41
|
+
DEFAULT_MARGIN = 20.0
|
40
42
|
|
41
43
|
DEFAULT_TARGET_WIDTH = 800
|
44
|
+
|
45
|
+
THOUSAND_SEPARATOR = ','
|
42
46
|
|
43
47
|
# Blank space above the graph
|
44
48
|
attr_accessor :top_margin
|
@@ -51,6 +55,12 @@ module Gruff
|
|
51
55
|
|
52
56
|
# Blank space to the left of the graph
|
53
57
|
attr_accessor :left_margin
|
58
|
+
|
59
|
+
# Blank space below the title
|
60
|
+
attr_accessor :title_margin
|
61
|
+
|
62
|
+
# Blank space below the legend
|
63
|
+
attr_accessor :legend_margin
|
54
64
|
|
55
65
|
# A hash of names for the individual columns, where the key is the array
|
56
66
|
# index for the column this label represents.
|
@@ -170,8 +180,6 @@ module Gruff
|
|
170
180
|
# Looks for Bitstream Vera as the default font. Expects an environment var
|
171
181
|
# of MAGICK_FONT_PATH to be set. (Uses RMagick's default font otherwise.)
|
172
182
|
def initialize(target_width=DEFAULT_TARGET_WIDTH)
|
173
|
-
@top_margin = @bottom_margin = @left_margin = @right_margin = 20.0
|
174
|
-
|
175
183
|
if not Numeric === target_width
|
176
184
|
geometric_width, geometric_height = target_width.split('x')
|
177
185
|
@columns = geometric_width.to_f
|
@@ -215,6 +223,10 @@ module Gruff
|
|
215
223
|
@marker_font_size = 21.0
|
216
224
|
@legend_font_size = 20.0
|
217
225
|
@title_font_size = 36.0
|
226
|
+
|
227
|
+
@top_margin = @bottom_margin = @left_margin = @right_margin = DEFAULT_MARGIN
|
228
|
+
@legend_margin = LEGEND_MARGIN
|
229
|
+
@title_margin = TITLE_MARGIN
|
218
230
|
|
219
231
|
@legend_box_size = 20.0
|
220
232
|
|
@@ -253,14 +265,23 @@ module Gruff
|
|
253
265
|
@colors << colorname
|
254
266
|
end
|
255
267
|
|
256
|
-
# Replace the entire color list with a new array of colors.
|
257
|
-
# have one more color than the number of datasets you intend to draw. Also
|
268
|
+
# Replace the entire color list with a new array of colors. Also
|
258
269
|
# aliased as the colors= setter method.
|
259
270
|
#
|
271
|
+
# If you specify fewer colors than the number of datasets you intend
|
272
|
+
# to draw, 'increment_color' will cycle through the array, reusing
|
273
|
+
# colors as needed.
|
274
|
+
#
|
275
|
+
# Note that (as with the 'theme' method), you should set up your color
|
276
|
+
# list before you send your data (via the 'data' method). Calls to the
|
277
|
+
# 'data' method made prior to this call will use whatever color scheme
|
278
|
+
# was in place at the time data was called.
|
279
|
+
#
|
260
280
|
# Example:
|
261
281
|
# replace_colors ['#cc99cc', '#d9e043', '#34d8a2']
|
262
282
|
def replace_colors(color_list=[])
|
263
283
|
@colors = color_list
|
284
|
+
@color_index = 0
|
264
285
|
end
|
265
286
|
|
266
287
|
# You can set a theme manually. Assign a hash to this method before you
|
@@ -382,14 +403,14 @@ module Gruff
|
|
382
403
|
def theme_pastel
|
383
404
|
# Colors
|
384
405
|
@colors = [
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
406
|
+
'#a9dada', # blue
|
407
|
+
'#aedaa9', # green
|
408
|
+
'#daaea9', # peach
|
409
|
+
'#dadaa9', # yellow
|
410
|
+
'#a9a9da', # dk purple
|
411
|
+
'#daaeda', # purple
|
412
|
+
'#dadada' # grey
|
413
|
+
]
|
393
414
|
|
394
415
|
self.theme = {
|
395
416
|
:colors => @colors,
|
@@ -403,13 +424,13 @@ module Gruff
|
|
403
424
|
def theme_greyscale
|
404
425
|
# Colors
|
405
426
|
@colors = [
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
427
|
+
'#282828', #
|
428
|
+
'#383838', #
|
429
|
+
'#686868', #
|
430
|
+
'#989898', #
|
431
|
+
'#c8c8c8', #
|
432
|
+
'#e8e8e8', #
|
433
|
+
]
|
413
434
|
|
414
435
|
self.theme = {
|
415
436
|
:colors => @colors,
|
@@ -451,7 +472,7 @@ module Gruff
|
|
451
472
|
# TODO Doesn't work with stacked bar graphs
|
452
473
|
# Original: @maximum_value = larger_than_max?(data_point, index) ? max(data_point, index) : @maximum_value
|
453
474
|
@maximum_value = larger_than_max?(data_point) ? data_point : @maximum_value
|
454
|
-
@has_data = true if @maximum_value
|
475
|
+
@has_data = true if @maximum_value >= 0
|
455
476
|
|
456
477
|
@minimum_value = less_than_min?(data_point) ? data_point : @minimum_value
|
457
478
|
@has_data = true if @minimum_value < 0
|
@@ -489,7 +510,7 @@ module Gruff
|
|
489
510
|
debug {
|
490
511
|
# Outer margin
|
491
512
|
@d.rectangle( @left_margin, @top_margin,
|
492
|
-
|
513
|
+
@raw_columns - @right_margin, @raw_rows - @bottom_margin)
|
493
514
|
# Graph area box
|
494
515
|
@d.rectangle( @graph_left, @graph_top, @graph_right, @graph_bottom)
|
495
516
|
}
|
@@ -549,58 +570,58 @@ module Gruff
|
|
549
570
|
|
550
571
|
def setup_graph_measurements
|
551
572
|
@marker_caps_height = @hide_line_markers ? 0 :
|
552
|
-
|
573
|
+
calculate_caps_height(@marker_font_size)
|
553
574
|
@title_caps_height = @hide_title ? 0 :
|
554
|
-
|
575
|
+
calculate_caps_height(@title_font_size)
|
555
576
|
@legend_caps_height = @hide_legend ? 0 :
|
556
|
-
|
577
|
+
calculate_caps_height(@legend_font_size)
|
557
578
|
|
558
579
|
if @hide_line_markers
|
559
580
|
(@graph_left,
|
560
|
-
|
561
|
-
|
581
|
+
@graph_right_margin,
|
582
|
+
@graph_bottom_margin) = [@left_margin, @right_margin, @bottom_margin]
|
562
583
|
else
|
563
584
|
longest_left_label_width = 0
|
564
585
|
if @has_left_labels
|
565
586
|
longest_left_label_width = calculate_width(@marker_font_size,
|
566
|
-
|
587
|
+
labels.values.inject('') { |value, memo| (value.to_s.length > memo.to_s.length) ? value : memo }) * 1.25
|
567
588
|
else
|
568
589
|
longest_left_label_width = calculate_width(@marker_font_size,
|
569
|
-
|
590
|
+
label(@maximum_value.to_f))
|
570
591
|
end
|
571
592
|
|
572
593
|
# Shift graph if left line numbers are hidden
|
573
594
|
line_number_width = @hide_line_numbers && !@has_left_labels ?
|
574
595
|
0.0 :
|
575
|
-
|
596
|
+
(longest_left_label_width + LABEL_MARGIN * 2)
|
576
597
|
|
577
598
|
@graph_left = @left_margin +
|
578
|
-
|
579
|
-
|
599
|
+
line_number_width +
|
600
|
+
(@y_axis_label.nil? ? 0.0 : @marker_caps_height + LABEL_MARGIN * 2)
|
580
601
|
|
581
602
|
# Make space for half the width of the rightmost column label.
|
582
603
|
# Might be greater than the number of columns if between-style bar markers are used.
|
583
604
|
last_label = @labels.keys.sort.last.to_i
|
584
605
|
extra_room_for_long_label = (last_label >= (@column_count-1) && @center_labels_over_point) ?
|
585
606
|
calculate_width(@marker_font_size, @labels[last_label]) / 2.0 :
|
586
|
-
|
607
|
+
0
|
587
608
|
@graph_right_margin = @right_margin + extra_room_for_long_label
|
588
609
|
|
589
610
|
@graph_bottom_margin = @bottom_margin +
|
590
|
-
|
611
|
+
@marker_caps_height + LABEL_MARGIN
|
591
612
|
end
|
592
613
|
|
593
614
|
@graph_right = @raw_columns - @graph_right_margin
|
594
615
|
@graph_width = @raw_columns - @graph_left - @graph_right_margin
|
595
616
|
|
596
|
-
# When @hide title, leave a
|
617
|
+
# When @hide title, leave a title_margin space for aesthetics.
|
597
618
|
# Same with @hide_legend
|
598
619
|
@graph_top = @top_margin +
|
599
|
-
|
600
|
-
|
620
|
+
(@hide_title ? title_margin : @title_caps_height + title_margin ) +
|
621
|
+
(@hide_legend ? legend_margin : @legend_caps_height + legend_margin)
|
601
622
|
|
602
623
|
x_axis_label_height = @x_axis_label.nil? ? 0.0 :
|
603
|
-
|
624
|
+
@marker_caps_height + LABEL_MARGIN
|
604
625
|
@graph_bottom = @raw_rows - @graph_bottom_margin - x_axis_label_height
|
605
626
|
@graph_height = @graph_bottom - @graph_top
|
606
627
|
end
|
@@ -620,9 +641,9 @@ module Gruff
|
|
620
641
|
@d.pointsize = scale_fontsize(@marker_font_size)
|
621
642
|
@d.gravity = NorthGravity
|
622
643
|
@d = @d.annotate_scaled( @base_image,
|
623
|
-
|
624
|
-
|
625
|
-
|
644
|
+
@raw_columns, 1.0,
|
645
|
+
0.0, x_axis_label_y_coordinate,
|
646
|
+
@x_axis_label, @scale)
|
626
647
|
debug { @d.line 0.0, x_axis_label_y_coordinate, @raw_columns, x_axis_label_y_coordinate }
|
627
648
|
end
|
628
649
|
|
@@ -631,9 +652,9 @@ module Gruff
|
|
631
652
|
@d.rotation = 90.0
|
632
653
|
@d.gravity = CenterGravity
|
633
654
|
@d = @d.annotate_scaled( @base_image,
|
634
|
-
|
635
|
-
|
636
|
-
|
655
|
+
1.0, @raw_rows,
|
656
|
+
@left_margin + @marker_caps_height / 2.0, 0.0,
|
657
|
+
@y_axis_label, @scale)
|
637
658
|
@d.rotation = -90.0
|
638
659
|
end
|
639
660
|
end
|
@@ -674,8 +695,7 @@ module Gruff
|
|
674
695
|
(0..@marker_count).each do |index|
|
675
696
|
y = @graph_top + @graph_height - index.to_f * @increment_scaled
|
676
697
|
|
677
|
-
@d = @d.
|
678
|
-
@d = @d.stroke_width 1
|
698
|
+
@d = @d.fill(@marker_color)
|
679
699
|
@d = @d.line(@graph_left, y, @graph_right, y)
|
680
700
|
|
681
701
|
marker_label = index * @increment + @minimum_value.to_f
|
@@ -689,9 +709,9 @@ module Gruff
|
|
689
709
|
|
690
710
|
# Vertically center with 1.0 for the height
|
691
711
|
@d = @d.annotate_scaled( @base_image,
|
692
|
-
|
693
|
-
|
694
|
-
|
712
|
+
@graph_left - LABEL_MARGIN, 1.0,
|
713
|
+
0.0, y,
|
714
|
+
label(marker_label), @scale)
|
695
715
|
end
|
696
716
|
end
|
697
717
|
|
@@ -765,8 +785,8 @@ module Gruff
|
|
765
785
|
|
766
786
|
current_x_offset = center(sum(label_widths.first))
|
767
787
|
current_y_offset = @hide_title ?
|
768
|
-
@top_margin +
|
769
|
-
|
788
|
+
@top_margin + title_margin :
|
789
|
+
@top_margin + title_margin + @title_caps_height
|
770
790
|
|
771
791
|
@legend_labels.each_with_index do |legend_label, index|
|
772
792
|
|
@@ -778,17 +798,17 @@ module Gruff
|
|
778
798
|
@d.font_weight = NormalWeight
|
779
799
|
@d.gravity = WestGravity
|
780
800
|
@d = @d.annotate_scaled( @base_image,
|
781
|
-
|
782
|
-
|
783
|
-
|
801
|
+
@raw_columns, 1.0,
|
802
|
+
current_x_offset + (legend_square_width * 1.7), current_y_offset,
|
803
|
+
legend_label.to_s, @scale)
|
784
804
|
|
785
805
|
# Now draw box with color of this dataset
|
786
806
|
@d = @d.stroke('transparent')
|
787
807
|
@d = @d.fill @data[index][DATA_COLOR_INDEX]
|
788
808
|
@d = @d.rectangle(current_x_offset,
|
789
|
-
|
790
|
-
|
791
|
-
|
809
|
+
current_y_offset - legend_square_width / 2.0,
|
810
|
+
current_x_offset + legend_square_width,
|
811
|
+
current_y_offset + legend_square_width / 2.0)
|
792
812
|
|
793
813
|
@d.pointsize = @legend_font_size
|
794
814
|
metrics = @d.get_type_metrics(@base_image, legend_label.to_s)
|
@@ -801,7 +821,7 @@ module Gruff
|
|
801
821
|
|
802
822
|
label_widths.shift
|
803
823
|
current_x_offset = center(sum(label_widths.first)) unless label_widths.empty?
|
804
|
-
line_height = [@legend_caps_height, legend_square_width].max +
|
824
|
+
line_height = [@legend_caps_height, legend_square_width].max + legend_margin
|
805
825
|
if label_widths.length > 0
|
806
826
|
# Wrap to next line and shrink available graph dimensions
|
807
827
|
current_y_offset += line_height
|
@@ -826,9 +846,9 @@ module Gruff
|
|
826
846
|
@d.font_weight = BoldWeight
|
827
847
|
@d.gravity = NorthGravity
|
828
848
|
@d = @d.annotate_scaled( @base_image,
|
829
|
-
|
830
|
-
|
831
|
-
|
849
|
+
@raw_columns, 1.0,
|
850
|
+
0, @top_margin,
|
851
|
+
@title, @scale)
|
832
852
|
end
|
833
853
|
|
834
854
|
# Draws column labels below graph, centered over x_offset
|
@@ -847,9 +867,9 @@ module Gruff
|
|
847
867
|
@d.pointsize = scale_fontsize(@marker_font_size)
|
848
868
|
@d.gravity = NorthGravity
|
849
869
|
@d = @d.annotate_scaled(@base_image,
|
850
|
-
|
851
|
-
|
852
|
-
|
870
|
+
1.0, 1.0,
|
871
|
+
x_offset, y_offset,
|
872
|
+
@labels[index], @scale)
|
853
873
|
@labels_seen[index] = 1
|
854
874
|
debug { @d.line 0.0, y_offset, @raw_columns, y_offset }
|
855
875
|
end
|
@@ -864,9 +884,9 @@ module Gruff
|
|
864
884
|
@d.pointsize = scale_fontsize(80)
|
865
885
|
@d.gravity = CenterGravity
|
866
886
|
@d = @d.annotate_scaled( @base_image,
|
867
|
-
|
868
|
-
|
869
|
-
|
887
|
+
@raw_columns, @raw_rows/2.0,
|
888
|
+
0, 10,
|
889
|
+
@no_data_message, @scale)
|
870
890
|
end
|
871
891
|
|
872
892
|
# Finds the best background to render based on the provided theme options.
|
@@ -893,7 +913,7 @@ module Gruff
|
|
893
913
|
# Use with a theme definition method to draw a gradiated background.
|
894
914
|
def render_gradiated_background(top_color, bottom_color)
|
895
915
|
Image.new(@columns, @rows,
|
896
|
-
|
916
|
+
GradientFill.new(0, 0, 100, 0, top_color, bottom_color))
|
897
917
|
end
|
898
918
|
|
899
919
|
# Use with a theme to use an image (800x600 original) background.
|
@@ -1034,37 +1054,28 @@ module Gruff
|
|
1034
1054
|
end
|
1035
1055
|
end
|
1036
1056
|
|
1037
|
-
#
|
1057
|
+
# Returns the next color in your color list.
|
1038
1058
|
def increment_color
|
1039
|
-
|
1040
|
-
|
1041
|
-
return @colors[0]
|
1042
|
-
else
|
1043
|
-
if @color_index < @colors.length
|
1044
|
-
@color_index += 1
|
1045
|
-
return @colors[@color_index - 1]
|
1046
|
-
else
|
1047
|
-
# Start over
|
1048
|
-
@color_index = 0
|
1049
|
-
return @colors[-1]
|
1050
|
-
end
|
1051
|
-
end
|
1059
|
+
@color_index = (@color_index + 1) % @colors.length
|
1060
|
+
return @colors[@color_index - 1]
|
1052
1061
|
end
|
1053
1062
|
|
1054
1063
|
# Return a formatted string representing a number value that should be
|
1055
1064
|
# printed as a label.
|
1056
1065
|
def label(value)
|
1057
|
-
if (@spread.to_f % @marker_count.to_f == 0) || !@y_axis_increment.nil?
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
if @spread > 10.0
|
1066
|
+
label = if (@spread.to_f % @marker_count.to_f == 0) || !@y_axis_increment.nil?
|
1067
|
+
value.to_i.to_s
|
1068
|
+
elsif @spread > 10.0
|
1062
1069
|
sprintf("%0i", value)
|
1063
1070
|
elsif @spread >= 3.0
|
1064
1071
|
sprintf("%0.2f", value)
|
1065
1072
|
else
|
1066
1073
|
value.to_s
|
1067
1074
|
end
|
1075
|
+
|
1076
|
+
parts = label.split('.')
|
1077
|
+
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{THOUSAND_SEPARATOR}")
|
1078
|
+
parts.join('.')
|
1068
1079
|
end
|
1069
1080
|
|
1070
1081
|
# Returns the height of the capital letter 'X' for the current font and
|
@@ -1102,9 +1113,9 @@ module Magick
|
|
1102
1113
|
scaled_height = (height * scale) >= 1 ? (height * scale) : 1
|
1103
1114
|
|
1104
1115
|
self.annotate( img,
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1116
|
+
scaled_width, scaled_height,
|
1117
|
+
x * scale, y * scale,
|
1118
|
+
text)
|
1108
1119
|
end
|
1109
1120
|
|
1110
1121
|
end
|