apexcharts 0.1.9 → 0.2.0
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 +4 -4
- data/README.md +149 -56
- data/lib/apex_charts/charts/base_chart.rb +1 -1
- data/lib/apex_charts/charts/box_plot_chart.rb +9 -0
- data/lib/apex_charts/charts.rb +1 -0
- data/lib/apex_charts/helper.rb +9 -8
- data/lib/apex_charts/options_builder.rb +146 -123
- data/lib/apex_charts/renderer.rb +69 -25
- data/lib/apex_charts/series/bubble_series.rb +2 -3
- data/lib/apex_charts/series/cartesian_series.rb +8 -2
- data/lib/apex_charts/theme.rb +0 -16
- data/lib/apex_charts/version.rb +2 -1
- data/vendor/assets/javascripts/apexcharts.js +5 -5
- metadata +4 -51
- data/lib/apex_charts/options/annotations_options.rb +0 -12
- data/lib/apex_charts/options/axis_options.rb +0 -19
- data/lib/apex_charts/options/chart_options.rb +0 -28
- data/lib/apex_charts/options/data_labels_options.rb +0 -16
- data/lib/apex_charts/options/div_attributes.rb +0 -11
- data/lib/apex_charts/options/fill_options.rb +0 -14
- data/lib/apex_charts/options/grid_options.rb +0 -17
- data/lib/apex_charts/options/legend_options.rb +0 -29
- data/lib/apex_charts/options/markers_options.rb +0 -20
- data/lib/apex_charts/options/no_data_options.rb +0 -14
- data/lib/apex_charts/options/plot_options.rb +0 -14
- data/lib/apex_charts/options/root_options.rb +0 -46
- data/lib/apex_charts/options/states_options.rb +0 -11
- data/lib/apex_charts/options/stroke_options.rb +0 -14
- data/lib/apex_charts/options/theme_options.rb +0 -10
- data/lib/apex_charts/options/title_subtitle_options.rb +0 -15
- data/lib/apex_charts/options/tooltip_options.rb +0 -24
- data/lib/apex_charts/options/x_axis_options.rb +0 -14
- data/lib/apex_charts/options/y_axis_options.rb +0 -18
- data/lib/apexcharts/prefix_with_apex.rb +0 -20
@@ -1,28 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'smart_kv'
|
4
|
-
require_relative 'options/root_options'
|
5
|
-
require_relative 'options/annotations_options'
|
6
|
-
require_relative 'options/data_labels_options'
|
7
|
-
require_relative 'options/fill_options'
|
8
|
-
require_relative 'options/grid_options'
|
9
|
-
require_relative 'options/legend_options'
|
10
|
-
require_relative 'options/markers_options'
|
11
|
-
require_relative 'options/no_data_options'
|
12
|
-
require_relative 'options/plot_options'
|
13
|
-
require_relative 'options/states_options'
|
14
|
-
require_relative 'options/title_subtitle_options'
|
15
|
-
require_relative 'options/theme_options'
|
16
|
-
require_relative 'options/tooltip_options'
|
17
|
-
require_relative 'options/x_axis_options'
|
18
|
-
require_relative 'options/y_axis_options'
|
19
|
-
|
20
3
|
module ApexCharts
|
21
4
|
class OptionsBuilder
|
22
5
|
include ApexCharts::Utils::Hash
|
23
6
|
include ApexCharts::Utils::DateTime
|
24
7
|
|
25
|
-
|
8
|
+
attr_accessor :built
|
26
9
|
|
27
10
|
def initialize(sample, options)
|
28
11
|
@options = camelize_keys(options)
|
@@ -35,8 +18,6 @@ module ApexCharts
|
|
35
18
|
end
|
36
19
|
|
37
20
|
def build_options
|
38
|
-
Options::RootOptions.check @options
|
39
|
-
|
40
21
|
build_chart
|
41
22
|
build_div
|
42
23
|
build_general_options
|
@@ -46,11 +27,13 @@ module ApexCharts
|
|
46
27
|
build_annotations
|
47
28
|
build_colors
|
48
29
|
build_data_labels
|
30
|
+
build_defer
|
49
31
|
build_fill
|
50
32
|
build_grid
|
51
33
|
build_labels
|
52
34
|
build_legend
|
53
35
|
build_markers
|
36
|
+
build_module
|
54
37
|
build_no_data
|
55
38
|
build_plot_options
|
56
39
|
build_responsive
|
@@ -66,7 +49,7 @@ module ApexCharts
|
|
66
49
|
end
|
67
50
|
|
68
51
|
def build_div
|
69
|
-
|
52
|
+
built[:div] = {
|
70
53
|
id: @options.delete(:id),
|
71
54
|
var: @options.delete(:var),
|
72
55
|
class: @options.delete(:class),
|
@@ -76,20 +59,23 @@ module ApexCharts
|
|
76
59
|
|
77
60
|
def build_annotations
|
78
61
|
annotations = @options.delete :annotations
|
79
|
-
|
80
|
-
|
62
|
+
built[:annotations] = (
|
63
|
+
annotations.compact if annotations.is_a? Hash
|
81
64
|
)
|
82
65
|
end
|
83
66
|
|
84
67
|
def build_chart
|
85
|
-
|
68
|
+
built[:chart] =
|
86
69
|
if target = @options.delete(:brushTarget)
|
87
|
-
{
|
70
|
+
{
|
71
|
+
brush: {enabled: true, target: target.to_s},
|
72
|
+
selection: {enabled: true}
|
73
|
+
}
|
88
74
|
else
|
89
75
|
{}
|
90
76
|
end
|
91
77
|
|
92
|
-
|
78
|
+
built[:chart].merge!({
|
93
79
|
id: @options[:chartId] || @options[:id],
|
94
80
|
group: @options.delete(:group),
|
95
81
|
height: @options.delete(:height) { target ? 180 : 400 },
|
@@ -105,203 +91,234 @@ module ApexCharts
|
|
105
91
|
|
106
92
|
return unless chart.is_a? Hash
|
107
93
|
|
108
|
-
|
94
|
+
built[:chart].merge! chart.compact
|
109
95
|
end
|
110
96
|
|
111
97
|
def build_colors
|
112
98
|
colors = @options.delete :colors
|
113
99
|
colors &&= Array(colors)
|
114
|
-
|
100
|
+
built[:colors] = colors
|
115
101
|
end
|
116
102
|
|
117
103
|
def build_data_labels
|
118
104
|
data_labels = @options.delete :dataLabels
|
119
105
|
return if data_labels.nil?
|
120
106
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
107
|
+
built[:dataLabels] = if [true, false].include? data_labels
|
108
|
+
{enabled: data_labels}
|
109
|
+
elsif data_labels.is_a? Hash
|
110
|
+
data_labels.compact
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def build_defer
|
115
|
+
defer = @options.delete :defer
|
116
|
+
built[:defer] = defer == true
|
117
|
+
end
|
118
|
+
|
119
|
+
def build_module
|
120
|
+
omodule = @options.delete :module
|
121
|
+
built[:module] = omodule == true
|
126
122
|
end
|
127
123
|
|
128
124
|
def build_fill
|
129
125
|
fill = @options.delete :fill
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
126
|
+
built[:fill] = case fill
|
127
|
+
when String
|
128
|
+
{type: fill}
|
129
|
+
when Hash
|
130
|
+
fill.compact
|
131
|
+
end
|
135
132
|
end
|
136
133
|
|
137
134
|
def build_grid
|
138
135
|
grid = @options.delete :grid
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
136
|
+
built[:grid] = if [true, false].include? grid
|
137
|
+
{show: grid}
|
138
|
+
elsif grid.is_a? Hash
|
139
|
+
grid.compact
|
140
|
+
end
|
144
141
|
end
|
145
142
|
|
146
143
|
def build_labels
|
147
144
|
labels = @options.delete :labels
|
148
145
|
labels &&= Array(labels)
|
149
|
-
|
146
|
+
built[:labels] = labels
|
150
147
|
end
|
151
148
|
|
152
149
|
def build_legend
|
153
150
|
legend = @options.delete :legend
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
151
|
+
built[:legend] = if [true, false].include? legend
|
152
|
+
{show: legend}
|
153
|
+
elsif legend.is_a? String
|
154
|
+
{show: true, position: legend}
|
155
|
+
elsif legend.is_a? Hash
|
156
|
+
legend.compact
|
157
|
+
end
|
161
158
|
end
|
162
159
|
|
163
160
|
def build_markers
|
164
161
|
markers = @options.delete :markers
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
162
|
+
built[:markers] = case markers
|
163
|
+
when String
|
164
|
+
{shape: markers}
|
165
|
+
when Hash
|
166
|
+
markers.compact
|
167
|
+
end
|
170
168
|
end
|
171
169
|
|
172
170
|
def build_no_data
|
173
171
|
no_data = @options.delete :noData
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
172
|
+
built[:noData] = case no_data
|
173
|
+
when String
|
174
|
+
{text: no_data}
|
175
|
+
when Hash
|
176
|
+
no_data.compact
|
177
|
+
end
|
179
178
|
end
|
180
179
|
|
181
180
|
def build_plot_options
|
182
181
|
plot_options = @options.delete :plotOptions
|
183
182
|
return unless plot_options.is_a? Hash
|
184
183
|
|
185
|
-
|
186
|
-
|
184
|
+
built[:plotOptions] =
|
185
|
+
plot_options.compact
|
187
186
|
end
|
188
187
|
|
189
188
|
def build_responsive
|
190
189
|
responsive = @options.delete :responsive
|
191
190
|
responsive &&= responsive.is_a?(Hash) ? [responsive] : Array(responsive)
|
192
|
-
|
191
|
+
built[:responsive] = responsive
|
193
192
|
end
|
194
193
|
|
195
194
|
def build_states
|
196
|
-
|
195
|
+
built[:states] = {
|
197
196
|
normal: filter_type_hash(@options.delete(:normal)),
|
198
197
|
hover: filter_type_hash(@options.delete(:hover)),
|
199
198
|
active: filter_type_hash(@options.delete(:active))
|
200
199
|
}.compact
|
201
200
|
|
202
201
|
states = @options.delete :states
|
203
|
-
|
202
|
+
built[:states].merge! states.compact if states.is_a? Hash
|
204
203
|
|
205
|
-
|
204
|
+
built[:states] = nil if built[:states].empty?
|
206
205
|
end
|
207
206
|
|
208
207
|
def build_stroke
|
209
208
|
curve = @options.delete :curve
|
210
|
-
|
209
|
+
built[:stroke] = {curve: curve}.compact
|
211
210
|
|
212
211
|
stroke = @options.delete :stroke
|
213
212
|
if [true, false].include? stroke
|
214
|
-
|
213
|
+
built[:stroke].merge!(show: stroke)
|
215
214
|
elsif stroke.is_a? Hash
|
216
|
-
|
215
|
+
built[:stroke].merge! stroke.compact
|
217
216
|
end
|
218
217
|
|
219
|
-
|
218
|
+
built[:stroke] = nil if built[:stroke].empty?
|
220
219
|
end
|
221
220
|
|
222
221
|
def build_subtitle
|
223
222
|
subtitle = @options.delete(:subtitle)
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
223
|
+
built[:subtitle] = case subtitle
|
224
|
+
when String
|
225
|
+
{text: subtitle}
|
226
|
+
when Hash
|
227
|
+
subtitle.compact
|
228
|
+
end
|
229
229
|
end
|
230
230
|
|
231
231
|
def build_theme
|
232
232
|
theme = @options.delete(:theme)
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
233
|
+
built[:theme] = case theme
|
234
|
+
when String
|
235
|
+
case theme
|
236
|
+
when 'random'
|
237
|
+
resolve_theme(Theme.all_palettes.sample)
|
238
|
+
when 'monochrome'
|
239
|
+
{monochrome: {enabled: true}}
|
240
|
+
else
|
241
|
+
resolve_theme(theme)
|
242
|
+
end
|
243
|
+
when Hash
|
244
|
+
theme.compact
|
245
|
+
end
|
245
246
|
end
|
246
247
|
|
247
248
|
def build_title
|
248
249
|
title = @options.delete(:title)
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
250
|
+
built[:title] = case title
|
251
|
+
when String
|
252
|
+
{text: title}
|
253
|
+
when Hash
|
254
|
+
title.compact
|
255
|
+
end
|
254
256
|
end
|
255
257
|
|
256
258
|
def build_tooltip
|
257
259
|
tooltip = @options.delete :tooltip
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
260
|
+
built[:tooltip] = if [true, false].include? tooltip
|
261
|
+
{enabled: tooltip}
|
262
|
+
elsif tooltip.is_a? Hash
|
263
|
+
tooltip.compact
|
264
|
+
end
|
263
265
|
end
|
264
266
|
|
265
267
|
def build_xaxis
|
266
268
|
xaxis = @options.delete :xaxis
|
267
|
-
|
269
|
+
built[:xaxis] = {
|
268
270
|
type: @options.delete(:xtype) { @xtype },
|
269
271
|
title: {
|
270
272
|
text: @options.delete(:xtitle)
|
271
273
|
}.compact
|
272
274
|
}.compact
|
273
|
-
|
275
|
+
built[:xaxis].delete(:title) if built[:xaxis][:title].empty?
|
274
276
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
277
|
+
case xaxis
|
278
|
+
when String
|
279
|
+
built[:xaxis][:title] = {text: xaxis}
|
280
|
+
when Hash
|
281
|
+
built[:xaxis].merge! xaxis
|
280
282
|
end
|
281
283
|
|
282
|
-
|
284
|
+
built[:xaxis] = nil if built[:xaxis].empty?
|
283
285
|
end
|
284
286
|
|
285
287
|
def build_yaxis
|
286
288
|
yaxis = @options.delete :yaxis
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
289
|
+
|
290
|
+
case yaxis
|
291
|
+
when Array
|
292
|
+
built[:yaxis] = yaxis
|
293
|
+
when Hash
|
294
|
+
built[:yaxis] = {
|
295
|
+
type: @options.delete(:ytype) { @ytype },
|
296
|
+
title: {
|
297
|
+
text: @options.delete(:ytitle)
|
298
|
+
}.compact
|
291
299
|
}.compact
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
+
built[:yaxis].delete(:title) if built[:yaxis][:title].empty?
|
301
|
+
built[:yaxis].merge! yaxis
|
302
|
+
when String
|
303
|
+
built[:yaxis] = {
|
304
|
+
type: @options.delete(:ytype) { @ytype },
|
305
|
+
title: {
|
306
|
+
text: yaxis
|
307
|
+
}
|
308
|
+
}.compact
|
309
|
+
when NilClass
|
310
|
+
built[:yaxis] = if ytitle = @options.delete(:ytitle)
|
311
|
+
{title: {text: ytitle}}
|
312
|
+
else
|
313
|
+
{}
|
314
|
+
end
|
300
315
|
end
|
301
316
|
|
302
|
-
|
317
|
+
built[:yaxis] = nil if built[:yaxis].all?(&:empty?)
|
303
318
|
end
|
304
319
|
|
320
|
+
private
|
321
|
+
|
305
322
|
def enabled(options)
|
306
323
|
boolean_to_hash(options) do |opts|
|
307
324
|
{enabled: opts}
|
@@ -319,9 +336,10 @@ module ApexCharts
|
|
319
336
|
end
|
320
337
|
|
321
338
|
def filter_type_hash(state)
|
322
|
-
|
339
|
+
case state
|
340
|
+
when String
|
323
341
|
{filter: {type: state}}
|
324
|
-
|
342
|
+
when Hash
|
325
343
|
state.compact
|
326
344
|
end
|
327
345
|
end
|
@@ -329,10 +347,15 @@ module ApexCharts
|
|
329
347
|
def resolve_theme(theme)
|
330
348
|
if Theme::PALETTES.include? theme
|
331
349
|
{palette: theme}
|
332
|
-
elsif Theme
|
333
|
-
|
350
|
+
elsif Theme.palette_names.include? theme
|
351
|
+
built[:colors] = Theme.get_colors(theme)
|
334
352
|
nil
|
335
353
|
end
|
336
354
|
end
|
355
|
+
|
356
|
+
def options_class(name)
|
357
|
+
schema = ApexCharts.config.schema
|
358
|
+
ApexCharts.const_get "Options::#{schema}::#{name}Options"
|
359
|
+
end
|
337
360
|
end
|
338
361
|
end
|
data/lib/apex_charts/renderer.rb
CHANGED
@@ -1,31 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'json'
|
4
|
+
require_relative 'version'
|
4
5
|
|
5
6
|
module ApexCharts
|
6
7
|
class Renderer
|
7
8
|
class << self
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
html = if renderer.id_number == '1' && !ApexCharts.config.default_options.empty?
|
12
|
-
renderer.window_apex
|
13
|
-
else
|
14
|
-
''
|
15
|
-
end
|
16
|
-
html + <<~HTML
|
17
|
-
<div id="#{renderer.element_id}" class="#{renderer.css_class}" style="#{renderer.style}"></div>
|
18
|
-
<script type="text/javascript">
|
19
|
-
var #{renderer.variable} = new ApexCharts(document.querySelector("##{renderer.element_id}"), #{substitute_function_object(renderer.options.to_json)});
|
20
|
-
#{renderer.variable}.render();
|
21
|
-
</script>
|
22
|
-
HTML
|
23
|
-
end
|
24
|
-
|
25
|
-
def substitute_function_object(json)
|
26
|
-
json.gsub(%r[{"function":{"args":"(?<args>.*?)","body":"(?<body>.*?)"}}]) do
|
27
|
-
"function(#{$~&.[](:args)}){#{$~&.[](:body)}}"
|
28
|
-
end
|
9
|
+
def render(options)
|
10
|
+
new(options).render
|
29
11
|
end
|
30
12
|
end
|
31
13
|
|
@@ -35,6 +17,57 @@ module ApexCharts
|
|
35
17
|
@options = options
|
36
18
|
end
|
37
19
|
|
20
|
+
def render
|
21
|
+
html = ''
|
22
|
+
html = window_apex if id_number == '1' && !ApexCharts.config.default_options.empty?
|
23
|
+
|
24
|
+
chart_rendering = <<~JS
|
25
|
+
var #{variable} = new ApexCharts(document.querySelector("##{element_id}"), #{substitute_function_object(options.to_json)});
|
26
|
+
#{variable}.render();
|
27
|
+
JS
|
28
|
+
|
29
|
+
html += <<~HTML
|
30
|
+
<div id="#{element_id}" class="#{css_class}" style="#{style}"></div>
|
31
|
+
#{script(defer(chart_rendering))}
|
32
|
+
HTML
|
33
|
+
end
|
34
|
+
|
35
|
+
def defer(js)
|
36
|
+
if defer?
|
37
|
+
<<~DEFERRED
|
38
|
+
(function() {
|
39
|
+
var createChart = function() {
|
40
|
+
#{indent(js)}
|
41
|
+
};
|
42
|
+
if (window.addEventListener) {
|
43
|
+
window.addEventListener("load", createChart, true);
|
44
|
+
} else if (window.attachEvent) {
|
45
|
+
window.attachEvent("onload", createChart);
|
46
|
+
} else {
|
47
|
+
createChart();
|
48
|
+
}
|
49
|
+
})();
|
50
|
+
DEFERRED
|
51
|
+
else
|
52
|
+
js
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def substitute_function_object(json)
|
57
|
+
json.gsub(/{"function":{"args":"(?<args>.*?)","body":"(?<body>.*?)"}}/) do
|
58
|
+
body = "\"#{$LAST_MATCH_INFO&.[](:body)}\"".undump
|
59
|
+
"function(#{$LAST_MATCH_INFO&.[](:args)}){#{body}}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def defer?
|
64
|
+
@defer ||= options.delete(:defer)
|
65
|
+
end
|
66
|
+
|
67
|
+
def module?
|
68
|
+
@module ||= options.delete(:module)
|
69
|
+
end
|
70
|
+
|
38
71
|
def attributes
|
39
72
|
@attributes ||= options.delete(:div) { {} }
|
40
73
|
end
|
@@ -64,11 +97,22 @@ module ApexCharts
|
|
64
97
|
end
|
65
98
|
|
66
99
|
def window_apex
|
67
|
-
|
68
|
-
|
69
|
-
|
100
|
+
script("window.Apex = #{ApexCharts.config.default_options.to_json}")
|
101
|
+
end
|
102
|
+
|
103
|
+
def script(js)
|
104
|
+
type = module? ? 'module' : 'text/javascript'
|
105
|
+
<<~SCRIPT
|
106
|
+
<script type="#{type}" apexcharts-rb="#{RELEASE}" >
|
107
|
+
#{js}
|
70
108
|
</script>
|
71
|
-
|
109
|
+
SCRIPT
|
110
|
+
end
|
111
|
+
|
112
|
+
def indent(content, times=2)
|
113
|
+
content.lines.map.with_index do |line, index|
|
114
|
+
(index.zero? ? '' : ' ' * times) + line
|
115
|
+
end.join
|
72
116
|
end
|
73
117
|
end
|
74
118
|
end
|
@@ -15,7 +15,8 @@ module ApexCharts::Series
|
|
15
15
|
def sample
|
16
16
|
return if empty?
|
17
17
|
|
18
|
-
sanitized[:series][0][:data][0]
|
18
|
+
first_data = sanitized[:series][0][:data][0]
|
19
|
+
first_data.is_a?(Hash) ? first_data[:x] : first_data
|
19
20
|
end
|
20
21
|
|
21
22
|
private
|
@@ -75,7 +76,12 @@ module ApexCharts::Series
|
|
75
76
|
end
|
76
77
|
|
77
78
|
def array_of_array_to_array_of_xy(data)
|
78
|
-
data.
|
79
|
+
case data.first
|
80
|
+
when Array
|
81
|
+
data.map {|d| {x: d.first, y: d.last} }
|
82
|
+
else
|
83
|
+
data
|
84
|
+
end
|
79
85
|
end
|
80
86
|
end
|
81
87
|
end
|
data/lib/apex_charts/theme.rb
CHANGED
@@ -39,22 +39,6 @@ module ApexCharts
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
class Local
|
43
|
-
module LocalClassMethods
|
44
|
-
include ClassMethods
|
45
|
-
|
46
|
-
def custom_palettes
|
47
|
-
Theme.palettes.merge(super)
|
48
|
-
end
|
49
|
-
|
50
|
-
def palettes
|
51
|
-
Thread.current[:_ApexCharts_Palettes_] ||= {}
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
extend LocalClassMethods
|
56
|
-
end
|
57
|
-
|
58
42
|
extend ClassMethods
|
59
43
|
end
|
60
44
|
end
|