will_paginate 3.0.pre4 → 3.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.
Potentially problematic release.
This version of will_paginate might be problematic. Click here for more details.
- data/README.md +61 -0
- data/Rakefile +19 -36
- data/lib/will_paginate.rb +16 -14
- data/lib/will_paginate/{finders/active_record.rb → active_record.rb} +66 -35
- data/lib/will_paginate/array.rb +5 -5
- data/lib/will_paginate/collection.rb +12 -36
- data/lib/will_paginate/core_ext.rb +0 -28
- data/lib/will_paginate/data_mapper.rb +91 -0
- data/lib/will_paginate/i18n.rb +22 -0
- data/lib/will_paginate/locale/en.yml +33 -0
- data/lib/will_paginate/page_number.rb +57 -0
- data/lib/will_paginate/per_page.rb +27 -0
- data/lib/will_paginate/railtie.rb +13 -4
- data/lib/will_paginate/sequel.rb +36 -0
- data/lib/will_paginate/version.rb +1 -1
- data/lib/will_paginate/view_helpers.rb +136 -22
- data/lib/will_paginate/view_helpers/action_view.rb +129 -117
- data/lib/will_paginate/view_helpers/link_renderer.rb +10 -11
- data/lib/will_paginate/view_helpers/link_renderer_base.rb +2 -8
- data/lib/will_paginate/view_helpers/merb.rb +20 -7
- data/lib/will_paginate/view_helpers/sinatra.rb +41 -0
- data/spec/ci.rb +29 -0
- data/spec/collection_spec.rb +7 -23
- data/spec/console +9 -1
- data/spec/console_fixtures.rb +1 -3
- data/spec/database.yml +10 -10
- data/spec/finders/active_record_spec.rb +82 -28
- data/spec/finders/activerecord_test_connector.rb +9 -1
- data/spec/finders/data_mapper_spec.rb +59 -48
- data/spec/finders/data_mapper_test_connector.rb +8 -1
- data/spec/finders/sequel_spec.rb +9 -3
- data/spec/fixtures/project.rb +2 -0
- data/spec/page_number_spec.rb +65 -0
- data/spec/per_page_spec.rb +41 -0
- data/spec/spec_helper.rb +5 -29
- data/spec/view_helpers/action_view_spec.rb +48 -32
- data/spec/view_helpers/base_spec.rb +59 -7
- data/spec/view_helpers/link_renderer_base_spec.rb +7 -12
- data/spec/view_helpers/view_example_group.rb +1 -0
- metadata +22 -23
- data/CHANGELOG.rdoc +0 -105
- data/README.rdoc +0 -111
- data/lib/will_paginate/deprecation.rb +0 -50
- data/lib/will_paginate/finders.rb +0 -9
- data/lib/will_paginate/finders/active_resource.rb +0 -51
- data/lib/will_paginate/finders/base.rb +0 -112
- data/lib/will_paginate/finders/data_mapper.rb +0 -30
- data/lib/will_paginate/finders/sequel.rb +0 -23
- data/lib/will_paginate/view_helpers/base.rb +0 -126
- data/spec/finders/active_resource_spec.rb +0 -52
- data/spec/finders_spec.rb +0 -76
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-aggregates'
|
3
|
+
require 'will_paginate/per_page'
|
4
|
+
require 'will_paginate/page_number'
|
5
|
+
require 'will_paginate/collection'
|
6
|
+
|
7
|
+
module WillPaginate
|
8
|
+
module DataMapper
|
9
|
+
module Pagination
|
10
|
+
def page(num)
|
11
|
+
pagenum = ::WillPaginate::PageNumber(num.nil? ? 1 : num)
|
12
|
+
per_page = query.limit || self.per_page
|
13
|
+
options = {:offset => pagenum.to_offset(per_page).to_i}
|
14
|
+
options[:limit] = per_page unless query.limit
|
15
|
+
col = new_collection(query.merge(options))
|
16
|
+
col.current_page = pagenum
|
17
|
+
col
|
18
|
+
end
|
19
|
+
|
20
|
+
def paginate(options)
|
21
|
+
options = options.dup
|
22
|
+
pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" }
|
23
|
+
per_page = options.delete(:per_page) || self.per_page
|
24
|
+
|
25
|
+
options.delete(:page)
|
26
|
+
options[:limit] = per_page.to_i
|
27
|
+
|
28
|
+
all(options).page(pagenum)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module CollectionMethods
|
33
|
+
attr_accessor :current_page
|
34
|
+
|
35
|
+
def paginated?
|
36
|
+
!current_page.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
def per_page
|
40
|
+
query.limit || model.per_page
|
41
|
+
end
|
42
|
+
|
43
|
+
def offset
|
44
|
+
query.offset
|
45
|
+
end
|
46
|
+
|
47
|
+
def total_entries
|
48
|
+
@total_entries ||= begin
|
49
|
+
if loaded? and @array.size < per_page and (current_page == 1 or @array.size > 0)
|
50
|
+
offset + @array.size
|
51
|
+
else
|
52
|
+
clean_query = query.merge(:order => [])
|
53
|
+
# seems like the only way
|
54
|
+
clean_query.instance_variable_set('@limit', nil)
|
55
|
+
clean_query.instance_variable_set('@offset', 0)
|
56
|
+
new_collection(clean_query).count
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def total_pages
|
62
|
+
(total_entries / per_page.to_f).ceil
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_a
|
66
|
+
::WillPaginate::Collection.create(current_page, per_page) do |col|
|
67
|
+
col.replace super
|
68
|
+
col.total_entries ||= total_entries
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def new_collection(query, resources = nil)
|
75
|
+
col = super
|
76
|
+
col.current_page = self.current_page
|
77
|
+
col
|
78
|
+
end
|
79
|
+
|
80
|
+
def initialize_copy(original)
|
81
|
+
super
|
82
|
+
@total_entries = nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
::DataMapper::Model.append_extensions PerPage
|
87
|
+
::DataMapper::Model.append_extensions Pagination
|
88
|
+
::DataMapper::Collection.send(:include, Pagination)
|
89
|
+
::DataMapper::Collection.send(:include, CollectionMethods)
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module WillPaginate
|
2
|
+
module I18n
|
3
|
+
def self.locale_dir
|
4
|
+
File.expand_path('../locale', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.load_path
|
8
|
+
Dir["#{locale_dir}/*.{rb,yml}"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def will_paginate_translate(keys, options = {})
|
12
|
+
if defined? ::I18n
|
13
|
+
defaults = Array(keys).dup
|
14
|
+
defaults << Proc.new if block_given?
|
15
|
+
::I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => :will_paginate))
|
16
|
+
else
|
17
|
+
key = Array === keys ? keys.first : keys
|
18
|
+
yield key, options
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
en:
|
2
|
+
will_paginate:
|
3
|
+
previous_label: "← Previous"
|
4
|
+
next_label: "Next →"
|
5
|
+
page_gap: "…"
|
6
|
+
|
7
|
+
page_entries_info:
|
8
|
+
single_page:
|
9
|
+
zero: "No %{model} found"
|
10
|
+
one: "Displaying 1 %{model}"
|
11
|
+
other: "Displaying all %{count} %{model}"
|
12
|
+
single_page_html:
|
13
|
+
zero: "No %{model} found"
|
14
|
+
one: "Displaying <b>1</b> %{model}"
|
15
|
+
other: "Displaying <b>all %{count}</b> %{model}"
|
16
|
+
|
17
|
+
multi_page: "Displaying %{model} %{from} - %{to} of %{count} in total"
|
18
|
+
multi_page_html: "Displaying %{model} <b>%{from} - %{to}</b> of <b>%{count}</b> in total"
|
19
|
+
|
20
|
+
# models:
|
21
|
+
# entry:
|
22
|
+
# zero: entries
|
23
|
+
# one: entry
|
24
|
+
# few: entries
|
25
|
+
# other: entries
|
26
|
+
|
27
|
+
# line_item:
|
28
|
+
# page_entries_info:
|
29
|
+
# single_page:
|
30
|
+
# zero: "Your shopping cart is empty"
|
31
|
+
# one: "Displaying one item in your cart"
|
32
|
+
# other: "Displaying all %{count} items"
|
33
|
+
# multi_page: "Displaying items %{from} - %{to} of %{count} in total"
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module WillPaginate
|
5
|
+
# a module that page number exceptions are tagged with
|
6
|
+
module InvalidPage; end
|
7
|
+
|
8
|
+
# integer representing a page number
|
9
|
+
class PageNumber < DelegateClass(Integer)
|
10
|
+
# a value larger than this is not supported in SQL queries
|
11
|
+
BIGINT = 9223372036854775807
|
12
|
+
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
def initialize(value, name)
|
16
|
+
value = Integer(value)
|
17
|
+
if 'offset' == name ? (value < 0 or value > BIGINT) : value < 1
|
18
|
+
raise RangeError, "invalid #{name}: #{value.inspect}"
|
19
|
+
end
|
20
|
+
@name = name
|
21
|
+
super(value)
|
22
|
+
rescue ArgumentError, TypeError, RangeError => error
|
23
|
+
error.extend InvalidPage
|
24
|
+
raise error
|
25
|
+
end
|
26
|
+
|
27
|
+
alias_method :to_i, :__getobj__
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
"#{@name} #{to_i}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_offset(per_page)
|
34
|
+
PageNumber.new((to_i - 1) * per_page.to_i, 'offset')
|
35
|
+
end
|
36
|
+
|
37
|
+
def kind_of?(klass)
|
38
|
+
super || to_i.kind_of?(klass)
|
39
|
+
end
|
40
|
+
alias is_a? kind_of?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Ultrahax: makes `Fixnum === current_page` checks pass
|
44
|
+
Numeric.extend Module.new {
|
45
|
+
def ===(obj)
|
46
|
+
obj.instance_of? PageNumber or super
|
47
|
+
end
|
48
|
+
}
|
49
|
+
|
50
|
+
# An idemptotent coercion method
|
51
|
+
def self.PageNumber(value, name = 'page')
|
52
|
+
case value
|
53
|
+
when PageNumber then value
|
54
|
+
else PageNumber.new(value, name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module WillPaginate
|
2
|
+
module PerPage
|
3
|
+
def per_page
|
4
|
+
defined?(@per_page) ? @per_page : WillPaginate.per_page
|
5
|
+
end
|
6
|
+
|
7
|
+
def per_page=(limit)
|
8
|
+
@per_page = limit.to_i
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.extended(base)
|
12
|
+
base.extend Inheritance if base.is_a? Class
|
13
|
+
end
|
14
|
+
|
15
|
+
module Inheritance
|
16
|
+
def inherited(subclass)
|
17
|
+
super
|
18
|
+
subclass.per_page = self.per_page
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
extend PerPage
|
24
|
+
|
25
|
+
# default number of items per page
|
26
|
+
self.per_page = 30
|
27
|
+
end
|
@@ -1,22 +1,31 @@
|
|
1
1
|
require 'will_paginate'
|
2
2
|
require 'will_paginate/collection'
|
3
|
+
require 'will_paginate/i18n'
|
3
4
|
|
4
5
|
module WillPaginate
|
5
6
|
class Railtie < Rails::Railtie
|
6
7
|
initializer "will_paginate" do |app|
|
7
8
|
ActiveSupport.on_load :active_record do
|
8
|
-
require 'will_paginate/
|
9
|
-
WillPaginate::Finders::ActiveRecord.enable!
|
9
|
+
require 'will_paginate/active_record'
|
10
10
|
end
|
11
11
|
|
12
12
|
ActiveSupport.on_load :action_controller do
|
13
|
-
|
13
|
+
WillPaginate::Railtie.setup_actioncontroller
|
14
14
|
end
|
15
15
|
|
16
16
|
ActiveSupport.on_load :action_view do
|
17
17
|
require 'will_paginate/view_helpers/action_view'
|
18
|
-
include WillPaginate::ViewHelpers::ActionView
|
19
18
|
end
|
19
|
+
|
20
|
+
self.class.add_locale_path config
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.setup_actioncontroller
|
24
|
+
ActionDispatch::ShowExceptions.rescue_responses['WillPaginate::InvalidPage'] = :not_found
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.add_locale_path(config)
|
28
|
+
config.i18n.railties_load_path.unshift(*WillPaginate::I18n.load_path)
|
20
29
|
end
|
21
30
|
end
|
22
31
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'sequel/extensions/pagination'
|
3
|
+
|
4
|
+
module WillPaginate
|
5
|
+
# Sequel already supports pagination; we only need to make the
|
6
|
+
# resulting dataset look a bit more like WillPaginate::Collection
|
7
|
+
module SequelMethods
|
8
|
+
def total_pages
|
9
|
+
page_count
|
10
|
+
end
|
11
|
+
|
12
|
+
def per_page
|
13
|
+
page_size
|
14
|
+
end
|
15
|
+
|
16
|
+
def size
|
17
|
+
current_page_record_count
|
18
|
+
end
|
19
|
+
alias length size
|
20
|
+
|
21
|
+
def total_entries
|
22
|
+
pagination_record_count
|
23
|
+
end
|
24
|
+
|
25
|
+
def out_of_bounds?
|
26
|
+
current_page > total_pages
|
27
|
+
end
|
28
|
+
|
29
|
+
# Current offset of the paginated collection
|
30
|
+
def offset
|
31
|
+
(current_page - 1) * per_page
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Sequel::Dataset::Pagination.send(:include, SequelMethods)
|
36
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'will_paginate/core_ext'
|
3
|
+
require 'will_paginate/i18n'
|
2
4
|
|
3
5
|
module WillPaginate
|
4
6
|
# = Will Paginate view helpers
|
@@ -7,36 +9,148 @@ module WillPaginate
|
|
7
9
|
# for the given collection. The helper itself is lightweight and serves only
|
8
10
|
# as a wrapper around LinkRenderer instantiation; the renderer then does
|
9
11
|
# all the hard work of generating the HTML.
|
10
|
-
#
|
11
|
-
# Read more in WillPaginate::ViewHelpers::Base
|
12
12
|
module ViewHelpers
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
#
|
22
|
-
# texts to previous and next pages, as well as override some other defaults
|
23
|
-
# to your liking.
|
24
|
-
def self.pagination_options() @pagination_options; end
|
25
|
-
# Overrides the default +pagination_options+
|
26
|
-
def self.pagination_options=(value) @pagination_options = value; end
|
27
|
-
|
13
|
+
class << self
|
14
|
+
# Write to this hash to override default options on the global level:
|
15
|
+
#
|
16
|
+
# WillPaginate::ViewHelpers.pagination_options[:page_links] = false
|
17
|
+
#
|
18
|
+
attr_accessor :pagination_options
|
19
|
+
end
|
20
|
+
|
21
|
+
# default view options
|
28
22
|
self.pagination_options = {
|
29
23
|
:class => 'pagination',
|
30
|
-
:previous_label =>
|
31
|
-
:next_label =>
|
24
|
+
:previous_label => nil,
|
25
|
+
:next_label => nil,
|
32
26
|
:inner_window => 4, # links around the current page
|
33
27
|
:outer_window => 1, # links around beginning and end
|
34
|
-
:
|
28
|
+
:link_separator => ' ', # single space is friendly to spiders and non-graphic browsers
|
35
29
|
:param_name => :page,
|
36
30
|
:params => nil,
|
37
|
-
:renderer =>
|
31
|
+
:renderer => nil,
|
38
32
|
:page_links => true,
|
39
33
|
:container => true
|
40
34
|
}
|
35
|
+
|
36
|
+
include WillPaginate::I18n
|
37
|
+
|
38
|
+
# Returns HTML representing page links for a WillPaginate::Collection-like object.
|
39
|
+
# In case there is no more than one page in total, nil is returned.
|
40
|
+
#
|
41
|
+
# ==== Options
|
42
|
+
# * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
|
43
|
+
# * <tt>:previous_label</tt> -- default: "« Previous"
|
44
|
+
# * <tt>:next_label</tt> -- default: "Next »"
|
45
|
+
# * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
|
46
|
+
# * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
|
47
|
+
# * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
|
48
|
+
# * <tt>:link_separator</tt> -- string separator for page HTML elements (default: single space)
|
49
|
+
# * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
|
50
|
+
# * <tt>:params</tt> -- additional parameters when generating pagination links
|
51
|
+
# (eg. <tt>:controller => "foo", :action => nil</tt>)
|
52
|
+
# * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default:
|
53
|
+
# <tt>WillPaginate::LinkRenderer</tt>)
|
54
|
+
# * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
|
55
|
+
# * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
|
56
|
+
# false only when you are rendering your own pagination markup (default: true)
|
57
|
+
#
|
58
|
+
# All options not recognized by will_paginate will become HTML attributes on the container
|
59
|
+
# element for pagination links (the DIV). For example:
|
60
|
+
#
|
61
|
+
# <%= will_paginate @posts, :style => 'color:blue' %>
|
62
|
+
#
|
63
|
+
# will result in:
|
64
|
+
#
|
65
|
+
# <div class="pagination" style="color:blue"> ... </div>
|
66
|
+
#
|
67
|
+
def will_paginate(collection, options = {})
|
68
|
+
# early exit if there is nothing to render
|
69
|
+
return nil unless collection.total_pages > 1
|
70
|
+
|
71
|
+
options = WillPaginate::ViewHelpers.pagination_options.merge(options)
|
72
|
+
|
73
|
+
options[:previous_label] ||= will_paginate_translate(:previous_label) { '← Previous' }
|
74
|
+
options[:next_label] ||= will_paginate_translate(:next_label) { 'Next →' }
|
75
|
+
|
76
|
+
# get the renderer instance
|
77
|
+
renderer = case options[:renderer]
|
78
|
+
when nil
|
79
|
+
raise ArgumentError, ":renderer not specified"
|
80
|
+
when String
|
81
|
+
klass = if options[:renderer].respond_to? :constantize then options[:renderer].constantize
|
82
|
+
else Object.const_get(options[:renderer]) # poor man's constantize
|
83
|
+
end
|
84
|
+
klass.new
|
85
|
+
when Class then options[:renderer].new
|
86
|
+
else options[:renderer]
|
87
|
+
end
|
88
|
+
# render HTML for pagination
|
89
|
+
renderer.prepare collection, options, self
|
90
|
+
renderer.to_html
|
91
|
+
end
|
92
|
+
|
93
|
+
# Renders a message containing number of displayed vs. total entries.
|
94
|
+
#
|
95
|
+
# <%= page_entries_info @posts %>
|
96
|
+
# #-> Displaying posts 6 - 12 of 26 in total
|
97
|
+
#
|
98
|
+
# The default output contains HTML. Use ":html => false" for plain text.
|
99
|
+
def page_entries_info(collection, options = {})
|
100
|
+
model = options[:model]
|
101
|
+
model = collection.first.class unless model or collection.empty?
|
102
|
+
model ||= 'entry'
|
103
|
+
model_key = if model.respond_to? :model_name
|
104
|
+
model.model_name.i18n_key # ActiveModel::Naming
|
105
|
+
else
|
106
|
+
model.to_s.underscore
|
107
|
+
end
|
108
|
+
|
109
|
+
if options.fetch(:html, true)
|
110
|
+
b, eb = '<b>', '</b>'
|
111
|
+
sp = ' '
|
112
|
+
html_key = '_html'
|
113
|
+
else
|
114
|
+
b = eb = html_key = ''
|
115
|
+
sp = ' '
|
116
|
+
end
|
117
|
+
|
118
|
+
model_count = collection.total_pages > 1 ? 5 : collection.size
|
119
|
+
defaults = ["models.#{model_key}"]
|
120
|
+
defaults << Proc.new { |_, opts|
|
121
|
+
if model.respond_to? :model_name
|
122
|
+
model.model_name.human(:count => opts[:count])
|
123
|
+
else
|
124
|
+
name = model_key.to_s.tr('_', ' ')
|
125
|
+
raise "can't pluralize model name: #{model.inspect}" unless name.respond_to? :pluralize
|
126
|
+
opts[:count] == 1 ? name : name.pluralize
|
127
|
+
end
|
128
|
+
}
|
129
|
+
model_name = will_paginate_translate defaults, :count => model_count
|
130
|
+
|
131
|
+
if collection.total_pages < 2
|
132
|
+
i18n_key = :"page_entries_info.single_page#{html_key}"
|
133
|
+
keys = [:"#{model_key}.#{i18n_key}", i18n_key]
|
134
|
+
|
135
|
+
will_paginate_translate keys, :count => collection.size, :model => model_name do |_, opts|
|
136
|
+
case opts[:count]
|
137
|
+
when 0; "No #{opts[:model]} found"
|
138
|
+
when 1; "Displaying #{b}1#{eb} #{opts[:model]}"
|
139
|
+
else "Displaying #{b}all#{sp}#{opts[:count]}#{eb} #{opts[:model]}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
else
|
143
|
+
i18n_key = :"page_entries_info.multi_page#{html_key}"
|
144
|
+
keys = [:"#{model_key}.#{i18n_key}", i18n_key]
|
145
|
+
params = {
|
146
|
+
:model => model_name, :count => collection.total_entries,
|
147
|
+
:from => collection.offset + 1, :to => collection.offset + collection.length
|
148
|
+
}
|
149
|
+
will_paginate_translate keys, params do |_, opts|
|
150
|
+
%{Displaying %s #{b}%d#{sp}-#{sp}%d#{eb} of #{b}%d#{eb} in total} %
|
151
|
+
[ opts[:model], opts[:from], opts[:to], opts[:count] ]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
41
155
|
end
|
42
156
|
end
|