root/websites/toror/trunk/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb @ 8678

Revision 8678, 8.0 kB (checked in by niku, 5 years ago)

toror

Line 
1require 'will_paginate/core_ext'
2
3module WillPaginate
4  # = Will Paginate view helpers
5  #
6  # Currently there is only one view helper: +will_paginate+. It renders the
7  # pagination links for the given collection. The helper itself is lightweight
8  # and serves only as a wrapper around link renderer instantiation; the
9  # renderer then does all the hard work of generating the HTML.
10  #
11  # == Global options for helpers
12  #
13  # Options for pagination helpers are optional and get their default values from the
14  # WillPaginate::ViewHelpers.pagination_options hash. You can write to this hash to
15  # override default options on the global level:
16  #
17  #   WillPaginate::ViewHelpers.pagination_options[:prev_label] = 'Previous page'
18  #
19  # By putting this into your environment.rb you can easily translate link texts to previous
20  # and next pages, as well as override some other defaults to your liking.
21  module ViewHelpers
22    # default options that can be overridden on the global level
23    @@pagination_options = {
24      :class        => 'pagination',
25      :prev_label   => '« Previous',
26      :next_label   => 'Next »',
27      :inner_window => 4, # links around the current page
28      :outer_window => 1, # links around beginning and end
29      :separator    => ' ', # single space is friendly to spiders and non-graphic browsers
30      :param_name   => :page,
31      :params       => nil,
32      :renderer     => 'WillPaginate::LinkRenderer',
33      :page_links   => true,
34      :container    => true
35    }
36    mattr_reader :pagination_options
37
38    # Renders Digg/Flickr-style pagination for a WillPaginate::Collection
39    # object. Nil is returned if there is only one page in total; no point in
40    # rendering the pagination in that case...
41    #
42    # ==== Options
43    # * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
44    # * <tt>:prev_label</tt> -- default: "« Previous"
45    # * <tt>:next_label</tt> -- default: "Next »"
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>: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 of the link renderer (default: WillPaginate::LinkRenderer)
53    # * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
54    # * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
55    #   false only when you are rendering your own pagination markup (default: true)
56    # * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID automatically
57    #   generated from the class name of objects in collection: for example, paginating
58    #   ArticleComment models would yield an ID of "article_comments_pagination".
59    #
60    # All options beside listed ones are passed as HTML attributes to the container
61    # element for pagination links (the DIV). For example:
62    #
63    #   <%= will_paginate @posts, :id => 'wp_posts' %>
64    #
65    # ... will result in:
66    #
67    #   <div class="pagination" id="wp_posts"> ... </div>
68    #
69    # ==== Using the helper without arguments
70    # If the helper is called without passing in the collection object, it will
71    # try to read from the instance variable inferred by the controller name.
72    # For example, calling +will_paginate+ while the current controller is
73    # PostsController will result in trying to read from the <tt>@posts</tt>
74    # variable. Example:
75    #
76    #   <%= will_paginate :id => true %>
77    #
78    # ... will result in <tt>@post</tt> collection getting paginated:
79    #
80    #   <div class="pagination" id="posts_pagination"> ... </div>
81    #
82    def will_paginate(collection = nil, options = {})
83      options, collection = collection, nil if collection.is_a? Hash
84      unless collection or !controller
85        collection_name = "@#{controller.controller_name}"
86        collection = instance_variable_get(collection_name)
87        raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
88          "forget to specify the collection object for will_paginate?" unless collection
89      end
90      # early exit if there is nothing to render
91      return nil unless collection.page_count > 1
92      options = options.symbolize_keys.reverse_merge WillPaginate::ViewHelpers.pagination_options
93      # create the renderer instance
94      renderer_class = options[:renderer].to_s.constantize
95      renderer = renderer_class.new collection, options, self
96      # render HTML for pagination
97      renderer.to_html
98    end
99  end
100
101  # This class does the heavy lifting of actually building the pagination
102  # links. It is used by +will_paginate+ helper internally, but avoid using it
103  # directly (for now) because its API is not set in stone yet.
104  class LinkRenderer
105
106    def initialize(collection, options, template)
107      @collection = collection
108      @options    = options
109      @template   = template
110    end
111
112    def to_html
113      links = @options[:page_links] ? windowed_paginator : []
114      # previous/next buttons
115      links.unshift page_link_or_span(@collection.previous_page, 'disabled', @options[:prev_label])
116      links.push    page_link_or_span(@collection.next_page,     'disabled', @options[:next_label])
117     
118      html = links.join(@options[:separator])
119      @options[:container] ? @template.content_tag(:div, html, html_attributes) : html
120    end
121
122    def html_attributes
123      return @html_attributes if @html_attributes
124      @html_attributes = @options.except *(WillPaginate::ViewHelpers.pagination_options.keys - [:class])
125      # pagination of Post models will have the ID of "posts_pagination"
126      if @options[:container] and @options[:id] === true
127        @html_attributes[:id] = @collection.first.class.name.underscore.pluralize + '_pagination'
128      end
129      @html_attributes
130    end
131   
132  protected
133
134    def gap_marker; '...'; end
135   
136    def windowed_paginator
137      inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
138      window_from = current_page - inner_window
139      window_to = current_page + inner_window
140     
141      # adjust lower or upper limit if other is out of bounds
142      if window_to > total_pages
143        window_from -= window_to - total_pages
144        window_to = total_pages
145      elsif window_from < 1
146        window_to += 1 - window_from
147        window_from = 1
148      end
149     
150      visible   = (1..total_pages).to_a
151      left_gap  = (2 + outer_window)...window_from
152      right_gap = (window_to + 1)...(total_pages - outer_window)
153      visible  -= left_gap.to_a  if left_gap.last - left_gap.first > 1
154      visible  -= right_gap.to_a if right_gap.last - right_gap.first > 1
155     
156      links, prev = [], nil
157
158      visible.each do |n|
159        # detect gaps:
160        links << gap_marker if prev and n > prev + 1
161        links << page_link_or_span(n)
162        prev = n
163      end
164     
165      links
166    end
167
168    def page_link_or_span(page, span_class = 'current', text = nil)
169      text ||= page.to_s
170      if page and page != current_page
171        @template.link_to text, url_options(page)
172      else
173        @template.content_tag :span, text, :class => span_class
174      end
175    end
176
177    def url_options(page)
178      options = { param_name => page }
179      # page links should preserve GET parameters
180      options = params.merge(options) if @template.request.get?
181      options.rec_merge!(@options[:params]) if @options[:params]
182      return options
183    end
184
185  private
186
187    def current_page
188      @collection.current_page
189    end
190
191    def total_pages
192      @collection.page_count
193    end
194
195    def param_name
196      @param_name ||= @options[:param_name].to_sym
197    end
198
199    def params
200      @params ||= @template.params.to_hash.symbolize_keys
201    end
202  end
203end
Note: See TracBrowser for help on using the browser.