motion-prime 0.9.6 → 0.9.7
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 +8 -8
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +6 -6
- data/README.md +1 -10
- data/Rakefile +0 -1
- data/app/app_delegate.rb +1 -1
- data/bin/prime +1 -1
- data/files/Gemfile +1 -1
- data/motion-prime/api_client.rb +87 -31
- data/motion-prime/config/base.rb +20 -11
- data/motion-prime/config/config.rb +9 -3
- data/motion-prime/core_ext/kernel.rb +2 -1
- data/motion-prime/core_ext/nil_class.rb +5 -0
- data/motion-prime/delegate/app_delegate.rb +5 -0
- data/motion-prime/elements/base_element.rb +21 -2
- data/motion-prime/elements/draw.rb +6 -16
- data/motion-prime/helpers/has_authorization.rb +1 -1
- data/motion-prime/models/_base_mixin.rb +1 -1
- data/motion-prime/prime.rb +4 -0
- data/motion-prime/screens/extensions/_indicators_mixin.rb +16 -10
- data/motion-prime/screens/screen.rb +1 -1
- data/motion-prime/sections/_async_table_mixin.rb +1 -2
- data/motion-prime/sections/_draw_section_mixin.rb +3 -1
- data/motion-prime/sections/base_section.rb +37 -26
- data/motion-prime/sections/form.rb +23 -8
- data/motion-prime/sections/form/base_field_section.rb +1 -15
- data/motion-prime/sections/form/date_field_section.rb +1 -1
- data/motion-prime/sections/form/password_field_section.rb +1 -1
- data/motion-prime/sections/form/select_field_section.rb +1 -1
- data/motion-prime/sections/form/string_field_section.rb +1 -1
- data/motion-prime/sections/form/submit_field_section.rb +1 -1
- data/motion-prime/sections/form/text_field_section.rb +1 -1
- data/motion-prime/sections/table.rb +58 -27
- data/motion-prime/sections/table/refresh_mixin.rb +1 -1
- data/motion-prime/services/logger.rb +60 -21
- data/motion-prime/support/consts.rb +11 -0
- data/motion-prime/version.rb +1 -1
- data/motion-prime/views/layout.rb +5 -4
- data/motion-prime/views/view_styler.rb +3 -0
- data/spec/{helpers → factories}/delegates.rb +0 -3
- data/spec/{helpers → factories}/init.rb +0 -0
- data/spec/{helpers → factories}/models.rb +0 -0
- data/spec/factories/scaffold/models/task.rb +4 -0
- data/spec/factories/scaffold/screens/tasks.rb +45 -0
- data/spec/factories/scaffold/sections/tasks/form.rb +31 -0
- data/spec/factories/scaffold/sections/tasks/index_cell.rb +4 -0
- data/spec/factories/scaffold/sections/tasks/index_table.rb +12 -0
- data/spec/factories/scaffold/sections/tasks/show.rb +3 -0
- data/spec/factories/scaffold/styles/tasks.rb +18 -0
- data/spec/{helpers → factories}/screens.rb +0 -0
- data/spec/factories/sections.rb +9 -0
- data/spec/features/scaffold/index.rb +14 -0
- data/spec/features/screens/open_screen.rb +14 -2
- data/spec/helpers/has_content.rb +25 -0
- data/spec/unit/models/dirty_spec.rb +0 -1
- data/spec/unit/models/model_spec.rb +2 -0
- data/spec/unit/prime/logger.rb +49 -0
- data/spec/unit/sections/section_spec.rb +59 -0
- data/travis.sh +1 -1
- metadata +36 -41
- data/doc/FAQ.md +0 -2
- data/doc/SECTION.md +0 -7
- data/doc/STYLE.md +0 -39
- data/doc/code/getting_started.rb +0 -106
- data/doc/code/models.rb +0 -36
- data/doc/code/screens.rb +0 -93
- data/doc/code/sections.rb +0 -65
- data/doc/code/tables.rb +0 -54
- data/doc/docs/docco.css +0 -337
- data/doc/docs/getting_started.html +0 -195
- data/doc/docs/index.html +0 -205
- data/doc/docs/models.html +0 -115
- data/doc/docs/public/fonts/aller-bold.eot +0 -0
- data/doc/docs/public/fonts/aller-bold.ttf +0 -0
- data/doc/docs/public/fonts/aller-bold.woff +0 -0
- data/doc/docs/public/fonts/aller-light.eot +0 -0
- data/doc/docs/public/fonts/aller-light.ttf +0 -0
- data/doc/docs/public/fonts/aller-light.woff +0 -0
- data/doc/docs/public/fonts/fleurons.eot +0 -0
- data/doc/docs/public/fonts/fleurons.ttf +0 -0
- data/doc/docs/public/fonts/fleurons.woff +0 -0
- data/doc/docs/public/fonts/novecento-bold.eot +0 -0
- data/doc/docs/public/fonts/novecento-bold.ttf +0 -0
- data/doc/docs/public/fonts/novecento-bold.woff +0 -0
- data/doc/docs/public/images/gray.png +0 -0
- data/doc/docs/public/stylesheets/normalize.css +0 -375
- data/doc/docs/screens.html +0 -187
- data/doc/docs/sections.html +0 -138
- data/doc/docs/tables.html +0 -128
- data/spec/helpers/sections.rb +0 -3
@@ -11,7 +11,7 @@ module MotionPrime
|
|
11
11
|
end
|
12
12
|
element :error_message, type: :error_message, text: proc { |field| field.observing_errors? and field.all_errors.join("\n") }
|
13
13
|
|
14
|
-
|
14
|
+
after_element_render :button, :bind_select_button
|
15
15
|
|
16
16
|
def bind_select_button
|
17
17
|
view(:button).on :touch_down do
|
@@ -9,7 +9,7 @@ module MotionPrime
|
|
9
9
|
end
|
10
10
|
|
11
11
|
element :error_message, type: :error_message, text: proc { |field| field.all_errors.join("\n") if field.observing_errors? }
|
12
|
-
|
12
|
+
after_element_render :input, :bind_text_input
|
13
13
|
|
14
14
|
def value
|
15
15
|
view(:input).text
|
@@ -5,7 +5,7 @@ module MotionPrime
|
|
5
5
|
end
|
6
6
|
element :error_message, type: :error_message, text: proc { |field| field.all_errors.join("\n") if field.observing_errors? }
|
7
7
|
|
8
|
-
|
8
|
+
after_element_render :button, :bind_button
|
9
9
|
|
10
10
|
def bind_button
|
11
11
|
view(:button).on :touch do
|
@@ -8,7 +8,7 @@ module MotionPrime
|
|
8
8
|
end
|
9
9
|
|
10
10
|
element :error_message, type: :error_message, text: proc { |field| field.observing_errors? and field.all_errors.join("\n") }
|
11
|
-
|
11
|
+
after_element_render :input, :bind_text_input
|
12
12
|
|
13
13
|
def value
|
14
14
|
view(:input).text
|
@@ -14,7 +14,7 @@ module MotionPrime
|
|
14
14
|
before_render :render_table
|
15
15
|
after_render :init_pull_to_refresh
|
16
16
|
delegate :init_pull_to_refresh, to: :table_delegate
|
17
|
-
|
17
|
+
delegate :set_options, :update_options, to: :table_element, allow_nil: true
|
18
18
|
|
19
19
|
# Return sections which will be used to render as table cells.
|
20
20
|
#
|
@@ -49,7 +49,7 @@ module MotionPrime
|
|
49
49
|
#
|
50
50
|
# @return [Boolean] true
|
51
51
|
def reload_data
|
52
|
-
|
52
|
+
reset_table_data
|
53
53
|
reload_table_data
|
54
54
|
end
|
55
55
|
|
@@ -60,7 +60,7 @@ module MotionPrime
|
|
60
60
|
reload_data
|
61
61
|
end
|
62
62
|
|
63
|
-
# Reload table view
|
63
|
+
# Reload table view data
|
64
64
|
#
|
65
65
|
# @return [Boolean] true
|
66
66
|
def reload_table_data
|
@@ -78,7 +78,7 @@ module MotionPrime
|
|
78
78
|
# Reset all table data.
|
79
79
|
#
|
80
80
|
# @return [Boolean] true
|
81
|
-
def
|
81
|
+
def reset_table_data
|
82
82
|
@did_appear = false
|
83
83
|
@data = nil
|
84
84
|
@data_stamp = nil
|
@@ -104,7 +104,7 @@ module MotionPrime
|
|
104
104
|
#
|
105
105
|
# @param cell sections [Prime::Section, Array<Prime::Section>] cells which will be removed from table view.
|
106
106
|
# @return [Array<NSIndexPath>] index paths of removed cells.
|
107
|
-
def delete_cell_sections(sections)
|
107
|
+
def delete_cell_sections(sections, &block)
|
108
108
|
paths = []
|
109
109
|
Array.wrap(sections).each do |section|
|
110
110
|
index = index_for_cell_section(section)
|
@@ -113,13 +113,46 @@ module MotionPrime
|
|
113
113
|
delete_from_data(index)
|
114
114
|
end
|
115
115
|
if paths.any?
|
116
|
-
|
117
|
-
|
118
|
-
|
116
|
+
UIView.animate(duration: 0, after: block) do
|
117
|
+
table_view.beginUpdates
|
118
|
+
table_view.deleteRowsAtIndexPaths(paths, withRowAnimation: UITableViewRowAnimationLeft)
|
119
|
+
table_view.endUpdates
|
120
|
+
end
|
119
121
|
end
|
120
122
|
paths
|
121
123
|
end
|
122
124
|
|
125
|
+
# Reloads cells with animation, executes given block before reloading.
|
126
|
+
#
|
127
|
+
# @param cell sections [Prime::Section, Array<Prime::Section>] cells which will be updated.
|
128
|
+
# @param around callback [Proc] Callback which will be executed before reloading.
|
129
|
+
# @return [Array<NSIndexPath>] index paths of reloaded cells.
|
130
|
+
def reload_cell_sections(sections, &block)
|
131
|
+
paths = []
|
132
|
+
Array.wrap(sections).each_with_index do |section, counter|
|
133
|
+
index = index_for_cell_section(section)
|
134
|
+
next Prime.logger.debug("Reload section: `#{section.name}` is not in the list") unless index
|
135
|
+
paths << index
|
136
|
+
block.call(section, index, counter)
|
137
|
+
set_data_stamp(section.object_id)
|
138
|
+
section.reload
|
139
|
+
end
|
140
|
+
table_view.reloadRowsAtIndexPaths(paths, withRowAnimation: UITableViewRowAnimationFade)
|
141
|
+
paths
|
142
|
+
end
|
143
|
+
|
144
|
+
# Changes height of cells with animation.
|
145
|
+
#
|
146
|
+
# @param cell sections [Prime::Section, Array<Prime::Section>] cells which will be updated.
|
147
|
+
# @param height [Integer, Array<Integer>] new height of all cells, or height for each cell.
|
148
|
+
# @return [Array<NSIndexPath>] index paths of removed cells.
|
149
|
+
def resize_cell_sections(sections, height)
|
150
|
+
reload_cell_sections(sections) do |section, index, counter|
|
151
|
+
container_height = height.is_a?(Array) ? height[counter] : height
|
152
|
+
section.container_options[:height] = container_height
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
123
156
|
# Delete section from data at index
|
124
157
|
#
|
125
158
|
# @param index [NSIndexPath] index of cell which will be removed from table data.
|
@@ -127,24 +160,18 @@ module MotionPrime
|
|
127
160
|
if flat_data?
|
128
161
|
delete_from_flat_data(index)
|
129
162
|
else
|
130
|
-
|
163
|
+
delete_from_grouped_data(index)
|
131
164
|
end
|
132
165
|
end
|
133
166
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
def delete_from_groped_data(index)
|
140
|
-
@data[index.section][index.row] = nil
|
141
|
-
@data[index.section].delete_at(index.row)
|
142
|
-
end
|
143
|
-
|
167
|
+
# Get index path for cell section
|
168
|
+
#
|
169
|
+
# @param section [Prime::Section] cell section.
|
170
|
+
# @return index [NSIndexPath] index of cell section.
|
144
171
|
def index_for_cell_section(section)
|
145
172
|
if flat_data?
|
146
173
|
row = @data.try(:index, section)
|
147
|
-
NSIndexPath.indexPathForRow(row, inSection: 0)
|
174
|
+
NSIndexPath.indexPathForRow(row, inSection: 0) if row
|
148
175
|
else
|
149
176
|
(@data || []).each_with_index do |cell_sections, group|
|
150
177
|
row = cell_sections.index(section)
|
@@ -153,12 +180,6 @@ module MotionPrime
|
|
153
180
|
end
|
154
181
|
end
|
155
182
|
|
156
|
-
def reload_cell_section(section)
|
157
|
-
section.elements.values.each(&:compute_options!)
|
158
|
-
section.cached_draw_image = nil
|
159
|
-
# TODO: reset date stamps, reload row
|
160
|
-
end
|
161
|
-
|
162
183
|
def table_styles
|
163
184
|
type = self.is_a?(FormSection) ? :base_form : :base_table
|
164
185
|
|
@@ -326,7 +347,7 @@ module MotionPrime
|
|
326
347
|
styles: styles,
|
327
348
|
parent_view: table_view,
|
328
349
|
reuse_identifier: reuse_identifier,
|
329
|
-
section: header
|
350
|
+
section: header.weak_ref
|
330
351
|
)
|
331
352
|
wrapper.render do |container_view, container_element|
|
332
353
|
header.container_element = container_element
|
@@ -355,6 +376,16 @@ module MotionPrime
|
|
355
376
|
end
|
356
377
|
|
357
378
|
private
|
379
|
+
def delete_from_flat_data(index)
|
380
|
+
@data[index.row] = nil
|
381
|
+
@data.delete_at(index.row)
|
382
|
+
end
|
383
|
+
|
384
|
+
def delete_from_grouped_data(index)
|
385
|
+
@data[index.section][index.row] = nil
|
386
|
+
@data[index.section].delete_at(index.row)
|
387
|
+
end
|
388
|
+
|
358
389
|
def display_pending_cells
|
359
390
|
table_view.visibleCells.each do |cell_view|
|
360
391
|
if cell_view.section && cell_view.section.pending_display
|
@@ -4,7 +4,7 @@ module MotionPrime
|
|
4
4
|
screen.automaticallyAdjustsScrollViewInsets = false
|
5
5
|
|
6
6
|
table_view.addPullToRefreshWithActionHandler(block) # block must be a variable
|
7
|
-
screen.
|
7
|
+
screen.set_options_for table_view.pullToRefreshView, styles: [:base_pull_to_refresh]
|
8
8
|
end
|
9
9
|
|
10
10
|
def finish_pull_to_refresh
|
@@ -5,50 +5,89 @@ module MotionPrime
|
|
5
5
|
LOGGER_DEBUG_LEVEL = 2
|
6
6
|
LOGGER_DEALLOC_LEVEL = 3
|
7
7
|
|
8
|
+
COLORS = {
|
9
|
+
red: [ "\e[0;31m", "\e[0m" ],
|
10
|
+
green: [ "\e[0;32m", "\e[0m" ],
|
11
|
+
yellow: [ "\e[0;33m", "\e[0m" ],
|
12
|
+
blue: [ "\e[0;34m", "\e[0m" ],
|
13
|
+
none: [ "", ""]
|
14
|
+
}
|
15
|
+
|
8
16
|
class_attribute :level, :dealloc_items
|
17
|
+
attr_accessor :disabled
|
9
18
|
|
10
19
|
def initialize
|
11
20
|
@default_level = Config.logger.level.nil? ? :info : Config.logger.level
|
21
|
+
@disabled = false
|
22
|
+
end
|
23
|
+
|
24
|
+
# Log message, colorized if using simulator.
|
25
|
+
# @param message [Array<String>, String] Message or array of messages to log.
|
26
|
+
# @param color [Symbol] Color of message: red, green, yellow, blue.
|
27
|
+
# @return message [Array<String>, String] Logged message
|
28
|
+
def log(objects, label = '', color = :none)
|
29
|
+
message = "#{label || 'PRIME_LOG'} : " + Array.wrap(objects).map(&:inspect).join(',')
|
30
|
+
if Device.simulator?
|
31
|
+
color_parts = COLORS[color] || COLORS[:none]
|
32
|
+
output(color_parts.first + message + color_parts.last)
|
33
|
+
else
|
34
|
+
output(message)
|
35
|
+
end
|
36
|
+
objects
|
37
|
+
end
|
38
|
+
|
39
|
+
# Output message, using "puts" for simulator and NSLog for Device.
|
40
|
+
# @param message [String] Message or array of messages to output.
|
41
|
+
# @return message [Array<String>, String] Message
|
42
|
+
def output(message)
|
43
|
+
return if disabled
|
44
|
+
if Device.simulator?
|
45
|
+
puts(message)
|
46
|
+
else
|
47
|
+
NSLog(message)
|
48
|
+
end
|
49
|
+
message
|
12
50
|
end
|
13
51
|
|
14
52
|
def error(*args)
|
15
|
-
|
53
|
+
log(args, "PRIME_ERROR", :red) if LOGGER_ERROR_LEVEL <= current_level
|
16
54
|
end
|
17
55
|
|
18
56
|
def info(*args)
|
19
|
-
|
57
|
+
log(args, "PRIME_INFO", :green) if LOGGER_INFO_LEVEL <= current_level
|
20
58
|
end
|
21
59
|
|
22
60
|
def debug(*args)
|
23
|
-
|
61
|
+
log(args, "PRIME_DEBUG", :yellow) if LOGGER_DEBUG_LEVEL <= current_level
|
24
62
|
end
|
25
63
|
|
26
64
|
def dealloc_message(type, object, *args)
|
27
65
|
if LOGGER_DEALLOC_LEVEL <= current_level
|
28
66
|
if dealloc_items.include?(type.to_s)
|
29
|
-
|
67
|
+
log([object.object_id, object.to_s] + args, "DEALLOC #{type}", :yellow)
|
30
68
|
end
|
31
69
|
end
|
32
70
|
end
|
33
71
|
|
34
|
-
|
35
|
-
|
36
|
-
|
72
|
+
private
|
73
|
+
def dealloc_items
|
74
|
+
self.class.dealloc_items || []
|
75
|
+
end
|
37
76
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
77
|
+
def current_level
|
78
|
+
current_level = self.class.level || @default_level
|
79
|
+
case current_level.to_s
|
80
|
+
when 'error'
|
81
|
+
LOGGER_ERROR_LEVEL
|
82
|
+
when 'info'
|
83
|
+
LOGGER_INFO_LEVEL
|
84
|
+
when 'debug'
|
85
|
+
LOGGER_DEBUG_LEVEL
|
86
|
+
when 'dealloc'
|
87
|
+
LOGGER_DEALLOC_LEVEL
|
88
|
+
else
|
89
|
+
2
|
90
|
+
end
|
51
91
|
end
|
52
|
-
end
|
53
92
|
end
|
54
93
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# this constants should be listed here to be accessible in constantize
|
2
|
+
|
3
|
+
UIControlContentHorizontalAlignmentCenter
|
4
|
+
UIControlContentHorizontalAlignmentLeft
|
5
|
+
UIControlContentHorizontalAlignmentRight
|
6
|
+
UIControlContentHorizontalAlignmentFill
|
7
|
+
|
8
|
+
UIControlContentVerticalAlignmentCenter
|
9
|
+
UIControlContentVerticalAlignmentTop
|
10
|
+
UIControlContentVerticalAlignmentBottom
|
11
|
+
UIControlContentVerticalAlignmentFill
|
data/motion-prime/version.rb
CHANGED
@@ -18,7 +18,7 @@ module MotionPrime
|
|
18
18
|
view = builder.view
|
19
19
|
insert_index = options.delete(:at_index)
|
20
20
|
|
21
|
-
|
21
|
+
set_options_for(view, options, &block)
|
22
22
|
if superview = render_target || view_stack.last
|
23
23
|
insert_index ? superview.insertSubview(view, atIndex: insert_index) : superview.addSubview(view)
|
24
24
|
end
|
@@ -26,16 +26,17 @@ module MotionPrime
|
|
26
26
|
view
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def set_options_for(view, options = {}, &block)
|
30
30
|
ViewStyler.new(view, options.delete(:parent_bounds), options).apply
|
31
31
|
view_stack.push(view)
|
32
32
|
block.call(view) if block_given?
|
33
33
|
view_stack.pop
|
34
34
|
end
|
35
|
+
alias_method :update_options_for, :set_options_for
|
35
36
|
|
36
37
|
def setup(view, options = {}, &block)
|
37
|
-
puts "DEPRECATION: screen#setup is deprecated, please use screen#
|
38
|
-
|
38
|
+
puts "DEPRECATION: screen#setup is deprecated, please use screen#set_options_for instead"
|
39
|
+
set_options_for(view, options, &block)
|
39
40
|
end
|
40
41
|
|
41
42
|
def view_stack
|
@@ -147,6 +147,9 @@ module MotionPrime
|
|
147
147
|
if key == 'content_horizontal_alignment' && value.is_a?(Symbol) && %[left right center fill].include?(value.to_s)
|
148
148
|
view.setValue "UIControlContentHorizontalAlignment#{value.camelize}".constantize, forKey: camelize_factory(key)
|
149
149
|
true
|
150
|
+
elsif key == 'content_vertical_alignment' && value.is_a?(Symbol) && %[top bottom center fill].include?(value.to_s)
|
151
|
+
view.setValue "UIControlContentHorizontalAlignment#{value.camelize}".constantize, forKey: camelize_factory(key)
|
152
|
+
true
|
150
153
|
elsif key.end_with?('alignment') && value.is_a?(Symbol)
|
151
154
|
view.setValue value.uitextalignment, forKey: camelize_factory(key)
|
152
155
|
true
|
@@ -1,6 +1,3 @@
|
|
1
|
-
# motion_require '../../motion-prime/helpers/has_authorization'
|
2
|
-
# motion_require './../motion-prime/delegate/_base_mixin'
|
3
|
-
# motion_require './../motion-prime/delegate/_navigation_mixin'
|
4
1
|
AppDelegate.send :include, MotionPrime::HasAuthorization
|
5
2
|
AppDelegate.send :include, MotionPrime::DelegateBaseMixin
|
6
3
|
AppDelegate.send :include, MotionPrime::DelegateNavigationMixin
|
File without changes
|
File without changes
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class TasksScreen < Prime::Screen
|
2
|
+
title "Tasks"
|
3
|
+
|
4
|
+
# open_screen "tasks#index"
|
5
|
+
def index
|
6
|
+
set_title "Tasks"
|
7
|
+
set_navigation_right_button 'New' do
|
8
|
+
open_screen "tasks#new"
|
9
|
+
end
|
10
|
+
set_section :tasks_index_table
|
11
|
+
end
|
12
|
+
|
13
|
+
# open_screen "tasks#show"
|
14
|
+
def show
|
15
|
+
@model = params[:model]
|
16
|
+
set_title "Show Task"
|
17
|
+
set_navigation_back_button 'Back'
|
18
|
+
set_navigation_right_button 'Edit' do
|
19
|
+
open_screen "tasks#edit", params: { model: @model }
|
20
|
+
end
|
21
|
+
set_section :tasks_show, model: @model
|
22
|
+
end
|
23
|
+
|
24
|
+
# open_screen "tasks#edit"
|
25
|
+
def edit
|
26
|
+
@model = params[:model]
|
27
|
+
set_title "Edit Task"
|
28
|
+
set_navigation_back_button 'Cancel'
|
29
|
+
set_section :tasks_form, model: @model
|
30
|
+
end
|
31
|
+
|
32
|
+
# open_screen "tasks#new"
|
33
|
+
def new
|
34
|
+
@model = Task.new
|
35
|
+
set_title "New Task"
|
36
|
+
set_navigation_back_button 'Cancel'
|
37
|
+
set_section :tasks_form, model: @model
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_return
|
41
|
+
if action?(:index) || action?(:show)
|
42
|
+
refresh
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|