glimmer 0.1.0.0 → 0.1.2
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/.rvmrc +1 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +54 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/bin/glimmer +132 -0
- data/glimmer.gemspec +145 -0
- data/images/Bitter-sweet.jpg +0 -0
- data/{src → lib}/command_handler.rb +7 -10
- data/{src → lib}/command_handler_chain_factory.rb +29 -22
- data/{src → lib}/command_handler_chain_link.rb +19 -22
- data/{src → lib}/command_handlers.rb +29 -26
- data/{src → lib}/command_handlers/bind_command_handler.rb +25 -28
- data/lib/command_handlers/combo_selection_data_binding_command_handler.rb +39 -0
- data/{src → lib}/command_handlers/data_binding_command_handler.rb +61 -64
- data/lib/command_handlers/list_selection_data_binding_command_handler.rb +42 -0
- data/lib/command_handlers/models/list_observer.rb +31 -0
- data/{src → lib}/command_handlers/models/model_observer.rb +28 -25
- data/{src → lib}/command_handlers/models/observable_array.rb +53 -46
- data/lib/command_handlers/models/observable_model.rb +61 -0
- data/{src → lib}/command_handlers/models/r_runnable.rb +11 -14
- data/{src → lib}/command_handlers/models/r_shell.rb +23 -26
- data/lib/command_handlers/models/r_tab_item_composite.rb +25 -0
- data/{src → lib}/command_handlers/models/r_widget.rb +16 -14
- data/lib/command_handlers/models/r_widget_listener.rb +9 -0
- data/{src → lib}/command_handlers/models/r_widget_packages.rb +5 -8
- data/{src → lib}/command_handlers/models/table_items_updater.rb +38 -39
- data/{src → lib}/command_handlers/models/widget_observer.rb +4 -6
- data/{src → lib}/command_handlers/shell_command_handler.rb +14 -17
- data/{src → lib}/command_handlers/swt_constant_command_handler.rb +18 -21
- data/lib/command_handlers/tab_item_command_handler.rb +22 -0
- data/{src → lib}/command_handlers/table_column_properties_data_binding_command_handler.rb +20 -23
- data/{src → lib}/command_handlers/table_items_data_binding_command_handler.rb +26 -29
- data/{src → lib}/command_handlers/widget_command_handler.rb +22 -25
- data/{src → lib}/command_handlers/widget_listener_command_handler.rb +36 -34
- data/{src → lib}/command_handlers/widget_method_command_handler.rb +18 -21
- data/{src → lib}/glimmer.rb +52 -45
- data/lib/parent.rb +5 -0
- data/{src → lib}/shine.rb +21 -24
- data/{src → lib}/xml_command_handlers.rb +15 -18
- data/{src → lib}/xml_command_handlers/html_command_handler.rb +45 -49
- data/{src → lib}/xml_command_handlers/models/depth_first_search_iterator.rb +17 -20
- data/{src → lib}/xml_command_handlers/models/name_space_visitor.rb +17 -20
- data/{src → lib}/xml_command_handlers/models/node.rb +80 -83
- data/{src → lib}/xml_command_handlers/models/node_visitor.rb +8 -11
- data/{src → lib}/xml_command_handlers/models/xml_visitor.rb +58 -61
- data/{src → lib}/xml_command_handlers/xml_command_handler.rb +18 -22
- data/{src → lib}/xml_command_handlers/xml_name_space_command_handler.rb +31 -34
- data/{src → lib}/xml_command_handlers/xml_tag_command_handler.rb +23 -26
- data/{src → lib}/xml_command_handlers/xml_text_command_handler.rb +19 -22
- data/samples/contactmanager/contact.rb +0 -3
- data/samples/contactmanager/contact_manager.rb +1 -4
- data/samples/contactmanager/contact_manager_presenter.rb +0 -3
- data/samples/contactmanager/contact_repository.rb +0 -3
- data/samples/hello_combo.rb +32 -0
- data/samples/hello_world.rb +1 -4
- data/samples/login.rb +1 -4
- data/samples/tictactoe/tic_tac_toe.rb +2 -5
- data/samples/tictactoe/tic_tac_toe_board.rb +0 -3
- data/test/glimmer_combo_data_binding_test.rb +131 -0
- data/test/glimmer_constant_test.rb +6 -11
- data/test/glimmer_data_binding_test.rb +16 -16
- data/test/glimmer_list_data_binding_test.rb +223 -0
- data/test/glimmer_listeners_test.rb +6 -6
- data/test/glimmer_shine_data_binding_test.rb +7 -6
- data/test/glimmer_tab_item_test.rb +61 -0
- data/test/glimmer_table_data_binding_test.rb +7 -20
- data/test/glimmer_test.rb +18 -13
- data/test/helper.rb +21 -0
- data/test/observable_model_test.rb +2 -4
- data/test/r_widget_test.rb +5 -5
- data/test/samples/contactmanager/contact_manager_presenter_test.rb +1 -4
- data/test/samples/tictactoe/tic_tac_toe_test.rb +1 -4
- data/test/xml/glimmer_xml_test.rb +43 -43
- metadata +210 -105
- data/COPYING.LGPL +0 -504
- data/README +0 -27
- data/src/command_handlers/models/observable_model.rb +0 -35
- data/src/command_handlers/models/r_widget_listener.rb +0 -12
- data/src/parent.rb +0 -8
- data/src/swt.rb +0 -10
- data/src/xml.rb +0 -10
@@ -1,23 +1,30 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@@
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
1
|
+
require File.dirname(__FILE__) + "/command_handler_chain_link"
|
2
|
+
|
3
|
+
class CommandHandlerChainFactory
|
4
|
+
@@dsls = {}
|
5
|
+
|
6
|
+
def self.def_dsl(dsl, *command_handler_array)
|
7
|
+
@@last_chain_link = nil
|
8
|
+
@@chain = nil
|
9
|
+
command_handler_array.each do |command_handler|
|
10
|
+
puts "Loading #{command_handler.class.to_s}..."
|
11
|
+
chain_link = CommandHandlerChainLink.new(command_handler)
|
12
|
+
@@last_chain_link.chain_to(chain_link) if @@last_chain_link
|
13
|
+
@@last_chain_link = chain_link
|
14
|
+
@@chain = chain_link unless @@chain
|
15
|
+
end
|
16
|
+
@@dsls[dsl] = {
|
17
|
+
:last_chain_link => @@last_chain_link,
|
18
|
+
:chain => @@chain
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.select_dsl(dsl)
|
23
|
+
@@last_chain_link = @@dsls[dsl][:last_chain_link]
|
24
|
+
@@chain = @@dsls[dsl][:chain]
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.chain
|
28
|
+
@@chain
|
29
|
+
end
|
23
30
|
end
|
@@ -1,22 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def
|
6
|
-
@
|
7
|
-
end
|
8
|
-
def
|
9
|
-
@
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
return
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
1
|
+
class CommandHandlerChainLink
|
2
|
+
def initialize(command_handler)
|
3
|
+
@command_handler = command_handler
|
4
|
+
end
|
5
|
+
def chain_to(next_chain_link)
|
6
|
+
@next_chain_link = next_chain_link
|
7
|
+
end
|
8
|
+
def handle(parent, command_symbol, *args, &block)
|
9
|
+
if (@command_handler.can_handle?(parent, command_symbol, *args, &block))
|
10
|
+
puts "#{@command_handler.class.to_s} will handle command: #{command_symbol} with arguments #{args}"
|
11
|
+
return @command_handler.do_handle(parent, command_symbol, *args, &block)
|
12
|
+
elsif @next_chain_link
|
13
|
+
return @next_chain_link.handle(parent, command_symbol, *args, &block)
|
14
|
+
else
|
15
|
+
puts "Command: #{command_symbol} cannot be handled!"
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,26 +1,29 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require File.dirname(__FILE__) + "/
|
5
|
-
require File.dirname(__FILE__) + "/command_handlers/
|
6
|
-
require File.dirname(__FILE__) + "/command_handlers/
|
7
|
-
require File.dirname(__FILE__) + "/command_handlers/
|
8
|
-
require File.dirname(__FILE__) + "/command_handlers/table_items_data_binding_command_handler"
|
9
|
-
require File.dirname(__FILE__) + "/command_handlers/table_column_properties_data_binding_command_handler"
|
10
|
-
require File.dirname(__FILE__) + "/command_handlers/data_binding_command_handler"
|
11
|
-
require File.dirname(__FILE__) + "/command_handlers/widget_method_command_handler"
|
12
|
-
require File.dirname(__FILE__) + "/command_handlers/widget_command_handler"
|
13
|
-
require File.dirname(__FILE__) + "/command_handlers/swt_constant_command_handler"
|
14
|
-
|
15
|
-
# edit to add more command handlers and extend Glimmer
|
16
|
-
CommandHandlerChainFactory.
|
17
|
-
ShellCommandHandler.new,
|
18
|
-
WidgetListenerCommandHandler.new,
|
19
|
-
BindCommandHandler.new,
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
1
|
+
require File.dirname(__FILE__) + "/command_handler_chain_factory"
|
2
|
+
require File.dirname(__FILE__) + "/command_handlers/shell_command_handler"
|
3
|
+
require File.dirname(__FILE__) + "/command_handlers/widget_listener_command_handler"
|
4
|
+
require File.dirname(__FILE__) + "/command_handlers/bind_command_handler"
|
5
|
+
require File.dirname(__FILE__) + "/command_handlers/tab_item_command_handler"
|
6
|
+
require File.dirname(__FILE__) + "/command_handlers/combo_selection_data_binding_command_handler"
|
7
|
+
require File.dirname(__FILE__) + "/command_handlers/list_selection_data_binding_command_handler"
|
8
|
+
require File.dirname(__FILE__) + "/command_handlers/table_items_data_binding_command_handler"
|
9
|
+
require File.dirname(__FILE__) + "/command_handlers/table_column_properties_data_binding_command_handler"
|
10
|
+
require File.dirname(__FILE__) + "/command_handlers/data_binding_command_handler"
|
11
|
+
require File.dirname(__FILE__) + "/command_handlers/widget_method_command_handler"
|
12
|
+
require File.dirname(__FILE__) + "/command_handlers/widget_command_handler"
|
13
|
+
require File.dirname(__FILE__) + "/command_handlers/swt_constant_command_handler"
|
14
|
+
|
15
|
+
# edit to add more command handlers and extend Glimmer
|
16
|
+
CommandHandlerChainFactory.def_dsl(:swt,
|
17
|
+
ShellCommandHandler.new,
|
18
|
+
WidgetListenerCommandHandler.new,
|
19
|
+
BindCommandHandler.new,
|
20
|
+
TabItemCommandHandler.new,
|
21
|
+
ComboSelectionDataBindingCommandHandler.new,
|
22
|
+
ListSelectionDataBindingCommandHandler.new,
|
23
|
+
TableItemsDataBindingCommandHandler.new,
|
24
|
+
TableColumnPropertiesDataBindingCommandHandler.new,
|
25
|
+
DataBindingCommandHandler.new,
|
26
|
+
WidgetMethodCommandHandler.new,
|
27
|
+
WidgetCommandHandler.new,
|
28
|
+
SwtConstantCommandHandler.new #at the bottom to avoid stealing commands from other command handlers
|
29
|
+
)
|
@@ -1,29 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
(
|
17
|
-
(args[
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
ModelObserver.new(args[0], args[1].to_s, property_type)
|
27
|
-
end
|
28
|
-
|
1
|
+
require File.dirname(__FILE__) + "/../command_handler"
|
2
|
+
require File.dirname(__FILE__) + "/models/r_widget"
|
3
|
+
require File.dirname(__FILE__) + "/models/model_observer"
|
4
|
+
|
5
|
+
class BindCommandHandler
|
6
|
+
include CommandHandler
|
7
|
+
|
8
|
+
include_package 'org.eclipse.swt.widgets'
|
9
|
+
|
10
|
+
def can_handle?(parent, command_symbol, *args, &block)
|
11
|
+
parent.is_a?(RWidget) and
|
12
|
+
command_symbol.to_s == "bind" and
|
13
|
+
(((args.size == 2) and
|
14
|
+
(args[1].is_a?(Symbol) or args[1].is_a?(String))) or
|
15
|
+
((args.size == 3) and
|
16
|
+
(args[1].is_a?(Symbol) or args[1].is_a?(String)) and
|
17
|
+
(args[2].is_a?(Symbol) or args[2].is_a?(String)))) and
|
18
|
+
block == nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def do_handle(parent, command_symbol, *args, &block)
|
22
|
+
property_type = args[2] if (args.size == 3)
|
23
|
+
ModelObserver.new(args[0], args[1].to_s, property_type)
|
24
|
+
end
|
25
|
+
|
29
26
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../command_handler"
|
2
|
+
require File.dirname(__FILE__) + "/models/r_widget"
|
3
|
+
|
4
|
+
class ComboSelectionDataBindingCommandHandler
|
5
|
+
include CommandHandler
|
6
|
+
include Glimmer
|
7
|
+
|
8
|
+
include_package 'org.eclipse.swt.widgets'
|
9
|
+
|
10
|
+
def can_handle?(parent, command_symbol, *args, &block)
|
11
|
+
parent.is_a?(RWidget) and
|
12
|
+
parent.widget.is_a?(Combo) and
|
13
|
+
command_symbol.to_s == "selection" and
|
14
|
+
args.size == 1 and
|
15
|
+
args[0].is_a?(ModelObserver) and
|
16
|
+
args[0].evaluate_options_property.is_a?(Array) and
|
17
|
+
block == nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def do_handle(parent, command_symbol, *args, &block)
|
21
|
+
model_observer = args[0]
|
22
|
+
widget_observer = WidgetObserver.new(parent, "items")
|
23
|
+
widget_observer.update(model_observer.evaluate_options_property)
|
24
|
+
model = model_observer.model
|
25
|
+
model.extend ObservableModel unless model.is_a?(ObservableModel)
|
26
|
+
model.add_observer(model_observer.options_property_name, widget_observer)
|
27
|
+
|
28
|
+
widget_observer = WidgetObserver.new(parent, "text")
|
29
|
+
widget_observer.update(model_observer.evaluate_property)
|
30
|
+
model.add_observer(model_observer.property_name, widget_observer)
|
31
|
+
|
32
|
+
add_contents(parent) {
|
33
|
+
on_widget_selected {
|
34
|
+
model_observer.update(widget_observer.evaluate_property)
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -1,64 +1,61 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require File.dirname(__FILE__) + "
|
5
|
-
require File.dirname(__FILE__) + "/models/
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
1
|
+
require File.dirname(__FILE__) + "/../command_handler"
|
2
|
+
require File.dirname(__FILE__) + "/models/r_widget"
|
3
|
+
require File.dirname(__FILE__) + "/models/observable_model"
|
4
|
+
require File.dirname(__FILE__) + "/models/model_observer"
|
5
|
+
require File.dirname(__FILE__) + "/models/widget_observer"
|
6
|
+
|
7
|
+
class DataBindingCommandHandler
|
8
|
+
extend Glimmer
|
9
|
+
include CommandHandler
|
10
|
+
|
11
|
+
include_package 'org.eclipse.swt.widgets'
|
12
|
+
|
13
|
+
@@widget_data_binders = {
|
14
|
+
Java::OrgEclipseSwtWidgets::Text => {
|
15
|
+
:text => Proc.new do |rwidget, model_observer|
|
16
|
+
add_contents(rwidget) {
|
17
|
+
on_modify_text { |modify_event|
|
18
|
+
model_observer.update(rwidget.widget.getText)
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end,
|
22
|
+
},
|
23
|
+
Java::OrgEclipseSwtWidgets::Button => {
|
24
|
+
:selection => Proc.new do |rwidget, model_observer|
|
25
|
+
add_contents(rwidget) {
|
26
|
+
on_widget_selected { |selection_event|
|
27
|
+
model_observer.update(rwidget.widget.getSelection)
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
},
|
32
|
+
Java::OrgEclipseSwtWidgets::Spinner => {
|
33
|
+
:selection => Proc.new do |rwidget, model_observer|
|
34
|
+
add_contents(rwidget) {
|
35
|
+
on_widget_selected { |selection_event|
|
36
|
+
model_observer.update(rwidget.widget.getSelection)
|
37
|
+
}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
def can_handle?(parent, command_symbol, *args, &block)
|
44
|
+
parent.is_a?(RWidget) and
|
45
|
+
args.size == 1 and
|
46
|
+
args[0].is_a?(ModelObserver)
|
47
|
+
end
|
48
|
+
|
49
|
+
def do_handle(parent, command_symbol, *args, &block)
|
50
|
+
model_observer = args[0]
|
51
|
+
widget_observer = WidgetObserver.new(parent, command_symbol.to_s)
|
52
|
+
widget_observer.update(model_observer.evaluate_property)
|
53
|
+
model = model_observer.model
|
54
|
+
model.extend ObservableModel unless model.is_a?(ObservableModel)
|
55
|
+
model.add_observer(model_observer.property_name, widget_observer)
|
56
|
+
widget_data_binder_map = @@widget_data_binders[parent.widget.class]
|
57
|
+
widget_data_binder = widget_data_binder_map[command_symbol.to_s.to_sym] if widget_data_binder_map
|
58
|
+
widget_data_binder.call(parent, model_observer) if widget_data_binder
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../command_handler"
|
2
|
+
require File.dirname(__FILE__) + "/models/r_widget"
|
3
|
+
require File.dirname(__FILE__) + "/models/list_observer"
|
4
|
+
|
5
|
+
class ListSelectionDataBindingCommandHandler
|
6
|
+
include CommandHandler
|
7
|
+
include Glimmer
|
8
|
+
|
9
|
+
include_package 'org.eclipse.swt.widgets'
|
10
|
+
|
11
|
+
def can_handle?(parent, command_symbol, *args, &block)
|
12
|
+
parent.is_a?(RWidget) and
|
13
|
+
parent.widget.is_a?(List) and
|
14
|
+
command_symbol.to_s == "selection" and
|
15
|
+
args.size == 1 and
|
16
|
+
args[0].is_a?(ModelObserver) and
|
17
|
+
args[0].evaluate_options_property.is_a?(Array) and
|
18
|
+
block == nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def do_handle(parent, command_symbol, *args, &block)
|
22
|
+
model_observer = args[0]
|
23
|
+
widget_observer = WidgetObserver.new(parent, "items")
|
24
|
+
widget_observer.update(model_observer.evaluate_options_property)
|
25
|
+
model = model_observer.model
|
26
|
+
model.extend ObservableModel unless model.is_a?(ObservableModel)
|
27
|
+
model.add_observer(model_observer.options_property_name, widget_observer)
|
28
|
+
|
29
|
+
property_type = :string
|
30
|
+
property_type = :array if parent.has_style?(multi)
|
31
|
+
list_observer = ListObserver.new(parent, property_type)
|
32
|
+
list_observer.update(model_observer.evaluate_property)
|
33
|
+
model.add_observer(model_observer.property_name, list_observer)
|
34
|
+
|
35
|
+
add_contents(parent) {
|
36
|
+
on_widget_selected {
|
37
|
+
model_observer.update(list_observer.evaluate_property)
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class ListObserver
|
2
|
+
attr_reader :widget
|
3
|
+
@@property_type_updaters = {
|
4
|
+
:string => lambda { |widget, value| widget.widget.select(widget.widget.index_of(value.to_s)) },
|
5
|
+
:array => lambda { |widget, value| widget.widget.selection=((value or []).to_java :string) }
|
6
|
+
}
|
7
|
+
@@property_evaluators = {
|
8
|
+
:string => lambda do |selection_array|
|
9
|
+
return nil if selection_array.empty?
|
10
|
+
selection_array[0]
|
11
|
+
end,
|
12
|
+
:array => lambda do |selection_array|
|
13
|
+
selection_array
|
14
|
+
end
|
15
|
+
}
|
16
|
+
def initialize(widget, property_type)
|
17
|
+
property_type = :string if property_type.nil? or property_type == :undefined
|
18
|
+
@widget = widget
|
19
|
+
@property_type = property_type
|
20
|
+
end
|
21
|
+
def update(value)
|
22
|
+
raise "hell" if value == ["Canada"]
|
23
|
+
@@property_type_updaters[@property_type].call(@widget, value) unless evaluate_property == value
|
24
|
+
end
|
25
|
+
def evaluate_property
|
26
|
+
selection_array = @widget.widget.send("selection").to_a
|
27
|
+
property_value = @@property_evaluators[@property_type].call(selection_array)
|
28
|
+
return property_value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|