diff --git a/app/controllers/changesets_controller.rb b/app/controllers/changesets_controller.rb
index 928f1c1ecf..d6dec70adb 100644
--- a/app/controllers/changesets_controller.rb
+++ b/app/controllers/changesets_controller.rb
@@ -79,28 +79,33 @@ def show
@changeset = Changeset.find(params[:id])
case turbo_frame_request_id
when "changeset_nodes"
- @node_pages, @nodes = paginate(:old_nodes, :conditions => { :changeset_id => @changeset.id }, :order => [:node_id, :version], :per_page => 20, :parameter => "node_page")
- render :partial => "elements", :locals => { :type => "node", :elements => @nodes, :pages => @node_pages }
+ load_nodes
+
+ render :partial => "elements", :locals => { :type => "node", :paginator => @nodes_paginator }
when "changeset_ways"
- @way_pages, @ways = paginate(:old_ways, :conditions => { :changeset_id => @changeset.id }, :order => [:way_id, :version], :per_page => 20, :parameter => "way_page")
- render :partial => "elements", :locals => { :type => "way", :elements => @ways, :pages => @way_pages }
+ load_ways
+
+ render :partial => "elements", :locals => { :type => "way", :paginator => @ways_paginator }
when "changeset_relations"
- @relation_pages, @relations = paginate(:old_relations, :conditions => { :changeset_id => @changeset.id }, :order => [:relation_id, :version], :per_page => 20, :parameter => "relation_page")
- render :partial => "elements", :locals => { :type => "relation", :elements => @relations, :pages => @relation_pages }
+ load_relations
+
+ render :partial => "elements", :locals => { :type => "relation", :paginator => @relations_paginator }
else
@comments = if current_user&.moderator?
@changeset.comments.unscope(:where => :visible).includes(:author)
else
@changeset.comments.includes(:author)
end
- @node_pages, @nodes = paginate(:old_nodes, :conditions => { :changeset_id => @changeset.id }, :order => [:node_id, :version], :per_page => 20, :parameter => "node_page")
- @way_pages, @ways = paginate(:old_ways, :conditions => { :changeset_id => @changeset.id }, :order => [:way_id, :version], :per_page => 20, :parameter => "way_page")
- @relation_pages, @relations = paginate(:old_relations, :conditions => { :changeset_id => @changeset.id }, :order => [:relation_id, :version], :per_page => 20, :parameter => "relation_page")
+ load_nodes
+ load_ways
+ load_relations
+
if @changeset.user.active? && @changeset.user.data_public?
changesets = conditions_nonempty(@changeset.user.changesets)
@next_by_user = changesets.where("id > ?", @changeset.id).reorder(:id => :asc).first
@prev_by_user = changesets.where(:id => ...@changeset.id).reorder(:id => :desc).first
end
+
render :layout => map_layout
end
rescue ActiveRecord::RecordNotFound
@@ -163,4 +168,50 @@ def conditions_bbox(changesets, bbox)
def conditions_nonempty(changesets)
changesets.where("num_changes > 0")
end
+
+ def load_nodes
+ nodes = OldNode.where(:changeset => @changeset).order(:node_id, :version)
+ @nodes_paginator = ElementsPaginator.new(nodes, 20, params[:node_page])
+ end
+
+ def load_ways
+ ways = OldWay.where(:changeset => @changeset).order(:way_id, :version)
+ @ways_paginator = ElementsPaginator.new(ways, 20, params[:way_page])
+ end
+
+ def load_relations
+ relations = OldRelation.where(:changeset => @changeset).order(:relation_id, :version)
+ @relations_paginator = ElementsPaginator.new(relations, 20, params[:relation_page])
+ end
+
+ class ElementsPaginator
+ attr_reader :elements_count, :elements_per_page, :current_page, :current_page_elements
+
+ def initialize(elements, elements_per_page, current_page = 1)
+ @elements_count = elements.count
+ @elements_per_page = elements_per_page.to_i
+ @current_page = current_page.to_i.clamp(1, pages_count)
+ @current_page_elements = elements.offset(@elements_per_page * (@current_page - 1)).limit(@elements_per_page)
+ end
+
+ def pages_count
+ [1, 1 + ((elements_count - 1) / elements_per_page)].max
+ end
+
+ def pages
+ 1..pages_count
+ end
+
+ def pages_window(window_size, page = current_page)
+ [page - window_size, pages.first].max..[page + window_size, pages.last].min
+ end
+
+ def lower_element_number(page = current_page)
+ (elements_per_page * (page - 1)) + 1
+ end
+
+ def upper_element_number(page = current_page)
+ [elements_per_page * page, elements_count].min
+ end
+ end
end
diff --git a/app/helpers/browse_helper.rb b/app/helpers/browse_helper.rb
index 69a8f8fa2e..26ef808219 100644
--- a/app/helpers/browse_helper.rb
+++ b/app/helpers/browse_helper.rb
@@ -70,33 +70,56 @@ def link_follow(object)
"nofollow" if object.tags.empty?
end
- def type_and_paginated_count(type, pages, selected_page = pages.current_page)
- if pages.page_count == 1
+ def type_and_paginated_count(type, paginator, page = paginator.current_page)
+ if paginator.pages_count <= 1
t ".#{type.pluralize}",
- :count => pages.item_count
+ :count => paginator.elements_count
else
t ".#{type.pluralize}_paginated",
- :x => selected_page.first_item,
- :y => selected_page.last_item,
- :count => pages.item_count
+ :x => paginator.lower_element_number(page),
+ :y => paginator.upper_element_number(page),
+ :count => paginator.elements_count
end
end
- def sidebar_classic_pagination(pages, page_param)
+ def sidebar_classic_pagination(paginator, page_param)
+ window_size = 2
max_width_for_default_padding = 35
+ pages = paginator.pages
+ window = paginator.pages_window(window_size)
+ page_items = []
+
+ if window.first != pages.first
+ page_items.push [pages.first.to_s, pages.first]
+ page_items.push ["...", "disabled"] if window.first - pages.first > 1
+ end
+
+ window.each do |page|
+ if paginator.current_page == page
+ page_items.push [page.to_s, "active"]
+ else
+ page_items.push [page.to_s, page]
+ end
+ end
+
+ if window.last != pages.last
+ page_items.push ["...", "disabled"] if pages.last - window.last > 1
+ page_items.push [pages.last.to_s, pages.last]
+ end
+
width = 0
- pagination_items(pages, {}).each do |(body)|
+ page_items.each do |(body)|
width += 2 # padding width
width += body.length
end
link_classes = ["page-link", { "px-1" => width > max_width_for_default_padding }]
tag.ul :class => "pagination pagination-sm mb-2" do
- pagination_items(pages, {}).each do |body, page_or_class|
+ page_items.each do |body, page_or_class|
linked = !(page_or_class.is_a? String)
link = if linked
- link_to body, url_for(page_param => page_or_class.number), :class => link_classes, **yield(page_or_class)
+ link_to body, url_for(page_param => page_or_class), :class => link_classes, **yield(page_or_class)
else
tag.span body, :class => link_classes
end
diff --git a/app/views/changesets/_elements.html.erb b/app/views/changesets/_elements.html.erb
index fd5dd8a26a..838cb71641 100644
--- a/app/views/changesets/_elements.html.erb
+++ b/app/views/changesets/_elements.html.erb
@@ -1,7 +1,7 @@
<%= turbo_frame_tag "changeset_#{type.pluralize}" do %>
- <%= render :partial => "paging_nav", :locals => { :type => type, :pages => pages } %>
+ <%= render :partial => "paging_nav", :locals => { :type => type, :paginator => paginator } %>
- <% elements.each do |element| %>
+ <% paginator.current_page_elements.each do |element| %>
<%= element_list_item type, element do
t "printable_name.current_and_old_links_html",
:current_link => link_to(printable_element_name(element), :controller => type.pluralize, :action => :show, :id => element.id[0]),
diff --git a/app/views/changesets/_paging_nav.html.erb b/app/views/changesets/_paging_nav.html.erb
index 0587382222..53f6031e78 100644
--- a/app/views/changesets/_paging_nav.html.erb
+++ b/app/views/changesets/_paging_nav.html.erb
@@ -1,8 +1,8 @@
-<%= type_and_paginated_count(type, pages) %>
-<% if pages.page_count > 1 %>
- <%= sidebar_classic_pagination(pages, "#{type}_page") do |page|
+<%= type_and_paginated_count(type, paginator) %>
+<% if paginator.pages_count > 1 %>
+ <%= sidebar_classic_pagination(paginator, "#{type}_page") do |page|
{
- :title => type_and_paginated_count(type, pages, page),
+ :title => type_and_paginated_count(type, paginator, page),
:data => { :turbo => "true" }
}
end %>
diff --git a/app/views/changesets/show.html.erb b/app/views/changesets/show.html.erb
index a47049e999..76d33386b4 100644
--- a/app/views/changesets/show.html.erb
+++ b/app/views/changesets/show.html.erb
@@ -89,16 +89,16 @@
<% end %>
<% end %>
- <% unless @ways.empty? %>
- <%= render :partial => "elements", :locals => { :type => "way", :elements => @ways, :pages => @way_pages } %>
+ <% unless @ways_paginator.current_page_elements.empty? %>
+ <%= render :partial => "elements", :locals => { :type => "way", :paginator => @ways_paginator } %>
<% end %>
- <% unless @relations.empty? %>
- <%= render :partial => "elements", :locals => { :type => "relation", :elements => @relations, :pages => @relation_pages } %>
+ <% unless @relations_paginator.current_page_elements.empty? %>
+ <%= render :partial => "elements", :locals => { :type => "relation", :paginator => @relations_paginator } %>
<% end %>
- <% unless @nodes.empty? %>
- <%= render :partial => "elements", :locals => { :type => "node", :elements => @nodes, :pages => @node_pages } %>
+ <% unless @nodes_paginator.current_page_elements.empty? %>
+ <%= render :partial => "elements", :locals => { :type => "node", :paginator => @nodes_paginator } %>
<% end %>
diff --git a/config/initializers/classic_pagination.rb b/config/initializers/classic_pagination.rb
deleted file mode 100644
index d87993a2bc..0000000000
--- a/config/initializers/classic_pagination.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-#--
-# Copyright (c) 2004-2006 David Heinemeier Hansson
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#++
-
-require "classic_pagination/pagination"
-require "classic_pagination/pagination_helper"
-
-ActionController::Base.class_eval do
- include ActionController::Pagination
-end
-
-ActionView::Base.class_eval do
- include ActionView::Helpers::PaginationHelper
-end
diff --git a/lib/classic_pagination/pagination.rb b/lib/classic_pagination/pagination.rb
deleted file mode 100644
index b54b0e9f85..0000000000
--- a/lib/classic_pagination/pagination.rb
+++ /dev/null
@@ -1,422 +0,0 @@
-module ActionController
- # === Action Pack pagination for Active Record collections
- #
- # The Pagination module aids in the process of paging large collections of
- # Active Record objects. It offers macro-style automatic fetching of your
- # model for multiple views, or explicit fetching for single actions. And if
- # the magic isn't flexible enough for your needs, you can create your own
- # paginators with a minimal amount of code.
- #
- # The Pagination module can handle as much or as little as you wish. In the
- # controller, have it automatically query your model for pagination; or,
- # if you prefer, create Paginator objects yourself.
- #
- # Pagination is included automatically for all controllers.
- #
- # For help rendering pagination links, see
- # ActionView::Helpers::PaginationHelper.
- #
- # ==== Automatic pagination for every action in a controller
- #
- # class PersonController < ApplicationController
- # model :person
- #
- # paginate :people, :order => 'last_name, first_name',
- # :per_page => 20
- #
- # # ...
- # end
- #
- # Each action in this controller now has access to a @people
- # instance variable, which is an ordered collection of model objects for the
- # current page (at most 20, sorted by last name and first name), and a
- # @person_pages Paginator instance. The current page is determined
- # by the params[:page] variable.
- #
- # ==== Pagination for a single action
- #
- # def list
- # @person_pages, @people =
- # paginate :people, :order => 'last_name, first_name'
- # end
- #
- # Like the previous example, but explicitly creates @person_pages
- # and @people for a single action, and uses the default of 10 items
- # per page.
- #
- # ==== Custom/"classic" pagination
- #
- # def list
- # @person_pages = Paginator.new self, Person.count, 10, params[:page]
- # @people = Person.find :all, :order => 'last_name, first_name',
- # :limit => @person_pages.items_per_page,
- # :offset => @person_pages.current.offset
- # end
- #
- # Explicitly creates the paginator from the previous example and uses
- # Paginator#to_sql to retrieve @people from the model.
- #
- module Pagination
- if const_defined?(:OPTIONS)
- DEFAULT_OPTIONS[:group] = nil
- else
- # A hash holding options for controllers using macro-style pagination
- OPTIONS = {}.freeze
-
- # The default options for pagination
- DEFAULT_OPTIONS = {
- :class_name => nil,
- :singular_name => nil,
- :per_page => 10,
- :conditions => nil,
- :order_by => nil,
- :order => nil,
- :join => nil,
- :joins => nil,
- :count => nil,
- :include => nil,
- :select => nil,
- :group => nil,
- :parameter => "page"
- }.freeze
- end
-
- def self.included(base) # :nodoc:
- super
- base.extend(ClassMethods)
- end
-
- def self.validate_options!(collection_id, options, in_action) # :nodoc:
- options.merge!(DEFAULT_OPTIONS) { |_key, old, _new| old }
-
- valid_options = DEFAULT_OPTIONS.keys
- valid_options << :actions unless in_action
-
- unknown_option_keys = options.keys - valid_options
- unless unknown_option_keys.empty?
- raise ActionController::ActionControllerError,
- "Unknown options: #{unknown_option_keys.join(', ')}"
- end
-
- options[:singular_name] ||= ActiveSupport::Inflector.singularize(collection_id.to_s)
- options[:class_name] ||= ActiveSupport::Inflector.camelize(options[:singular_name])
- end
-
- # Returns a paginator and a collection of Active Record model instances
- # for the paginator's current page. This is designed to be used in a
- # single action; to automatically paginate multiple actions, consider
- # ClassMethods#paginate.
- #
- # +options+ are:
- # :singular_name:: the singular name to use, if it can't be inferred by singularizing the collection name
- # :class_name:: the class name to use, if it can't be inferred by
- # camelizing the singular name
- # :per_page:: the maximum number of items to include in a
- # single page. Defaults to 10
- # :conditions:: optional conditions passed to Model.find(:all, *params) and
- # Model.count
- # :order:: optional order parameter passed to Model.find(:all, *params)
- # :order_by:: (deprecated, used :order) optional order parameter passed to Model.find(:all, *params)
- # :joins:: optional joins parameter passed to Model.find(:all, *params)
- # and Model.count
- # :join:: (deprecated, used :joins or :include) optional join parameter passed to Model.find(:all, *params)
- # and Model.count
- # :include:: optional eager loading parameter passed to Model.find(:all, *params)
- # and Model.count
- # :select:: :select parameter passed to Model.find(:all, *params)
- #
- # :count:: parameter passed as :select option to Model.count(*params)
- #
- # :group:: :group parameter passed to Model.find(:all, *params). It forces the use of DISTINCT instead of plain COUNT to come up with the total number of records
- #
- def paginate(collection_id, options = {})
- Pagination.validate_options!(collection_id, options, true)
- paginator_and_collection_for(collection_id, options)
- end
-
- # These methods become class methods on any controller
- module ClassMethods
- # Creates a +before_action+ which automatically paginates an Active
- # Record model for all actions in a controller (or certain actions if
- # specified with the :actions option).
- #
- # +options+ are the same as PaginationHelper#paginate, with the addition
- # of:
- # :actions:: an array of actions for which the pagination is
- # active. Defaults to +nil+ (i.e., every action)
- def paginate(collection_id, options = {})
- Pagination.validate_options!(collection_id, options, false)
- module_eval do
- before_action :create_paginators_and_retrieve_collections
- OPTIONS[self] ||= {}
- OPTIONS[self][collection_id] = options
- end
- end
- end
-
- protected
-
- def create_paginators_and_retrieve_collections # :nodoc:
- Pagination::OPTIONS[self.class].each do |collection_id, options|
- next if options[:actions]&.exclude?(action_name)
-
- paginator, collection =
- paginator_and_collection_for(collection_id, options)
-
- paginator_name = "@#{options[:singular_name]}_pages"
- instance_variable_set(paginator_name, paginator)
-
- collection_name = "@#{collection_id}"
- instance_variable_set(collection_name, collection)
- end
- end
-
- # Returns the total number of items in the collection to be paginated for
- # the +model+ and given +conditions+. Override this method to implement a
- # custom counter.
- def count_collection_for_pagination(model, options)
- collection = model.joins(options[:join] || options[:joins])
- collection = collection.where(options[:conditions])
- collection = collection.includes(options[:include])
-
- if options[:group]
- collection = collection.select(options[:group]).distinct
- elsif options[:count]
- collection = collection.select(options[:count])
- end
-
- collection.count
- end
-
- # Returns a collection of items for the given +model+ and +options[conditions]+,
- # ordered by +options[order]+, for the current page in the given +paginator+.
- # Override this method to implement a custom finder.
- def find_collection_for_pagination(model, options, paginator)
- collection = model.joins(options[:join] || options[:joins])
- collection = collection.where(options[:conditions])
- collection = collection.order(options[:order_by] || options[:order])
- collection = collection.includes(options[:include])
- collection = collection.group(options[:group])
- collection = collection.select(options[:select]) if options[:select]
-
- collection.offset(paginator.current.offset).limit(options[:per_page])
- end
-
- private
-
- def paginator_and_collection_for(_collection_id, options) # :nodoc:
- klass = options[:class_name].constantize
- page = params[options[:parameter]]
- count = count_collection_for_pagination(klass, options)
- paginator = Paginator.new(self, count, options[:per_page], page)
- collection = find_collection_for_pagination(klass, options, paginator)
-
- [paginator, collection]
- end
-
- # A class representing a paginator for an Active Record collection.
- class Paginator
- include Enumerable
-
- # Creates a new Paginator on the given +controller+ for a set of items
- # of size +item_count+ and having +items_per_page+ items per page.
- # Raises ArgumentError if items_per_page is out of bounds (i.e., less
- # than or equal to zero). The page CGI parameter for links defaults to
- # "page" and can be overridden with +page_parameter+.
- def initialize(controller, item_count, items_per_page, current_page = 1)
- raise ArgumentError, "must have at least one item per page" if
- items_per_page <= 0
-
- @controller = controller
- @item_count = item_count || 0
- @items_per_page = items_per_page
- @pages = {}
-
- self.current_page = current_page
- end
- attr_reader :controller, :item_count, :items_per_page
-
- # Sets the current page number of this paginator. If +page+ is a Page
- # object, its +number+ attribute is used as the value; if the page does
- # not belong to this Paginator, an ArgumentError is raised.
- def current_page=(page)
- raise ArgumentError, "Page/Paginator mismatch" if page.is_a?(Page) && page.paginator != self
-
- page = page.to_i
- @current_page_number = contains_page?(page) ? page : 1
- end
-
- # Returns a Page object representing this paginator's current page.
- def current_page
- @current_page ||= self[@current_page_number]
- end
- alias current current_page
-
- # Returns a new Page representing the first page in this paginator.
- def first_page
- @first_page ||= self[1]
- end
- alias first first_page
-
- # Returns a new Page representing the last page in this paginator.
- def last_page
- @last_page ||= self[page_count]
- end
- alias last last_page
-
- # Returns the number of pages in this paginator.
- def page_count
- @page_count ||= if @item_count.zero?
- 1
- else
- q, r = @item_count.divmod(@items_per_page)
- r.zero? ? q : q + 1
- end
- end
-
- alias length page_count
-
- # Returns true if this paginator contains the page of index +number+.
- def contains_page?(number)
- number >= 1 && number <= page_count
- end
-
- # Returns a new Page representing the page with the given index
- # +number+.
- def [](number)
- @pages[number] ||= Page.new(self, number)
- end
-
- # Successively yields all the paginator's pages to the given block.
- def each(&)
- page_count.times do |n|
- yield self[n + 1]
- end
- end
-
- # A class representing a single page in a paginator.
- class Page
- include Comparable
-
- # Creates a new Page for the given +paginator+ with the index
- # +number+. If +number+ is not in the range of valid page numbers or
- # is not a number at all, it defaults to 1.
- def initialize(paginator, number)
- @paginator = paginator
- @number = number.to_i
- @number = 1 unless @paginator.contains_page? @number
- end
- attr_reader :paginator, :number
-
- alias to_i number
-
- # Compares two Page objects and returns true when they represent the
- # same page (i.e., their paginators are the same and they have the
- # same page number).
- def ==(other)
- return false if other.nil?
-
- @paginator == other.paginator &&
- @number == other.number
- end
-
- # Compares two Page objects and returns -1 if the left-hand page comes
- # before the right-hand page, 0 if the pages are equal, and 1 if the
- # left-hand page comes after the right-hand page. Raises ArgumentError
- # if the pages do not belong to the same Paginator object.
- def <=>(other)
- raise ArgumentError unless @paginator == other.paginator
-
- @number <=> other.number
- end
-
- # Returns the item offset for the first item in this page.
- def offset
- @paginator.items_per_page * (@number - 1)
- end
-
- # Returns the number of the first item displayed.
- def first_item
- offset + 1
- end
-
- # Returns the number of the last item displayed.
- def last_item
- [@paginator.items_per_page * @number, @paginator.item_count].min
- end
-
- # Returns true if this page is the first page in the paginator.
- def first?
- self == @paginator.first
- end
-
- # Returns true if this page is the last page in the paginator.
- def last?
- self == @paginator.last
- end
-
- # Returns a new Page object representing the page just before this
- # page, or nil if this is the first page.
- def previous
- first? ? nil : @paginator[@number - 1]
- end
-
- # Returns a new Page object representing the page just after this
- # page, or nil if this is the last page.
- def next
- last? ? nil : @paginator[@number + 1]
- end
-
- # Returns a new Window object for this page with the specified
- # +padding+.
- def window(padding = 2)
- Window.new(self, padding)
- end
-
- # Returns the limit/offset array for this page.
- def to_sql
- [@paginator.items_per_page, offset]
- end
-
- def to_param # :nodoc:
- @number.to_s
- end
- end
-
- # A class for representing ranges around a given page.
- class Window
- # Creates a new Window object for the given +page+ with the specified
- # +padding+.
- def initialize(page, padding = 2)
- @paginator = page.paginator
- @page = page
- self.padding = padding
- end
- attr_reader :paginator, :page, :padding, :first, :last
-
- # Sets the window's padding (the number of pages on either side of the
- # window page).
- def padding=(padding)
- @padding = padding.negative? ? 0 : padding
- # Find the beginning and end pages of the window
- @first = if @paginator.contains_page?(@page.number - @padding)
- @paginator[@page.number - @padding]
- else
- @paginator.first
- end
- @last = if @paginator.contains_page?(@page.number + @padding)
- @paginator[@page.number + @padding]
- else
- @paginator.last
- end
- end
-
- # Returns an array of Page objects in the current window.
- def pages
- (@first.number..@last.number).to_a.collect! { |n| @paginator[n] }
- end
- alias to_a pages
- end
- end
- end
-end
diff --git a/lib/classic_pagination/pagination_helper.rb b/lib/classic_pagination/pagination_helper.rb
deleted file mode 100644
index c450de4e14..0000000000
--- a/lib/classic_pagination/pagination_helper.rb
+++ /dev/null
@@ -1,169 +0,0 @@
-module ActionView
- module Helpers
- # Provides methods for linking to ActionController::Pagination objects using a simple generator API. You can optionally
- # also build your links manually using ActionView::Helpers::AssetHelper#link_to like so:
- #
- # <%= link_to "Previous page", { :page => paginator.current.previous } if paginator.current.previous %>
- # <%= link_to "Next page", { :page => paginator.current.next } if paginator.current.next %>
- module PaginationHelper
- unless const_defined?(:DEFAULT_OPTIONS)
- DEFAULT_OPTIONS = {
- :name => :page,
- :window_size => 2,
- :always_show_anchors => true,
- :link_to_current_page => false,
- :params => {}
- }.freeze
- end
-
- # Creates a basic HTML link bar for the given +paginator+. Links will be created
- # for the next and/or previous page and for a number of other pages around the current
- # pages position. The +html_options+ hash is passed to +link_to+ when the links are created.
- #
- # ==== Options
- # :name:: the routing name for this paginator
- # (defaults to +page+)
- # :prefix:: prefix for pagination links
- # (i.e. Older Pages: 1 2 3 4)
- # :suffix:: suffix for pagination links
- # (i.e. 1 2 3 4 <- Older Pages)
- # :window_size:: the number of pages to show around
- # the current page (defaults to 2)
- # :always_show_anchors:: whether or not the first and last
- # pages should always be shown
- # (defaults to +true+)
- # :link_to_current_page:: whether or not the current page
- # should be linked to (defaults to
- # +false+)
- # :params:: any additional routing parameters
- # for page URLs
- #
- # ==== Examples
- # # We'll assume we have a paginator setup in @person_pages...
- #
- # pagination_links(@person_pages)
- # # => 1 2 3 ... 10
- #
- # pagination_links(@person_pages, :link_to_current_page => true)
- # # => 1 2 3 ... 10
- #
- # pagination_links(@person_pages, :always_show_anchors => false)
- # # => 1 2 3
- #
- # pagination_links(@person_pages, :window_size => 1)
- # # => 1 2 ... 10
- #
- # pagination_links(@person_pages, :params => { :viewer => "flash" })
- # # => 1 2 3 ...
- # # 10
- def pagination_links(paginator, options = {}, html_options = {})
- name = options[:name] || DEFAULT_OPTIONS[:name]
- params = (options[:params] || DEFAULT_OPTIONS[:params]).clone
-
- prefix = options[:prefix] || ""
- suffix = options[:suffix] || ""
-
- pagination_links_each(paginator, options, prefix, suffix) do |n|
- params[name] = n
- link_to(n.to_s, params, html_options)
- end
- end
-
- # Iterate through the pages of a given +paginator+, invoking a
- # block for each page number that needs to be rendered as a link.
- #
- # ==== Options
- # :window_size:: the number of pages to show around
- # the current page (defaults to +2+)
- # :always_show_anchors:: whether or not the first and last
- # pages should always be shown
- # (defaults to +true+)
- # :link_to_current_page:: whether or not the current page
- # should be linked to (defaults to
- # +false+)
- #
- # ==== Example
- # # Turn paginated links into an Ajax call
- # pagination_links_each(paginator, page_options) do |link|
- # options = { :url => {:action => 'list'}, :update => 'results' }
- # html_options = { :href => url_for(:action => 'list') }
- #
- # link_to_remote(link.to_s, options, html_options)
- # end
- def pagination_links_each(paginator, options, prefix = nil, suffix = nil)
- options = DEFAULT_OPTIONS.merge(options)
- link_to_current_page = options[:link_to_current_page]
- always_show_anchors = options[:always_show_anchors]
-
- current_page = paginator.current_page
- window_pages = current_page.window(options[:window_size]).pages
- return unless link_to_current_page || window_pages.length > 1
-
- first = paginator.first
- last = paginator.last
-
- html = ""
-
- html << prefix if prefix
-
- if always_show_anchors && !(wp_first = window_pages[0]).first?
- html << yield(first.number)
- html << " ... " if wp_first.number - first.number > 1
- html << " "
- end
-
- window_pages.each do |page|
- html << if current_page == page && !link_to_current_page
- page.number.to_s
- else
- yield(page.number)
- end
- html << " "
- end
-
- if always_show_anchors && !(wp_last = window_pages[-1]).last?
- html << " ... " if last.number - wp_last.number > 1
- html << yield(last.number)
- end
-
- html << suffix if suffix
-
- html
- end
-
- def pagination_items(paginator, options)
- options = DEFAULT_OPTIONS.merge(options)
- link_to_current_page = options[:link_to_current_page]
- always_show_anchors = options[:always_show_anchors]
-
- current_page = paginator.current_page
- window_pages = current_page.window(options[:window_size]).pages
-
- first = paginator.first
- last = paginator.last
-
- items = []
-
- if always_show_anchors && !(wp_first = window_pages[0]).first?
- items.push [first.number.to_s, first]
- items.push ["...", "disabled"] if wp_first.number - first.number > 1
- end
-
- window_pages.each do |page|
- if current_page == page && !link_to_current_page
- items.push [page.number.to_s, "active"]
- else
- items.push [page.number.to_s, page]
- end
- end
-
- if always_show_anchors && !(wp_last = window_pages[-1]).last?
- items.push ["...", "disabled"] if last.number - wp_last.number > 1
- items.push [last.number.to_s, last]
- end
-
- items
- end
- end
- end
-end