caboose-cms 0.5.28 → 0.5.30
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/Rakefile +10 -25
- data/app/assets/javascripts/caboose/admin_page_edit_content.js +68 -43
- data/app/assets/stylesheets/caboose/admin_page_edit_content.css +18 -9
- data/app/controllers/caboose/blocks_controller.rb +24 -17
- data/app/models/caboose/block.rb +54 -19
- data/app/models/caboose/user.rb +2 -0
- data/app/views/caboose/pages/admin_edit_content.html.erb +12 -9
- data/lib/caboose.rb +4 -0
- data/lib/caboose/engine.rb +10 -1
- data/lib/caboose/version.rb +1 -1
- data/lib/tasks/caboose.rake +6 -22
- data/lib/tasks/caboose_sync.rake +112 -0
- data/spec/factories/caboose_blocks.rb +20 -0
- data/spec/factories/caboose_users.rb +19 -0
- data/spec/models/block_spec.rb +114 -0
- data/spec/models/user_spec.rb +9 -0
- metadata +109 -68
- data/test/caboose_test.rb +0 -7
- data/test/dummy/README.rdoc +0 -261
- data/test/dummy/Rakefile +0 -7
- data/test/dummy/app/assets/javascripts/application.js +0 -15
- data/test/dummy/app/assets/stylesheets/application.css +0 -13
- data/test/dummy/app/controllers/application_controller.rb +0 -3
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -59
- data/test/dummy/config/boot.rb +0 -10
- data/test/dummy/config/database.yml +0 -25
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -37
- data/test/dummy/config/environments/production.rb +0 -67
- data/test/dummy/config/environments/test.rb +0 -37
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/inflections.rb +0 -15
- data/test/dummy/config/initializers/mime_types.rb +0 -5
- data/test/dummy/config/initializers/secret_token.rb +0 -7
- data/test/dummy/config/initializers/session_store.rb +0 -8
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -5
- data/test/dummy/config/routes.rb +0 -4
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +0 -25
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -25
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +0 -6
- data/test/integration/navigation_test.rb +0 -10
- data/test/test_helper.rb +0 -15
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YmJiNzg3OTk5YWQxNWNmM2NhMTU0ZTMzN2FjNDMxODk4NjgwZTVkMw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjA0MWM2Mjc5YzNkOWE0MjEyYTFiMzBkYzc5MzliOTYxNjQ1Y2I3ZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MmE1ZWFkYzk1NjM1NDYxY2MzODJkMmMwNzJiOTg3NzIzZDk1NDBiNGQwZjg1
|
10
|
+
ODI1NjMyOWZkZDVkYTMwMjA1NWZhZWVkMTE1NDNhY2QwOGJiYTBmYTQzMDli
|
11
|
+
ZjFhMjgyMGZlYmMwOTQyY2Q3YTk3MjYwNjlmYTQ0MWYwNDRjZmQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MjQwNzc0M2E3NjVlMTZiMDQ1NmVhZjIyOTRlZjc3NjQzZjY2ODY5NGZhZDc2
|
14
|
+
ZmE3ZjA3YzBkOGFmZThjNTYwNTZjNTQ5YTVmODZmZGU0ZTYyMmE0ZGVlODBk
|
15
|
+
ODQ4YmVlOTY0YWQ1ZjYzNzc3YWY3NzQzZGZhZjgzOWJlMDdmYzU=
|
data/Rakefile
CHANGED
@@ -1,38 +1,23 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
begin
|
3
|
-
require 'bundler/setup'
|
3
|
+
require 'bundler/setup'; Bundler.setup(:default, :development)
|
4
4
|
rescue LoadError
|
5
5
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
6
|
end
|
7
|
-
begin
|
8
|
-
require 'rdoc/task'
|
9
|
-
rescue LoadError
|
10
|
-
require 'rdoc/rdoc'
|
11
|
-
require 'rake/rdoctask'
|
12
|
-
RDoc::Task = Rake::RDocTask
|
13
|
-
end
|
14
7
|
|
15
|
-
|
16
|
-
rdoc.rdoc_dir = 'rdoc'
|
17
|
-
rdoc.title = 'Caboose'
|
18
|
-
rdoc.options << '--line-numbers'
|
19
|
-
rdoc.rdoc_files.include('README.rdoc')
|
20
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
-
end
|
22
|
-
|
23
|
-
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
8
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
24
9
|
load 'rails/tasks/engine.rake'
|
25
10
|
|
26
11
|
Bundler::GemHelper.install_tasks
|
27
12
|
|
28
|
-
|
13
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
29
14
|
|
30
|
-
|
31
|
-
|
32
|
-
t.libs << 'test'
|
33
|
-
t.pattern = 'test/**/*_test.rb'
|
34
|
-
t.verbose = false
|
35
|
-
end
|
15
|
+
require 'rspec/core'
|
16
|
+
require 'rspec/core/rake_task'
|
36
17
|
|
18
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
19
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
37
20
|
|
38
|
-
task :default => :test
|
21
|
+
#task :default => :test
|
22
|
+
task :default => :spec
|
23
|
+
task :test => :spec
|
@@ -19,45 +19,45 @@ PageContentController.prototype = {
|
|
19
19
|
|
20
20
|
sortable_blocks: function()
|
21
21
|
{
|
22
|
-
var that = this;
|
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
|
-
});
|
22
|
+
//var that = this;
|
23
|
+
//$('.sortable').sortable({
|
24
|
+
// //hoverClass: "ui-state-active",
|
25
|
+
// placeholder: 'sortable-placeholder',
|
26
|
+
// forcePlaceholderSize: true,
|
27
|
+
// handle: '.sort_handle',
|
28
|
+
// receive: function(e, ui) {
|
29
|
+
// that.new_block_type_id = ui.item.attr('id').replace('new_block_', '');
|
30
|
+
// },
|
31
|
+
// update: function(e, ui) {
|
32
|
+
// if (that.new_block_type_id)
|
33
|
+
// {
|
34
|
+
// $.ajax({
|
35
|
+
// url: '/admin/pages/' + that.page_id + '/blocks',
|
36
|
+
// type: 'post',
|
37
|
+
// data: { block_type_id: that.new_block_type_id, index: ui.item.index() },
|
38
|
+
// success: function(resp) { that.render_blocks(function() { that.edit_block(resp.block.id); }); }
|
39
|
+
// });
|
40
|
+
// that.new_block_type_id = false;
|
41
|
+
// }
|
42
|
+
// else
|
43
|
+
// {
|
44
|
+
// var ids = [];
|
45
|
+
// $.each($(e.target).children(), function(i, el) {
|
46
|
+
// var id = $(el).attr('id');
|
47
|
+
// if (id && id.substr(0, 6) == 'block_') ids.push(id.substr(6));
|
48
|
+
// });
|
49
|
+
//
|
50
|
+
// $.ajax({
|
51
|
+
// url: '/admin/pages/' + that.page_id + '/block-order',
|
52
|
+
// type: 'put',
|
53
|
+
// data: {
|
54
|
+
// block_ids: ids,
|
55
|
+
// },
|
56
|
+
// success: function(resp) {}
|
57
|
+
// });
|
58
|
+
// }
|
59
|
+
// }
|
60
|
+
//});
|
61
61
|
},
|
62
62
|
|
63
63
|
draggable_blocks: function()
|
@@ -129,6 +129,30 @@ PageContentController.prototype = {
|
|
129
129
|
}
|
130
130
|
that.render_blocks();
|
131
131
|
},
|
132
|
+
|
133
|
+
move_block_up: function(block_id)
|
134
|
+
{
|
135
|
+
var that = this;
|
136
|
+
$.ajax({
|
137
|
+
url: '/admin/pages/' + this.page_id + '/blocks/' + block_id + '/move-up',
|
138
|
+
type: 'put',
|
139
|
+
success: function(resp) {
|
140
|
+
if (resp.success) that.render_blocks();
|
141
|
+
}
|
142
|
+
});
|
143
|
+
},
|
144
|
+
|
145
|
+
move_block_down: function(block_id)
|
146
|
+
{
|
147
|
+
var that = this;
|
148
|
+
$.ajax({
|
149
|
+
url: '/admin/pages/' + this.page_id + '/blocks/' + block_id + '/move-down',
|
150
|
+
type: 'put',
|
151
|
+
success: function(resp) {
|
152
|
+
if (resp.success) that.render_blocks();
|
153
|
+
}
|
154
|
+
});
|
155
|
+
},
|
132
156
|
|
133
157
|
/*****************************************************************************
|
134
158
|
Block Rendering
|
@@ -148,7 +172,7 @@ PageContentController.prototype = {
|
|
148
172
|
that.selected_block_ids = [];
|
149
173
|
}
|
150
174
|
});
|
151
|
-
},
|
175
|
+
},
|
152
176
|
|
153
177
|
set_clickable: function()
|
154
178
|
{
|
@@ -168,9 +192,10 @@ PageContentController.prototype = {
|
|
168
192
|
var that = this;
|
169
193
|
|
170
194
|
$('#block_' + b.id)
|
171
|
-
.prepend($('<a/>').attr('id', 'block_' + b.id + '_select_handle'
|
172
|
-
.prepend($('<a/>').attr('id', 'block_' + b.id + '
|
173
|
-
.prepend($('<a/>').attr('id', 'block_' + b.id + '
|
195
|
+
.prepend($('<a/>').attr('id', 'block_' + b.id + '_select_handle' ).addClass('select_handle' ).append($('<span/>').addClass('ui-icon ui-icon-check' )).click(function(e) { e.preventDefault(); e.stopPropagation(); that.select_block(b.id); }))
|
196
|
+
.prepend($('<a/>').attr('id', 'block_' + b.id + '_move_up_handle' ).addClass('move_up_handle' ).append($('<span/>').addClass('ui-icon ui-icon-arrow-1-n' )).click(function(e) { e.preventDefault(); e.stopPropagation(); that.move_block_up(b.id); }))
|
197
|
+
.prepend($('<a/>').attr('id', 'block_' + b.id + '_move_down_handle' ).addClass('move_down_handle' ).append($('<span/>').addClass('ui-icon ui-icon-arrow-1-s' )).click(function(e) { e.preventDefault(); e.stopPropagation(); that.move_block_down(b.id); }))
|
198
|
+
.prepend($('<a/>').attr('id', 'block_' + b.id + '_delete_handle' ).addClass('delete_handle' ).append($('<span/>').addClass('ui-icon ui-icon-close' )).click(function(e) { e.preventDefault(); e.stopPropagation(); that.delete_block(b.id); }));
|
174
199
|
|
175
200
|
if (parent_allows_child_blocks && (!b.name || b.name.length == 0))
|
176
201
|
{
|
@@ -11,15 +11,24 @@
|
|
11
11
|
#blocks .ui-selected { background: #fff799; }
|
12
12
|
#blocks .sortable-placeholder { height: 40px; background: #fff799; border: #ccc 1px solid; }
|
13
13
|
|
14
|
-
|
15
|
-
#blocks li .sort_handle
|
16
|
-
#blocks li .
|
17
|
-
#blocks li:hover .
|
18
|
-
|
19
|
-
|
20
|
-
#blocks li
|
21
|
-
#blocks li
|
22
|
-
#blocks li
|
14
|
+
/*
|
15
|
+
#blocks li .sort_handle { display: none; }
|
16
|
+
#blocks li:hover .sort_handle { position: absolute; top: 0; right: 22px; display: block; width: 20px; height: 20px; background-color: #ccc; }
|
17
|
+
#blocks li:hover .sort_handle span { margin: 2px 1px; }
|
18
|
+
*/
|
19
|
+
|
20
|
+
#blocks li .select_handle { display: none; }
|
21
|
+
#blocks li .move_up_handle { display: none; }
|
22
|
+
#blocks li .move_down_handle { display: none; }
|
23
|
+
#blocks li .delete_handle { display: none; }
|
24
|
+
#blocks li:hover .select_handle { position: absolute; top: 0; right: 66px; display: block; width: 20px; height: 20px; background-color: #ccc; }
|
25
|
+
#blocks li:hover .move_up_handle { position: absolute; top: 0; right: 44px; display: block; width: 20px; height: 20px; background-color: #ccc; }
|
26
|
+
#blocks li:hover .move_down_handle { position: absolute; top: 0; right: 22px; display: block; width: 20px; height: 20px; background-color: #ccc; }
|
27
|
+
#blocks li:hover .delete_handle { position: absolute; top: 0; right: 0px; display: block; width: 20px; height: 20px; background-color: #ccc; }
|
28
|
+
#blocks li:hover .select_handle span { margin: 2px 1px; }
|
29
|
+
#blocks li:hover .move_up_handle span { margin: 2px 1px; }
|
30
|
+
#blocks li:hover .move_down_handle span { margin: 2px 1px; }
|
31
|
+
#blocks li:hover .delete_handle span { margin: 2px 1px; }
|
23
32
|
|
24
33
|
.sortable_placeholder {
|
25
34
|
background: #ccc;
|
@@ -353,27 +353,34 @@ module Caboose
|
|
353
353
|
|
354
354
|
resp = StdClass.new
|
355
355
|
b = Block.find(params[:id])
|
356
|
-
|
356
|
+
changed = b.move_up
|
357
|
+
if !changed
|
357
358
|
resp.error = "The block is already at the top."
|
358
359
|
else
|
359
|
-
b2 = nil
|
360
|
-
|
361
|
-
new_sort_order = b.sort_order - 1
|
362
|
-
while new_sort_order > 0 do
|
363
|
-
b2 = Block.where("parent_id = ? and sort_order = ?", b.parent_id, new_sort_order).first
|
364
|
-
break if b2
|
365
|
-
new_sort_order = new_sort_order - 1
|
366
|
-
end
|
367
|
-
if b2
|
368
|
-
b2.sort_order = new_sort_order + 1
|
369
|
-
b2.save
|
370
|
-
else
|
371
|
-
new_sort_order = 1
|
372
|
-
end
|
373
|
-
b.sort_order = new_sort_order
|
374
|
-
b.save
|
375
360
|
resp.success = "The block has been moved up successfully."
|
376
361
|
end
|
362
|
+
|
363
|
+
#if b.sort_order == 0
|
364
|
+
# resp.error = "The block is already at the top."
|
365
|
+
#else
|
366
|
+
# b2 = nil
|
367
|
+
#
|
368
|
+
# new_sort_order = b.sort_order - 1
|
369
|
+
# while new_sort_order > 0 do
|
370
|
+
# b2 = Block.where("parent_id = ? and sort_order = ?", b.parent_id, new_sort_order).first
|
371
|
+
# break if b2
|
372
|
+
# new_sort_order = new_sort_order - 1
|
373
|
+
# end
|
374
|
+
# if b2
|
375
|
+
# b2.sort_order = new_sort_order + 1
|
376
|
+
# b2.save
|
377
|
+
# else
|
378
|
+
# new_sort_order = 1
|
379
|
+
# end
|
380
|
+
# b.sort_order = new_sort_order
|
381
|
+
# b.save
|
382
|
+
# resp.success = "The block has been moved up successfully."
|
383
|
+
#end
|
377
384
|
|
378
385
|
render :json => resp
|
379
386
|
end
|
data/app/models/caboose/block.rb
CHANGED
@@ -29,29 +29,24 @@ class Caboose::Block < ActiveRecord::Base
|
|
29
29
|
:name,
|
30
30
|
:value
|
31
31
|
|
32
|
-
after_initialize
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
after_initialize :caste_value
|
33
|
+
before_save :caste_value
|
34
|
+
|
35
|
+
def caste_value
|
36
|
+
if self.block_type.nil?
|
36
37
|
bt = Caboose::BlockType.where(:field_type => 'text').first
|
37
|
-
|
38
|
+
if bt.nil?
|
39
|
+
bt = Caboose::BlockType.create(:name => 'text', :description => 'Text', :field_type => 'text', :default => '', :width => 800, :height => 400, :fixed_placeholder => false)
|
40
|
+
end
|
41
|
+
self.block_type_id = bt.id
|
38
42
|
end
|
39
|
-
if
|
40
|
-
|
41
|
-
b.save
|
43
|
+
if self.block_type.field_type.nil?
|
44
|
+
self.block_type.field_type = 'text'
|
42
45
|
end
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
before_save :caste_value
|
49
|
-
def caste_value
|
50
|
-
case self.block_type.field_type
|
46
|
+
v = self.value
|
47
|
+
case self.block_type.field_type
|
51
48
|
when 'checkbox'
|
52
|
-
|
53
|
-
else self.value = (self.value == 1 || self.value == '1' || self.value == true ? 1 : 0)
|
54
|
-
end
|
49
|
+
self.value = v ? (v == 1 || v == '1' || v == true ? 1 : 0) : 0
|
55
50
|
end
|
56
51
|
end
|
57
52
|
|
@@ -359,5 +354,45 @@ class Caboose::Block < ActiveRecord::Base
|
|
359
354
|
end
|
360
355
|
return current_value.join('|')
|
361
356
|
end
|
357
|
+
|
358
|
+
# Move a block up
|
359
|
+
def move_up
|
360
|
+
siblings = Caboose::Block.where(:parent_id => self.parent_id).reorder(:sort_order).all
|
361
|
+
siblings.each_with_index do |b2, i|
|
362
|
+
b2.sort_order = i
|
363
|
+
b2.save
|
364
|
+
end
|
365
|
+
changed = false
|
366
|
+
siblings.each_with_index do |b2, i|
|
367
|
+
if i > 0 && b2.id == self.id
|
368
|
+
siblings[i-1].sort_order = i
|
369
|
+
siblings[i-1].save
|
370
|
+
b2.sort_order = i - 1
|
371
|
+
b2.save
|
372
|
+
changed = true
|
373
|
+
end
|
374
|
+
end
|
375
|
+
return changed
|
376
|
+
end
|
377
|
+
|
378
|
+
# Move a block down
|
379
|
+
def move_down
|
380
|
+
siblings = Caboose::Block.where(:parent_id => self.parent_id).reorder(:sort_order).all
|
381
|
+
siblings.each_with_index do |b2, i|
|
382
|
+
b2.sort_order = i
|
383
|
+
b2.save
|
384
|
+
end
|
385
|
+
changed = false
|
386
|
+
siblings.each_with_index do |b2, i|
|
387
|
+
if i < (siblings.count-1) && b2.id == self.id
|
388
|
+
siblings[i+1].sort_order = i
|
389
|
+
siblings[i+1].save
|
390
|
+
b2.sort_order = i + 1
|
391
|
+
b2.save
|
392
|
+
changed = true
|
393
|
+
end
|
394
|
+
end
|
395
|
+
return changed
|
396
|
+
end
|
362
397
|
|
363
398
|
end
|
data/app/models/caboose/user.rb
CHANGED
@@ -14,6 +14,8 @@ class Caboose::User < ActiveRecord::Base
|
|
14
14
|
do_not_validate_attachment_file_type :image
|
15
15
|
attr_accessible :id, :email, :first_name, :last_name, :username, :token, :password, :phone, :timezone
|
16
16
|
|
17
|
+
validates :email, :presence => true
|
18
|
+
|
17
19
|
ADMIN_USER_ID = 1
|
18
20
|
LOGGED_OUT_USER_ID = 2
|
19
21
|
|
@@ -23,15 +23,18 @@
|
|
23
23
|
padding: 16px 10px;
|
24
24
|
}
|
25
25
|
.block_over { background: #e3e3e3; }
|
26
|
-
.select_handle
|
27
|
-
.
|
28
|
-
.
|
29
|
-
.
|
30
|
-
.block_over > .
|
31
|
-
.block_over > .
|
32
|
-
.block_over > .
|
33
|
-
.block_over > .
|
34
|
-
.block_over > .
|
26
|
+
.select_handle { display: none; }
|
27
|
+
.move_up_handle { display: none; }
|
28
|
+
.move_down_handle { display: none; }
|
29
|
+
.delete_handle { display: none; }
|
30
|
+
.block_over > .select_handle { display: block; position: relative; }
|
31
|
+
.block_over > .move_up_handle { display: block; position: relative; }
|
32
|
+
.block_over > .move_down_handle { display: block; position: relative; }
|
33
|
+
.block_over > .delete_handle { display: block; position: relative; }
|
34
|
+
.block_over > .select_handle span { position: absolute; top: 0; right: 54px; width: 18px; height: 18px; background-color: #fff; border: #ccc 1px solid; }
|
35
|
+
.block_over > .move_up_handle span { position: absolute; top: 0; right: 36px; width: 18px; height: 18px; background-color: #fff; border: #ccc 1px solid; }
|
36
|
+
.block_over > .move_down_handle span { position: absolute; top: 0; right: 18px; width: 18px; height: 18px; background-color: #fff; border: #ccc 1px solid; }
|
37
|
+
.block_over > .delete_handle span { position: absolute; top: 0; right: 0px; width: 18px; height: 18px; background-color: #fff; border: #ccc 1px solid; }
|
35
38
|
|
36
39
|
.selected { background: #fff799; }
|
37
40
|
|
data/lib/caboose.rb
CHANGED
data/lib/caboose/engine.rb
CHANGED
@@ -209,7 +209,16 @@ module Caboose
|
|
209
209
|
'*/css/application.js'
|
210
210
|
|
211
211
|
]
|
212
|
-
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Configure rspec
|
215
|
+
config.generators do |g|
|
216
|
+
g.test_framework :rspec, :fixture => false
|
217
|
+
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
|
218
|
+
g.assets false
|
219
|
+
g.helper false
|
220
|
+
end
|
221
|
+
|
213
222
|
end
|
214
223
|
end
|
215
224
|
|