require 'cgi' require 'will_paginate/core_ext' require 'will_paginate/view_helpers' require 'will_paginate/view_helpers/link_renderer_base' module WillPaginate module ViewHelpers # This class does the heavy lifting of actually building the pagination # links. It is used by +will_paginate+ helper internally. class LinkRenderer < LinkRendererBase # * +collection+ is a WillPaginate::Collection instance or any other object # that conforms to that API # * +options+ are forwarded from +will_paginate+ view helper # * +template+ is the reference to the template being rendered def prepare(collection, options, template) super(collection, options) @template = template @container_attributes = @base_url_params = nil end # Process it! This method returns the complete HTML string which contains # pagination links. Feel free to subclass LinkRenderer and change this # method as you see fit. def to_html html = pagination.map do |item| item.is_a?(Integer) ? page_number(item) : send(item) end.join(@options[:link_separator]) @options[:container] ? html_container(html) : html end # Returns the subset of +options+ this instance was initialized with that # represent HTML attributes for the container element of pagination links. def container_attributes @container_attributes ||= { :role => 'navigation', :"aria-label" => @template.will_paginate_translate(:container_aria_label) { 'Pagination' } }.update @options.except(*(ViewHelpers.pagination_options.keys + [:renderer] - [:class])) end protected def page_number(page) aria_label = @template.will_paginate_translate(:page_aria_label, :page => page.to_i) { "Page #{page}" } if page == current_page tag(:em, page, :class => 'current', :"aria-label" => aria_label, :"aria-current" => 'page') else link(page, page, :rel => rel_value(page), :"aria-label" => aria_label) end end def gap text = @template.will_paginate_translate(:page_gap) { '…' } %(#{text}) end def previous_page num = @collection.current_page > 1 && @collection.current_page - 1 aria_label = @template.will_paginate_translate(:previous_aria_label) { "Previous page" } previous_or_next_page(num, @options[:previous_label], 'previous_page', aria_label) end def next_page num = @collection.current_page < total_pages && @collection.current_page + 1 aria_label = @template.will_paginate_translate(:next_aria_label) { "Next page" } previous_or_next_page(num, @options[:next_label], 'next_page', aria_label) end def previous_or_next_page(page, text, classname, aria_label = nil) if page link(text, page, :class => classname, :'aria-label' => aria_label) else tag(:span, text, :class => classname + ' disabled', :'aria-label' => aria_label) end end def html_container(html) tag(:div, html, container_attributes) end # Returns URL params for +page_link_or_span+, taking the current GET params # and :params option into account. def url(page) raise NotImplementedError end private def param_name @options[:param_name].to_s end def link(text, target, attributes = {}) if target.is_a?(Integer) attributes[:rel] = rel_value(target) target = url(target) end attributes[:href] = target tag(:a, text, attributes) end def tag(name, value, attributes = {}) string_attributes = attributes.map do |pair| unless pair.last.nil? %( #{pair.first}="#{CGI::escapeHTML(pair.last.to_s)}") end end "<#{name}#{string_attributes.compact.join("")}>#{value}" end def rel_value(page) case page when @collection.current_page - 1; 'prev' when @collection.current_page + 1; 'next' end end def symbolized_update(target, other, blacklist = nil) other.each_pair do |key, value| key = key.to_sym existing = target[key] next if blacklist && blacklist.include?(key) if value.respond_to?(:each_pair) and (existing.is_a?(Hash) or existing.nil?) symbolized_update(existing || (target[key] = {}), value) else target[key] = value end end end end end end