active-list 5.0.1 → 6.0.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.
- data/README.rdoc +2 -40
- metadata +19 -130
- checksums.yaml +0 -7
- data/VERSION +0 -1
- data/app/assets/images/active-list.png +0 -0
- data/app/assets/images/active-list.svg +0 -415
- data/app/assets/javascripts/active_list.jquery.js +0 -136
- data/app/assets/stylesheets/active_list.css.scss +0 -7
- data/app/assets/stylesheets/active_list/background.scss +0 -39
- data/app/assets/stylesheets/active_list/minimal.scss +0 -87
- data/app/assets/stylesheets/active_list/theme.scss +0 -189
- data/lib/active-list.rb +0 -1
- data/lib/active_list.rb +0 -43
- data/lib/active_list/action_pack.rb +0 -46
- data/lib/active_list/definition.rb +0 -17
- data/lib/active_list/definition/abstract_column.rb +0 -54
- data/lib/active_list/definition/action_column.rb +0 -76
- data/lib/active_list/definition/association_column.rb +0 -80
- data/lib/active_list/definition/attribute_column.rb +0 -58
- data/lib/active_list/definition/check_box_column.rb +0 -17
- data/lib/active_list/definition/data_column.rb +0 -88
- data/lib/active_list/definition/empty_column.rb +0 -10
- data/lib/active_list/definition/field_column.rb +0 -20
- data/lib/active_list/definition/status_column.rb +0 -10
- data/lib/active_list/definition/table.rb +0 -159
- data/lib/active_list/definition/text_field_column.rb +0 -10
- data/lib/active_list/exporters.rb +0 -28
- data/lib/active_list/exporters/abstract_exporter.rb +0 -55
- data/lib/active_list/exporters/csv_exporter.rb +0 -32
- data/lib/active_list/exporters/excel_csv_exporter.rb +0 -38
- data/lib/active_list/exporters/open_document_spreadsheet_exporter.rb +0 -82
- data/lib/active_list/generator.rb +0 -122
- data/lib/active_list/generator/finder.rb +0 -150
- data/lib/active_list/helpers.rb +0 -33
- data/lib/active_list/rails/engine.rb +0 -13
- data/lib/active_list/renderers.rb +0 -25
- data/lib/active_list/renderers/abstract_renderer.rb +0 -29
- data/lib/active_list/renderers/simple_renderer.rb +0 -356
- data/locales/eng.yml +0 -25
- data/locales/fra.yml +0 -25
data/lib/active-list.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'active_list'
|
data/lib/active_list.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'csv'
|
2
|
-
require 'action_dispatch'
|
3
|
-
require 'rails'
|
4
|
-
require 'compass-rails'
|
5
|
-
|
6
|
-
module ActiveList
|
7
|
-
|
8
|
-
CSV = ::CSV
|
9
|
-
|
10
|
-
# Build and returns a short UID
|
11
|
-
def self.new_uid
|
12
|
-
@@last_uid ||= 0
|
13
|
-
uid = @@last_uid.to_s(36).to_sym
|
14
|
-
@@last_uid += 1
|
15
|
-
return uid
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.version
|
19
|
-
v = nil
|
20
|
-
File.open(File.join(File.dirname(__FILE__), "..", "VERSION")) {|f| v = f.read.strip}
|
21
|
-
return v
|
22
|
-
end
|
23
|
-
|
24
|
-
VERSION = self.version.freeze
|
25
|
-
|
26
|
-
autoload :Helpers, 'active_list/helpers'
|
27
|
-
autoload :Definition, 'active_list/definition'
|
28
|
-
autoload :Renderers, 'active_list/renderers'
|
29
|
-
autoload :Exporters, 'active_list/exporters'
|
30
|
-
autoload :Generator, 'active_list/generator'
|
31
|
-
autoload :ActionPack, 'active_list/action_pack'
|
32
|
-
end
|
33
|
-
|
34
|
-
unless "".respond_to? :dig
|
35
|
-
class ::String
|
36
|
-
def dig(depth = 1)
|
37
|
-
return self.strip.gsub(/^/, ' ' * depth) + "\n"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
require 'active_list/rails/engine'
|
@@ -1,46 +0,0 @@
|
|
1
|
-
module ActiveList
|
2
|
-
|
3
|
-
module ActionPack
|
4
|
-
|
5
|
-
module ActionController
|
6
|
-
|
7
|
-
def self.included(base) #:nodoc:
|
8
|
-
base.extend(ClassMethods)
|
9
|
-
end
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
|
13
|
-
# Permits to define and generate methods to manage dynamic
|
14
|
-
# table ActiveList
|
15
|
-
def list(*args, &block)
|
16
|
-
options = args.extract_options!
|
17
|
-
options[:controller] = self
|
18
|
-
args << options
|
19
|
-
generator = ActiveList::Generator.new(*args, &block)
|
20
|
-
class_eval(generator.controller_method_code, __FILE__, __LINE__)
|
21
|
-
ActionView::Base.send(:class_eval, generator.view_method_code, __FILE__, __LINE__)
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
module ViewsHelper
|
29
|
-
|
30
|
-
# Calls the generated view helper
|
31
|
-
def list(*args, &block)
|
32
|
-
options = args.extract_options!
|
33
|
-
name = args.shift
|
34
|
-
kontroller = self.controller.class
|
35
|
-
begin
|
36
|
-
helper_method = "_#{kontroller.controller_name}_#{__method__}_#{name || kontroller.controller_name}_tag".to_sym
|
37
|
-
kontroller = kontroller.superclass
|
38
|
-
end until self.respond_to?(helper_method)
|
39
|
-
return self.send(helper_method, &block)
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module ActiveList
|
2
|
-
|
3
|
-
module Definition
|
4
|
-
autoload :AbstractColumn, 'active_list/definition/abstract_column'
|
5
|
-
autoload :ActionColumn, 'active_list/definition/action_column'
|
6
|
-
autoload :AssociationColumn, 'active_list/definition/association_column'
|
7
|
-
autoload :AttributeColumn, 'active_list/definition/attribute_column'
|
8
|
-
autoload :CheckBoxColumn, 'active_list/definition/check_box_column'
|
9
|
-
autoload :DataColumn, 'active_list/definition/data_column'
|
10
|
-
autoload :EmptyColumn, 'active_list/definition/empty_column'
|
11
|
-
autoload :FieldColumn, 'active_list/definition/field_column'
|
12
|
-
autoload :StatusColumn, 'active_list/definition/status_column'
|
13
|
-
autoload :Table, 'active_list/definition/table'
|
14
|
-
autoload :TextFieldColumn, 'active_list/definition/text_field_column'
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module ActiveList
|
2
|
-
|
3
|
-
module Definition
|
4
|
-
|
5
|
-
class AbstractColumn
|
6
|
-
attr_reader :table, :name, :id, :options
|
7
|
-
|
8
|
-
def initialize(table, name, options = {})
|
9
|
-
@table = table
|
10
|
-
@name = name.to_sym
|
11
|
-
@options = options
|
12
|
-
@hidden = !!@options.delete(:hidden)
|
13
|
-
@id = ActiveList.new_uid
|
14
|
-
end
|
15
|
-
|
16
|
-
def header_code
|
17
|
-
raise NotImplementedError, "#{self.class.name}#header_code is not implemented."
|
18
|
-
end
|
19
|
-
|
20
|
-
def hidden?
|
21
|
-
@hidden
|
22
|
-
end
|
23
|
-
|
24
|
-
def sortable?
|
25
|
-
false
|
26
|
-
end
|
27
|
-
|
28
|
-
def exportable?
|
29
|
-
false
|
30
|
-
end
|
31
|
-
|
32
|
-
# Unique identifier of the column in the application
|
33
|
-
def unique_id
|
34
|
-
"#{@table.name}-#{@name}"
|
35
|
-
end
|
36
|
-
|
37
|
-
# Uncommon but simple identifier for CSS class uses
|
38
|
-
def short_id
|
39
|
-
@id
|
40
|
-
end
|
41
|
-
|
42
|
-
alias :sort_id :name
|
43
|
-
|
44
|
-
def check_options!(options, *keys)
|
45
|
-
for key in options.keys
|
46
|
-
raise ArgumentError, "Key :#{key} is unexpected. (Expecting: #{keys.to_sentence})"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
@@ -1,76 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
module ActiveList
|
3
|
-
|
4
|
-
module Definition
|
5
|
-
|
6
|
-
class ActionColumn < AbstractColumn
|
7
|
-
include ActiveList::Helpers
|
8
|
-
|
9
|
-
def header_code
|
10
|
-
"''".c
|
11
|
-
end
|
12
|
-
|
13
|
-
def operation(record = 'record_of_the_death')
|
14
|
-
@options[:method] = :delete if @name.to_s == "destroy" and !@options.has_key?(:method)
|
15
|
-
@options[:confirm] ||= :are_you_sure_you_want_to_delete if @name.to_s == "destroy" and !@options.has_key?(:confirm)
|
16
|
-
@options[:if] ||= :destroyable? if @name.to_s == "destroy"
|
17
|
-
@options[:if] ||= :editable? if @name.to_s == "edit"
|
18
|
-
@options[:confirm] = :are_you_sure if @options[:confirm].is_a?(TrueClass)
|
19
|
-
link_options = ""
|
20
|
-
if @options[:confirm]
|
21
|
-
link_options << ", 'data-confirm' => #{(@options[:confirm]).inspect}.t(scope: 'labels')"
|
22
|
-
end
|
23
|
-
if @options['data-method'] or @options[:method]
|
24
|
-
link_options << ", :method => h('#{(@options['data-method']||@options[:method])}')"
|
25
|
-
end
|
26
|
-
action = @name
|
27
|
-
format = @options[:format] ? ", :format => '#{@options[:format]}'" : ""
|
28
|
-
if @options[:remote]
|
29
|
-
raise StandardError, "Sure to use :remote ?"
|
30
|
-
# remote_options = @options.dup
|
31
|
-
# remote_options['data-confirm'] = "#{@options[:confirm].inspect}.tl".c unless @options[:confirm].nil?
|
32
|
-
# remote_options.delete :remote
|
33
|
-
# remote_options.delete :image
|
34
|
-
# remote_options = remote_options.inspect.to_s
|
35
|
-
# remote_options = remote_options[1..-2]
|
36
|
-
# code = "link_to_remote(#{image}"
|
37
|
-
# code += ", {url: {action: "+@name.to_s+", id: "+record+".id"+format+"}"
|
38
|
-
# code += ", "+remote_options+"}"
|
39
|
-
# code += ", {title: #{action.inspect}.tl}"
|
40
|
-
# code += ")"
|
41
|
-
elsif @options[:actions]
|
42
|
-
unless @options[:actions].is_a? Hash
|
43
|
-
raise StandardError, "options[:actions] have to be a Hash."
|
44
|
-
end
|
45
|
-
cases = []
|
46
|
-
for expected, url in @options[:actions]
|
47
|
-
cases << record+"."+@name.to_s+" == " + expected.inspect + "\nlink_to(content_tag(:i) + h(#{url[:action].inspect}.t(scope: 'labels'))"+
|
48
|
-
", {"+(url[:controller] ? 'controller: :'+url[:controller].to_s+', ' : '')+"action: '"+url[:action].to_s+"', id: "+record+".id"+format+"}"+
|
49
|
-
", {:class => '#{@name}'"+link_options+"}"+
|
50
|
-
")\n"
|
51
|
-
end
|
52
|
-
|
53
|
-
code = "if "+cases.join("elsif ")+"end"
|
54
|
-
else
|
55
|
-
url = @options[:url] ||= {}
|
56
|
-
url[:controller] ||= (@options[:controller] || "RECORD.class.name.tableize".c) # self.table.model.name.underscore.pluralize.to_s
|
57
|
-
url[:action] ||= @name.to_s
|
58
|
-
url[:id] ||= "RECORD.id".c
|
59
|
-
url.delete_if{|k, v| v.nil?}
|
60
|
-
url = "{" + url.collect{|k, v| "#{k}: " + urlify(v, record)}.join(", ")+format+"}"
|
61
|
-
code = "{class: '#{@name}'"+link_options+"}"
|
62
|
-
code = "link_to(content_tag(:i) + h('#{action}'.t(scope: 'labels')), "+url+", "+code+")"
|
63
|
-
end
|
64
|
-
if @options[:if]
|
65
|
-
code = "if " + recordify!(@options[:if], record) + "\n" + code.dig + "end"
|
66
|
-
end
|
67
|
-
if @options[:unless]
|
68
|
-
code = "unless " + recordify!(@options[:unless], record) + "\n" + code.dig + "end"
|
69
|
-
end
|
70
|
-
code.c
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
module ActiveList
|
2
|
-
|
3
|
-
module Definition
|
4
|
-
|
5
|
-
class AssociationColumn < DataColumn
|
6
|
-
|
7
|
-
attr_reader :label_method, :reflection
|
8
|
-
|
9
|
-
def initialize(table, name, options = {})
|
10
|
-
super(table, name, options)
|
11
|
-
unless @options[:through]
|
12
|
-
raise ArgumentError, "Option :through must be given"
|
13
|
-
end
|
14
|
-
reflection_name = @options.delete(:through).to_sym
|
15
|
-
if @reflection = @table.model.reflect_on_association(reflection_name)
|
16
|
-
if @reflection.macro == :belongs_to
|
17
|
-
# Do some stuff
|
18
|
-
elsif @reflection.macro == :has_one
|
19
|
-
# Do some stuff
|
20
|
-
else
|
21
|
-
raise ArgumentError, "Only belongs_to are usable. Can't handle: #{reflection.macro} :#{reflection.name}."
|
22
|
-
end
|
23
|
-
else
|
24
|
-
raise UnknownReflection, "Reflection #{reflection_name} cannot be found for #{table.model.name}."
|
25
|
-
end
|
26
|
-
columns_def = @reflection.class_name.constantize.columns_hash.keys.map(&:to_sym)
|
27
|
-
unless @label_method = @options.delete(:label_method)
|
28
|
-
columns = columns_def + @reflection.class_name.constantize.instance_methods.map(&:to_sym)
|
29
|
-
unless @label_method = LABELS_COLUMNS.detect{|m| columns.include?(m)}
|
30
|
-
raise ArgumentError, ":label_method option must be given for association #{name}. (#{columns.inspect})"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
unless @sort_column = @options.delete(:sort)
|
34
|
-
if columns_def.include?(@label_method)
|
35
|
-
@sort_column = @label_method
|
36
|
-
else
|
37
|
-
unless @sort_column = LABELS_COLUMNS.detect{|m| columns_def.include?(m)}
|
38
|
-
@sort_column = :id
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
# Code for rows
|
46
|
-
def datum_code(record = 'record_of_the_death', child = false)
|
47
|
-
code = ""
|
48
|
-
if child
|
49
|
-
code = "nil"
|
50
|
-
# if @options[:children].is_a?(FalseClass)
|
51
|
-
# code = "nil"
|
52
|
-
# else
|
53
|
-
# code = "#{record}.#{table.options[:children]}.#{@reflection.name}.#{@options[:children] || @label_method}"
|
54
|
-
# end
|
55
|
-
else
|
56
|
-
code = "(#{record}.#{@reflection.name} ? #{record}.#{@reflection.name}.#{@label_method} : nil)"
|
57
|
-
end
|
58
|
-
return code.c
|
59
|
-
end
|
60
|
-
|
61
|
-
def class_name
|
62
|
-
return @reflection.class_name
|
63
|
-
end
|
64
|
-
|
65
|
-
def record_expr(record = 'record_of_the_death')
|
66
|
-
return "#{record}.#{@reflection.name}"
|
67
|
-
end
|
68
|
-
|
69
|
-
def sort_expression
|
70
|
-
if table.reflections.select{|r| r.table_name == @reflection.table_name}.size > 1
|
71
|
-
"#{@reflection.name.to_s.pluralize}_#{@reflection.class_name.constantize.table_name}.#{@sort_column}"
|
72
|
-
else
|
73
|
-
"#{@reflection.class_name.constantize.table_name}.#{@sort_column}"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
module ActiveList
|
2
|
-
|
3
|
-
module Definition
|
4
|
-
|
5
|
-
class AttributeColumn < DataColumn
|
6
|
-
|
7
|
-
attr_reader :column, :label_method, :sort_column
|
8
|
-
|
9
|
-
def initialize(table, name, options = {})
|
10
|
-
super(table, name, options)
|
11
|
-
@label_method = (options[:label_method] || @name).to_sym
|
12
|
-
unless @sort_column = options[:sort]
|
13
|
-
if @table.model.columns_hash[@label_method]
|
14
|
-
@sort_column = @label_method
|
15
|
-
elsif @table.model.columns_hash[@name]
|
16
|
-
@sort_column = @name
|
17
|
-
else
|
18
|
-
@sort_column = :id
|
19
|
-
end
|
20
|
-
end
|
21
|
-
@column = @table.model.columns_hash[@label_method.to_s]
|
22
|
-
end
|
23
|
-
|
24
|
-
# Code for rows
|
25
|
-
def datum_code(record = 'record_of_the_death', child = false)
|
26
|
-
code = ""
|
27
|
-
if child
|
28
|
-
if @options[:children].is_a?(FalseClass)
|
29
|
-
code = "nil"
|
30
|
-
else
|
31
|
-
code = "#{record}.#{table.options[:children]}.#{@options[:children] || @label_method}"
|
32
|
-
end
|
33
|
-
else
|
34
|
-
code = "#{record}.#{@label_method}"
|
35
|
-
end
|
36
|
-
return code.c
|
37
|
-
end
|
38
|
-
|
39
|
-
# Returns the class name of the used model
|
40
|
-
def class_name
|
41
|
-
return self.table.model.name
|
42
|
-
end
|
43
|
-
|
44
|
-
def enumerize?
|
45
|
-
self.table.model.send(@label_method).send(:values)
|
46
|
-
return true
|
47
|
-
rescue
|
48
|
-
return false
|
49
|
-
end
|
50
|
-
|
51
|
-
def sort_expression
|
52
|
-
"#{@table.model.table_name}.#{@sort_column}"
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module ActiveList
|
2
|
-
|
3
|
-
module Definition
|
4
|
-
|
5
|
-
class CheckBoxColumn < FieldColumn
|
6
|
-
attr_reader :form_value
|
7
|
-
|
8
|
-
def initialize(table, name, options = {})
|
9
|
-
super(table, name, options)
|
10
|
-
@form_value = options.delete(:form_value)
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
module ActiveList
|
2
|
-
|
3
|
-
module Definition
|
4
|
-
|
5
|
-
class DataColumn < AbstractColumn
|
6
|
-
|
7
|
-
LABELS_COLUMNS = [:full_name, :label, :name, :number, :coordinate]
|
8
|
-
|
9
|
-
def header_code
|
10
|
-
if @options[:label]
|
11
|
-
"#{@options[:label].to_s.strip.inspect}.t(scope: 'labels')".c
|
12
|
-
else
|
13
|
-
"#{@table.model.name}.human_attribute_name(#{@name.inspect})".c
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# Code for exportation
|
18
|
-
def exporting_datum_code(record='record_of_the_death', noview=false)
|
19
|
-
datum = self.datum_code(record)
|
20
|
-
if self.datatype == :boolean
|
21
|
-
datum = "(#{datum} ? ::I18n.translate('list.export.true_value') : ::I18n.translate('list.export.false_value'))"
|
22
|
-
elsif self.datatype == :date
|
23
|
-
datum = "(#{datum}.nil? ? '' : #{datum}.l)"
|
24
|
-
elsif self.datatype == :decimal and not noview
|
25
|
-
currency = nil
|
26
|
-
if currency = self.options[:currency]
|
27
|
-
currency = currency[:body] if currency.is_a?(Hash)
|
28
|
-
currency = :currency if currency.is_a?(TrueClass)
|
29
|
-
currency = "#{record}.#{currency}".c if currency.is_a?(Symbol)
|
30
|
-
end
|
31
|
-
datum = "(#{datum}.nil? ? '' : #{datum}.l(#{'currency: ' + currency.inspect if currency}))"
|
32
|
-
elsif @name.to_s.match(/(^|\_)currency$/) and self.datatype == :string
|
33
|
-
datum = "(Nomen::Currencies[#{datum}] ? Nomen::Currencies[#{datum}].human_name : '')"
|
34
|
-
elsif @name.to_s.match(/(^|\_)country$/) and self.datatype == :string
|
35
|
-
datum = "(Nomen::Countries[#{datum}] ? Nomen::Countries[#{datum}].human_name : '')"
|
36
|
-
elsif @name.to_s.match(/(^|\_)language$/) and self.datatype == :string
|
37
|
-
datum = "(Nomen::Languages[#{datum}] ? Nomen::Languages[#{datum}].human_name : '')"
|
38
|
-
elsif self.enumerize?
|
39
|
-
datum = "(#{datum}.nil? ? '' : #{datum}.text)"
|
40
|
-
end
|
41
|
-
return datum
|
42
|
-
end
|
43
|
-
|
44
|
-
# Returns the data type of the column if the column is in the database
|
45
|
-
def datatype
|
46
|
-
@options[:datatype] || (@column ? @column.type : nil)
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
def enumerize?
|
51
|
-
return false
|
52
|
-
end
|
53
|
-
|
54
|
-
def numeric?
|
55
|
-
[:decimal, :integer, :float, :numeric].include? self.datatype
|
56
|
-
end
|
57
|
-
|
58
|
-
# Returns the size/length of the column if the column is in the database
|
59
|
-
def limit
|
60
|
-
@column[:limit] if @column
|
61
|
-
end
|
62
|
-
|
63
|
-
# Defines if column is exportable
|
64
|
-
def exportable?
|
65
|
-
true
|
66
|
-
end
|
67
|
-
|
68
|
-
# Check if a column is sortable
|
69
|
-
def sortable?
|
70
|
-
return true
|
71
|
-
# not self.action? and
|
72
|
-
not self.options[:through] and not @column.nil?
|
73
|
-
end
|
74
|
-
|
75
|
-
# Generate code in order to get the (foreign) record of the column
|
76
|
-
def record_expr(record = 'record_of_the_death')
|
77
|
-
return record
|
78
|
-
end
|
79
|
-
|
80
|
-
def sort_expression
|
81
|
-
raise NotImplementedError, "sort_expression must be implemented"
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|