vedeu 0.2.1 → 0.2.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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/README.md +1 -2
  4. data/Rakefile +5 -5
  5. data/bin/vedeu +8 -4
  6. data/docs/api.md +3 -1
  7. data/docs/events.md +43 -29
  8. data/docs/getting_started.md +2 -0
  9. data/examples/cursor_app/cursor_app.rb +85 -0
  10. data/examples/lines_app/lines_app.rb +60 -0
  11. data/lib/vedeu.rb +9 -3
  12. data/lib/vedeu/api/api.rb +28 -7
  13. data/lib/vedeu/api/composition.rb +2 -0
  14. data/lib/vedeu/api/helpers.rb +2 -0
  15. data/lib/vedeu/api/interface.rb +2 -20
  16. data/lib/vedeu/api/keymap.rb +2 -0
  17. data/lib/vedeu/api/line.rb +2 -0
  18. data/lib/vedeu/api/menu.rb +3 -1
  19. data/lib/vedeu/api/stream.rb +25 -2
  20. data/lib/vedeu/application.rb +4 -4
  21. data/lib/vedeu/configuration/api.rb +327 -0
  22. data/lib/vedeu/configuration/cli.rb +110 -0
  23. data/lib/vedeu/{configuration.rb → configuration/configuration.rb} +49 -99
  24. data/lib/vedeu/launcher.rb +0 -1
  25. data/lib/vedeu/models/attributes/colour_translator.rb +2 -2
  26. data/lib/vedeu/models/colour.rb +0 -1
  27. data/lib/vedeu/models/composition.rb +2 -8
  28. data/lib/vedeu/models/cursor.rb +261 -0
  29. data/lib/vedeu/models/geometry.rb +11 -19
  30. data/lib/vedeu/models/interface.rb +2 -12
  31. data/lib/vedeu/models/keymap.rb +2 -0
  32. data/lib/vedeu/models/line.rb +2 -0
  33. data/lib/vedeu/models/stream.rb +2 -0
  34. data/lib/vedeu/models/style.rb +2 -0
  35. data/lib/vedeu/output/clear.rb +0 -1
  36. data/lib/vedeu/output/compositor.rb +0 -1
  37. data/lib/vedeu/output/refresh.rb +3 -0
  38. data/lib/vedeu/output/render.rb +12 -6
  39. data/lib/vedeu/output/view.rb +1 -0
  40. data/lib/vedeu/repositories/cursors.rb +98 -0
  41. data/lib/vedeu/repositories/events.rb +0 -1
  42. data/lib/vedeu/repositories/focus.rb +18 -0
  43. data/lib/vedeu/repositories/menus.rb +4 -4
  44. data/lib/vedeu/support/common.rb +2 -1
  45. data/lib/vedeu/support/event.rb +1 -10
  46. data/lib/vedeu/support/grid.rb +1 -2
  47. data/lib/vedeu/{repositories → support}/keymap_validator.rb +5 -4
  48. data/lib/vedeu/support/log.rb +3 -0
  49. data/lib/vedeu/support/menu.rb +4 -1
  50. data/lib/vedeu/support/registrar.rb +2 -1
  51. data/test/integration/defining_interfaces_test.rb +0 -1
  52. data/test/integration/views/basic_view_test.rb +741 -739
  53. data/test/lib/vedeu/api/api_test.rb +14 -3
  54. data/test/lib/vedeu/api/helpers_test.rb +3 -3
  55. data/test/lib/vedeu/api/interface_test.rb +17 -70
  56. data/test/lib/vedeu/api/keymap_test.rb +2 -0
  57. data/test/lib/vedeu/api/line_test.rb +4 -4
  58. data/test/lib/vedeu/api/menu_test.rb +6 -5
  59. data/test/lib/vedeu/api/stream_test.rb +18 -0
  60. data/test/lib/vedeu/configuration/api_test.rb +248 -0
  61. data/test/lib/vedeu/configuration/cli_test.rb +88 -0
  62. data/test/lib/vedeu/configuration/configuration_test.rb +67 -0
  63. data/test/lib/vedeu/input/input_test.rb +2 -2
  64. data/test/lib/vedeu/models/attributes/background_test.rb +3 -3
  65. data/test/lib/vedeu/models/attributes/foreground_test.rb +3 -3
  66. data/test/lib/vedeu/models/composition_test.rb +0 -222
  67. data/test/lib/vedeu/models/cursor_test.rb +164 -0
  68. data/test/lib/vedeu/models/interface_test.rb +0 -11
  69. data/test/lib/vedeu/output/compositor_test.rb +2 -4
  70. data/test/lib/vedeu/output/render_test.rb +4 -41
  71. data/test/lib/vedeu/repositories/cursors_test.rb +13 -0
  72. data/test/lib/vedeu/repositories/focus_test.rb +14 -4
  73. data/test/lib/vedeu/repositories/menus_test.rb +36 -29
  74. data/test/lib/vedeu/{repositories → support}/keymap_validator_test.rb +0 -0
  75. data/test/lib/vedeu/support/menu_test.rb +3 -3
  76. data/test/lib/vedeu/support/registrar_test.rb +6 -0
  77. data/test/lib/vedeu/support/terminal_test.rb +2 -2
  78. data/test/test_helper.rb +1 -1
  79. data/vedeu.gemspec +1 -1
  80. metadata +23 -14
  81. data/elements.txt +0 -118
  82. data/lib/vedeu/support/cursor.rb +0 -96
  83. data/test/lib/vedeu/configuration_test.rb +0 -154
  84. data/test/lib/vedeu/support/cursor_test.rb +0 -79
  85. data/test/support/model_test_data.json +0 -437
@@ -0,0 +1,110 @@
1
+ module Vedeu
2
+
3
+ module Configuration
4
+
5
+ # The Configuration::CLI class parses command-line arguments using
6
+ # OptionParser into options used by Vedeu to affect certain behaviours.
7
+ #
8
+ # @api private
9
+ class CLI
10
+
11
+ # Configure Vedeu via command-line arguments. Options set here via
12
+ # arguments override the client application configuration set via
13
+ # {Vedeu::API#configure}.
14
+ #
15
+ # @param args [Array]
16
+ # @return [Hash]
17
+ def self.configure(args = [])
18
+ new(args = []).configuration
19
+ end
20
+
21
+ # Returns an instance of Configuration::CLI.
22
+ #
23
+ # @param args [Array]
24
+ # @return [Configuration::CLI]
25
+ def initialize(args = [])
26
+ @args = args
27
+ end
28
+
29
+ # Returns the configuration options set up by parsing the command-line
30
+ # arguments passed to the client application.
31
+ #
32
+ # @return [Hash]
33
+ def configuration
34
+ parser = OptionParser.new do |opts|
35
+ opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
36
+
37
+ opts.on('-i', '--interactive',
38
+ 'Run the application in interactive mode (default).') do
39
+ options[:interactive] = true
40
+ end
41
+
42
+ opts.on('-I', '--noninteractive', '--standalone',
43
+ 'Run the application non-interactively; i.e. not requiring ' \
44
+ 'intervention from the user.') do
45
+ options[:interactive] = false
46
+ end
47
+
48
+ opts.on('-1', '--run-once',
49
+ 'Run the application loop once.') do
50
+ options[:once] = true
51
+ end
52
+
53
+ opts.on('-n', '--run-many',
54
+ 'Run the application loop continuously (default).') do
55
+ options[:once] = false
56
+ end
57
+
58
+ opts.on('-c', '--cooked', 'Run application in cooked mode.') do
59
+ options[:terminal_mode] = :cooked
60
+ end
61
+
62
+ opts.on('-r', '--raw', 'Run application in raw mode (default).') do
63
+ options[:terminal_mode] = :raw
64
+ end
65
+
66
+ opts.on('-d', '--debug', 'Run application with debugging on.') do
67
+ options[:debug] = true
68
+ end
69
+
70
+ opts.on('-D', '--trace', 'Run application with debugging on with ' \
71
+ 'method and event tracing (noisy!).') do
72
+ options[:debug] = true
73
+ options[:trace] = true
74
+ end
75
+
76
+ opts.on('-C', '--colour-mode [COLOURS]', Integer,
77
+ 'Run application in either `8`, `16`, `256` or `16777216` ' \
78
+ 'colour mode.') do |colours|
79
+ if [8, 16, 256, 16777216].include?(colours)
80
+ options[:colour_mode] = colours
81
+
82
+ else
83
+ options[:colour_mode] = 8
84
+
85
+ end
86
+ end
87
+ end
88
+ parser.parse!(args)
89
+
90
+ options
91
+ end
92
+
93
+ private
94
+
95
+ attr_reader :args
96
+
97
+ # Returns the options set via command-line arguments parsed by
98
+ # OptionParser, or an empty Hash if none were set or parsed.
99
+ #
100
+ # @api private
101
+ # @return [Hash]
102
+ def options
103
+ @_options ||= {}
104
+ end
105
+
106
+ end
107
+
108
+ end
109
+
110
+ end
@@ -1,74 +1,30 @@
1
1
  module Vedeu
2
2
 
3
- # Allows the customisation of Vedeu's behaviour through command-line
4
- # arguments.
3
+ # Allows the customisation of Vedeu's behaviour through the configuration API
4
+ # or command-line arguments.
5
+
6
+ # Provides access to Vedeu's configuration, which was set with sensible
7
+ # defaults (influenced by environment variables), overridden by client
8
+ # application settings (via the configuration API), or any command-line
9
+ # arguments provided.
5
10
  #
6
- # @api public
11
+ # @api private
7
12
  module Configuration
8
13
 
9
14
  extend self
10
15
 
11
- # Parses arguments passed on the command-line or via {Vedeu::Launcher} into
12
- # options used by Vedeu to affect certain behaviours.
16
+ # Configure Vedeu with sensible defaults. If the client application sets
17
+ # options, override the defaults with those, and if command-line arguments
18
+ # are provided at application invocation, override any options with the
19
+ # arguments provided.
13
20
  #
14
21
  # @param args [Array]
22
+ # @param block [Proc]
15
23
  # @return [Hash]
16
- def configure(args = [])
17
- parser = OptionParser.new do |opts|
18
- opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
19
-
20
- opts.on('-i', '--interactive',
21
- 'Run the application in interactive mode (default).') do
22
- options[:interactive] = true
23
- end
24
-
25
- opts.on('-I', '--noninteractive', '--standalone',
26
- 'Run the application non-interactively; i.e. not requiring ' \
27
- 'intervention from the user.') do
28
- options[:interactive] = false
29
- end
30
-
31
- opts.on('-1', '--run-once',
32
- 'Run the application loop once.') do
33
- options[:once] = true
34
- end
35
-
36
- opts.on('-n', '--run-many',
37
- 'Run the application loop continuously (default).') do
38
- options[:once] = false
39
- end
40
-
41
- opts.on('-c', '--cooked', 'Run application in cooked mode.') do
42
- options[:terminal_mode] = :cooked
43
- end
44
-
45
- opts.on('-r', '--raw', 'Run application in raw mode (default).') do
46
- options[:terminal_mode] = :raw
47
- end
48
-
49
- opts.on('-d', '--debug', 'Run application with debugging on.') do
50
- options[:debug] = true
51
- end
24
+ def configure(args = [], &block)
25
+ options.merge!(API.configure(&block)) if block_given?
52
26
 
53
- opts.on('-D', '--trace', 'Run application with debugging on with ' \
54
- 'method and event tracing (noisy!).') do
55
- options[:debug] = true
56
- options[:trace] = true
57
- end
58
-
59
- opts.on('-C', '--colour-mode [COLOURS]', Integer,
60
- 'Run application in either `8`, `16`, `256` or `16777216` ' \
61
- 'colour mode.') do |colours|
62
- if [8, 16, 256, 16777216].include?(colours)
63
- options[:colour_mode] = colours
64
-
65
- else
66
- options[:colour_mode] = 8
67
-
68
- end
69
- end
70
- end
71
- parser.parse!(args)
27
+ options.merge!(CLI.configure(args)) if args.any?
72
28
 
73
29
  options
74
30
  end
@@ -133,23 +89,37 @@ module Vedeu
133
89
  end
134
90
  alias_method :trace, :trace?
135
91
 
136
- # Returns all the options current configured.
92
+ # Resets all options to Vedeu defaults.
137
93
  #
138
94
  # @return [Hash]
139
- def options
140
- @options ||= defaults
95
+ def reset
96
+ @options = defaults
141
97
  end
142
98
 
143
- # Resets all options to Vedeu defaults.
99
+ # Vedeu's default system keys. Use {#system_keys}.
144
100
  #
101
+ # @api private
145
102
  # @return [Hash]
146
- def reset
147
- @options = defaults
103
+ def default_system_keys
104
+ {
105
+ exit: 'q',
106
+ focus_next: :tab,
107
+ focus_prev: :shift_tab,
108
+ mode_switch: :escape,
109
+ }
148
110
  end
149
111
 
150
112
  private
151
113
 
152
- # The Vedeu default options, which of course are influenced by enviroment
114
+ # Returns all the options current configured.
115
+ #
116
+ # @api private
117
+ # @return [Hash]
118
+ def options
119
+ @options ||= defaults
120
+ end
121
+
122
+ # The Vedeu default options, which of course are influenced by environment
153
123
  # variables also.
154
124
  #
155
125
  # @api private
@@ -161,28 +131,20 @@ module Vedeu
161
131
  interactive: true,
162
132
  once: false,
163
133
  system_keys: default_system_keys,
164
- terminal_mode: :raw, #cooked
134
+ terminal_mode: :raw,
165
135
  trace: detect_trace_mode,
166
136
  }
167
137
  end
168
138
 
169
- # Vedeu's system keys.
170
- def default_system_keys
171
- {
172
- exit: 'q',
173
- focus_next: :tab,
174
- focus_prev: :shift_tab,
175
- mode_switch: :escape,
176
- }
177
- end
178
-
179
- # Determine the terminal colour mode via enviroment variables, or be
180
- # optimistic and settle for 256 colours.
139
+ # Attempt to determine the terminal colour mode via environment variables,
140
+ # or be optimistic and settle for 256 colours.
181
141
  #
182
142
  # @api private
183
143
  # @return [Fixnum]
184
144
  # :nocov:
185
145
  def detect_colour_mode
146
+ return 16777216 if ENV['VEDEU_TESTMODE']
147
+
186
148
  if ENV['VEDEU_TERM']
187
149
  case ENV['VEDEU_TERM']
188
150
  when /-256color$/ then 256
@@ -204,23 +166,17 @@ module Vedeu
204
166
  end
205
167
  # :nocov:
206
168
 
207
- # Determine the debug mode via an enviroment variable.
169
+ # Determine the debug mode via an environment variable.
208
170
  #
209
171
  # @api private
210
172
  # @return [Boolean]
211
173
  # :nocov:
212
174
  def detect_debug_mode
213
- if ENV['VEDEU_DEBUG']
214
- case ENV['VEDEU_DEBUG']
215
- when 'true' then true
216
- when 'false' then false
217
- else false
218
- end
175
+ return false if ENV['VEDEU_TESTMODE']
219
176
 
220
- else
221
- false
177
+ return true if ENV['VEDEU_DEBUG']
222
178
 
223
- end
179
+ false
224
180
  end
225
181
  # :nocov:
226
182
 
@@ -230,17 +186,11 @@ module Vedeu
230
186
  # @return [Boolean]
231
187
  # :nocov:
232
188
  def detect_trace_mode
233
- if ENV['VEDEU_TRACE']
234
- case ENV['VEDEU_TRACE']
235
- when 'true' then true
236
- when 'false' then false
237
- else false
238
- end
189
+ return false if ENV['VEDEU_TESTMODE']
239
190
 
240
- else
241
- false
191
+ return true if ENV['VEDEU_TRACE']
242
192
 
243
- end
193
+ false
244
194
  end
245
195
  # :nocov:
246
196
 
@@ -48,7 +48,6 @@ module Vedeu
48
48
 
49
49
  private
50
50
 
51
- # @return [Array]
52
51
  attr_reader :argv
53
52
 
54
53
  end
@@ -11,13 +11,14 @@ module Vedeu
11
11
  # :black, :red, :green, :yellow, :blue, :magenta, :cyan, :white, :default.
12
12
  #
13
13
  # When a number between 0 and 255 is provided, Vedeu will use the terminal
14
- # colour corresponding with that colour. TODO: Create chart.
14
+ # colour corresponding with that colour.
15
15
  #
16
16
  # Finally, when provided a CSS/HTML colour string e.g. '#ff0000', Vedeu will
17
17
  # translate that to the 8-bit escape sequence or if you have a capable
18
18
  # terminal and the `VEDEU_TERM=xterm-truecolor` environment variable set,
19
19
  # a 24-bit representation.
20
20
  #
21
+ # @todo More documentation required (create a fancy chart!)
21
22
  # @api private
22
23
  class ColourTranslator
23
24
 
@@ -60,7 +61,6 @@ module Vedeu
60
61
 
61
62
  private
62
63
 
63
- # @return [Fixnum|NilClass|Symbol|String]
64
64
  attr_reader :colour
65
65
 
66
66
  # @api private
@@ -7,7 +7,6 @@ module Vedeu
7
7
  # @api private
8
8
  class Colour
9
9
 
10
- # @return [Hash]
11
10
  attr_reader :attributes
12
11
 
13
12
  # Returns a new instance of Colour.
@@ -1,6 +1,8 @@
1
1
  module Vedeu
2
2
 
3
3
  # A composition is a collection of interfaces.
4
+ #
5
+ # @api private
4
6
  class Composition
5
7
 
6
8
  attr_reader :attributes
@@ -44,14 +46,6 @@ module Vedeu
44
46
  {}
45
47
  end
46
48
 
47
- # Returns the complete escape sequence which this composition renders to.
48
- # This is used by {Vedeu::Terminal.output} to draw the view.
49
- #
50
- # @return [String]
51
- def to_s
52
- interfaces.map(&:to_s).join
53
- end
54
-
55
49
  private
56
50
 
57
51
  # The default values for a new instance of Composition.
@@ -0,0 +1,261 @@
1
+ module Vedeu
2
+
3
+ # Each interface has its own Cursor which maintains the position and
4
+ # visibility of the cursor within that interface.
5
+ #
6
+ # @api private
7
+ class Cursor
8
+
9
+ extend Forwardable
10
+
11
+ def_delegators :geometry, :top, :right, :bottom, :left
12
+
13
+ # Provides a new instance of Cursor.
14
+ #
15
+ # @param attributes [Hash] The stored attributes for a cursor.
16
+ # @return [Cursor]
17
+ def initialize(attributes = {})
18
+ @attributes = defaults.merge!(attributes)
19
+
20
+ @name = @attributes[:name]
21
+ @state = @attributes[:state]
22
+ @x = @attributes[:x]
23
+ @y = @attributes[:y]
24
+ end
25
+
26
+ # Returns an attribute hash for the current position and visibility of the
27
+ # cursor.
28
+ #
29
+ # @return [Hash]
30
+ def attributes
31
+ {
32
+ name: name,
33
+ state: state,
34
+ x: x,
35
+ y: y,
36
+ }
37
+ end
38
+ alias_method :refresh, :attributes
39
+
40
+ # Move the cursor up one row.
41
+ #
42
+ # @return [Cursor]
43
+ def move_up
44
+ unless y == top || y - 1 < top
45
+ @y -= 1
46
+ end
47
+
48
+ attributes
49
+ end
50
+
51
+ # Move the cursor down one row.
52
+ #
53
+ # @return [Cursor]
54
+ def move_down
55
+ unless y == bottom || y + 1 >= bottom
56
+ @y += 1
57
+ end
58
+
59
+ attributes
60
+ end
61
+
62
+ # Move the cursor left one column.
63
+ #
64
+ # @return [Cursor]
65
+ def move_left
66
+ unless x == left || x - 1 < left
67
+ @x -= 1
68
+ end
69
+
70
+ attributes
71
+ end
72
+
73
+ # Move the cursor right one column.
74
+ #
75
+ # @return [Cursor]
76
+ def move_right
77
+ unless x == right || x + 1 >= right
78
+ @x += 1
79
+ end
80
+
81
+ attributes
82
+ end
83
+
84
+ # Make the cursor visible if it is not already.
85
+ #
86
+ # @return [Symbol]
87
+ def show
88
+ @state = :show
89
+
90
+ attributes
91
+ end
92
+
93
+ # Make the cursor invisible if it is not already.
94
+ #
95
+ # @return [Symbol]
96
+ def hide
97
+ @state = :hide
98
+
99
+ attributes
100
+ end
101
+
102
+ # Toggle the visibility of the cursor.
103
+ #
104
+ # @return [Symbol]
105
+ def toggle
106
+ if visible?
107
+ @state = :hide
108
+
109
+ else
110
+ @state = :show
111
+
112
+ end
113
+
114
+ attributes
115
+ end
116
+
117
+ # Returns an escape sequence to position the cursor and set its visibility.
118
+ # When passed a block, will position the cursor, yield and return the
119
+ # original position.
120
+ #
121
+ # @param block [Proc]
122
+ # @return [String]
123
+ def to_s(&block)
124
+ if block_given?
125
+ [ sequence, yield, sequence ].join
126
+
127
+ else
128
+ sequence
129
+
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ attr_reader :name, :state
136
+
137
+ # Returns the escape sequence to position the cursor and set its visibility.
138
+ #
139
+ # @api private
140
+ # @return [String]
141
+ def sequence
142
+ [ position, visibility ].join
143
+ end
144
+
145
+ # Returns the escape sequence to position the cursor.
146
+ #
147
+ # @api private
148
+ # @return [String]
149
+ def position
150
+ ["\e[", y, ';', x, 'H'].join
151
+ end
152
+
153
+ # Returns the escape sequence for setting the visibility of the cursor.
154
+ #
155
+ # @api private
156
+ # @return [String]
157
+ def visibility
158
+ return Esc.string('show_cursor') if visible?
159
+
160
+ Esc.string('hide_cursor')
161
+ end
162
+
163
+ # Return a boolean indicating the visibility of the cursor, invisible if
164
+ # the state is not defined.
165
+ #
166
+ # @api private
167
+ # @return [Boolean]
168
+ def visible?
169
+ return false unless states.include?(state)
170
+ return false if state == :hide
171
+
172
+ true
173
+ end
174
+
175
+ # Returns the y coordinate of the cursor, unless out of range, in which case
176
+ # sets y to the first row (top) of the interface.
177
+ #
178
+ # @api private
179
+ # @return [Fixnum]
180
+ def y
181
+ if y_out_of_range?
182
+ @y = top
183
+
184
+ else
185
+ @y
186
+
187
+ end
188
+ end
189
+
190
+ # Returns the x coordinate of the cursor, unless out of range, in which case
191
+ # sets x to the first column (left) of the interface.
192
+ #
193
+ # @api private
194
+ # @return [Fixnum]
195
+ def x
196
+ if x_out_of_range?
197
+ @x = left
198
+
199
+ else
200
+ @x
201
+
202
+ end
203
+ end
204
+
205
+ # Returns a boolean indicating whether the previous y coordinate is still
206
+ # inside the interface or terminal.
207
+ #
208
+ # @api private
209
+ # @return [Boolean]
210
+ def y_out_of_range?
211
+ @y < top || @y > bottom
212
+ end
213
+
214
+ # Returns a boolean indicating whether the previous x coordinate is still
215
+ # inside the interface or terminal.
216
+ #
217
+ # @api private
218
+ # @return [Boolean]
219
+ def x_out_of_range?
220
+ @x < left || @x > right
221
+ end
222
+
223
+ # Returns the position and size of the interface.
224
+ #
225
+ # @api private
226
+ # @return [Geometry]
227
+ def geometry
228
+ @geometry ||= Vedeu::Geometry.new(interface[:geometry])
229
+ end
230
+
231
+ # Returns the attributes of a named interface.
232
+ #
233
+ # @api private
234
+ # @return [Hash]
235
+ def interface
236
+ Vedeu::Interfaces.find(name)
237
+ end
238
+
239
+ # The valid visibility states for the cursor.
240
+ #
241
+ # @api private
242
+ # @return [Array]
243
+ def states
244
+ [:show, :hide]
245
+ end
246
+
247
+ # The default values for a new instance of Cursor.
248
+ #
249
+ # @api private
250
+ # @return [Hash]
251
+ def defaults
252
+ {
253
+ name: '',
254
+ x: 1,
255
+ y: 1,
256
+ state: :hide,
257
+ }
258
+ end
259
+ end
260
+
261
+ end