module Pagy::Frontend

def self.deprecate(old_meth, new_meth)

def self.deprecate(old_meth, new_meth)
  send(:define_method, old_meth) do |pagy, id=pagy_id|
    Warning.warn "WARNING: The ##{old_meth} pagy helper method is deprecated and will be removed in 2.0; please use ##{new_meth} instead. More info at https://github.com/ddnexus/pagy/blob/master/DEPRECATIONS.md\n"
    method(new_meth).arity == 1 ? send(new_meth, pagy) : send(new_meth, pagy, id)
  end
end

def pagy_apply_init_tag(pagy, function, payload=pagy_serialized(pagy))

Multi purpose JSON tag for custom javascript initialization
def pagy_apply_init_tag(pagy, function, payload=pagy_serialized(pagy))
  pagy_json_tag(:applyInit, function, payload)
end

def pagy_bootstrap_compact_nav(pagy, id=pagy_id)

we use a numeric input tag to set the page and the Pagy.compact javascript to navigate
Compact pagination for bootstrap: it returns the html with the series of links to the pages
def pagy_bootstrap_compact_nav(pagy, id=pagy_id)
  html, link, p_prev, p_next, p_page, p_pages = +'', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
  html << %(<nav id="#{id}" class="pagy-nav-compact-bootstrap pagy-bootstrap-compact-nav pagination" role="navigation" aria-label="pager">)
    html << link.call(MARKER, '', %(style="display: none;" ))
    (html << link.call(1, '', %(style="display: none;" ))) if defined?(TRIM)
    html << %(<div class="btn-group" role="group">)
    html << (p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous" class="prev btn btn-primary"')
                    : %(<a class="prev btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.prev')}</a>))
    input = %(<input type="number" min="1" max="#{p_pages}" value="#{p_page}" class="text-primary" style="padding: 0; border: none; text-align: center; width: #{p_pages.to_s.length+1}rem;">)
    html << %(<div class="pagy-compact-input btn btn-primary disabled">#{pagy_t('pagy.compact', page_input: input, count: p_page, pages: p_pages)}</div>)
    html << (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'aria-label="next" class="next btn btn-primary"')
                    : %(<a class="next btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.next')}</a>))
  html << %(</div></nav>#{pagy_json_tag(:compact, id, MARKER, p_page, !!defined?(TRIM))})
end

def pagy_bootstrap_nav(pagy)

Pagination for bootstrap: it returns the html with the series of links to the pages
def pagy_bootstrap_nav(pagy)
  html, link, p_prev, p_next = +'', pagy_link_proc(pagy, 'class="page-link"'), pagy.prev, pagy.next
  html << (p_prev ? %(<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
                  : %(<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.prev')}</a></li>))
  pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    html << if    item.is_a?(Integer); %(<li class="page-item">#{link.call item}</li>)                                                               # page link
            elsif item.is_a?(String) ; %(<li class="page-item active">#{link.call item}</li>)                                                        # active page
            elsif item == :gap       ; %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap')}</a></li>) # page gap
            end
  end
  html << (p_next ? %(<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
                  : %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.next')}</a></li>))
  %(<nav class="pagy-nav-bootstrap pagy-bootstrap-nav pagination" role="navigation" aria-label="pager"><ul class="pagination">#{html}</ul></nav>)
end

def pagy_bootstrap_responsive_nav(pagy, id=pagy_id)

rendered by the Pagy.responsive javascript
Responsive pagination for bootstrap: it returns the html with the series of links to the pages
def pagy_bootstrap_responsive_nav(pagy, id=pagy_id)
  tags, link, p_prev, p_next, responsive = {}, pagy_link_proc(pagy, 'class="page-link"'), pagy.prev, pagy.next, pagy.responsive
  tags['before'] = +'<ul class="pagination">'
  tags['before'] << (p_prev ? %(<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
                            : %(<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.prev')}</a></li>))
  responsive[:items].each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    tags[item.to_s] = if    item.is_a?(Integer); %(<li class="page-item">#{link.call item}</li>)                                                        # page link
                      elsif item.is_a?(String) ; %(<li class="page-item active">#{link.call item}</li>)                                                 # active page
                      elsif item == :gap       ; %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap')}</a></li>) # page gap
                      end
  end
  tags['after'] = +(p_next ? %(<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
                           : %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.next')}</a></li>))
  tags['after'] << '</ul>'
  script = pagy_json_tag(:responsive, id, tags,  responsive[:widths], responsive[:series])
  %(<nav id="#{id}" class="pagy-nav-responsive-bootstrap pagy-bootstrap-responsive-nav pagination" role="navigation" aria-label="pager"></nav>#{script})
end

def pagy_bulma_compact_nav(pagy, id=pagy_id)

we use a numeric input tag to set the page and the Pagy.compact javascript to navigate
Compact pagination for Bulma: it returns the html with the series of links to the pages
def pagy_bulma_compact_nav(pagy, id=pagy_id)
  html, link, p_prev, p_next, p_page, p_pages = +'', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
  html << %(<nav id="#{id}" class="pagy-nav-compact-bulma pagy-bulma-compact-nav" role="navigation" aria-label="pagination">)
    html << link.call(MARKER, '', 'style="display: none;"')
    (html << link.call(1, '', %(style="display: none;"))) if defined?(TRIM)
    html << %(<div class="field is-grouped is-grouped-centered" role="group">)
    html << (p_prev ? %(<p class="control">#{link.call(p_prev, pagy_t('pagy.nav.prev'), 'class="button" aria-label="previous page"')}</p>)
                    : %(<p class="control"><a class="button" disabled>#{pagy_t('pagy.nav.prev')}</a></p>))
    input = %(<input class="input" type="number" min="1" max="#{p_pages}" value="#{p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length+1}rem; margin:0 0.3rem;">)
    html << %(<div class="pagy-compact-input control level is-mobile">#{pagy_t('pagy.compact', page_input: input, count: p_page, pages: p_pages)}</div>)
    html << (p_next ? %(<p class="control">#{link.call(p_next, pagy_t('pagy.nav.next'), 'class="button" aria-label="next page"')}</p>)
                    : %(<p class="control"><a class="button" disabled>#{pagy_t('pagy.nav.next')}</a></p>))
  html << %(</div></nav>#{pagy_json_tag(:compact, id, MARKER, p_page, !!defined?(TRIM))})
end

def pagy_bulma_nav(pagy)

Pagination for Bulma: it returns the html with the series of links to the pages
def pagy_bulma_nav(pagy)
  html, link, p_prev, p_next = +'', pagy_link_proc(pagy), pagy.prev, pagy.next
  html << (p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'), 'class="pagination-previous" aria-label="previous page"')
                  : %(<a class="pagination-previous" disabled>#{pagy_t('pagy.nav.prev')}</a>))
  html << (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'class="pagination-next" aria-label="next page"')
                  : %(<a class="pagination-next" disabled>#{pagy_t('pagy.nav.next')}</a>))
  html << '<ul class="pagination-list">'
  pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    html << if    item.is_a?(Integer); %(<li>#{link.call item, item, %(class="pagination-link" aria-label="goto page #{item}") }</li>)                           # page link
            elsif item.is_a?(String) ; %(<li>#{link.call item, item, %(class="pagination-link is-current" aria-label="page #{item}" aria-current="page")}</li>)  # active page
            elsif item == :gap       ; %(<li><span class="pagination-ellipsis">#{pagy_t('pagy.nav.gap')}</span></li>)                                            # page gap
            end
  end
  html << '</ul>'
  %(<nav class="pagy-nav-bulma pagy-bulma-nav pagination is-centered" role="navigation" aria-label="pagination">#{html}</nav>)
end

def pagy_bulma_responsive_nav(pagy, id=pagy_id)

rendered by the Pagy.responsive javascript
Responsive pagination for Bulma: it returns the html with the series of links to the pages
def pagy_bulma_responsive_nav(pagy, id=pagy_id)
  tags, link, p_prev, p_next, responsive = {}, pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.responsive
  tags['before'] = +(p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'), 'class="pagination-previous" aria-label="previous page"')
                            : %(<a class="pagination-previous" disabled>#{pagy_t('pagy.nav.prev')}</a>))
  tags['before'] << (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'class="pagination-next" aria-label="next page"')
                            : %(<a class="pagination-next" disabled>#{pagy_t('pagy.nav.next')}</a>))
  tags['before'] << '<ul class="pagination-list">'
  responsive[:items].each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    tags[item.to_s] = if    item.is_a?(Integer); %(<li>#{link.call item, item, %(class="pagination-link" aria-label="goto page #{item}")}</li>)
                      elsif item.is_a?(String) ; %(<li>#{link.call item, item, %(class="pagination-link is-current" aria-current="page" aria-label="page #{item}")}</li>)
                      elsif item == :gap       ; %(<li><span class="pagination-ellipsis">#{pagy_t('pagy.nav.gap')}</span></li>)
                      end
  end
  tags['after'] = '</ul>'
  script = pagy_json_tag(:responsive, id, tags,  responsive[:widths], responsive[:series])
  %(<nav id="#{id}" class="pagy-nav-responsive-bulma pagy-bulma-responsive-nav pagination is-centered" role="navigation" aria-label="pagination"></nav>#{script})
end

def pagy_foundation_compact_nav(pagy, id=pagy_id)

we use a numeric input tag to set the page and the Pagy.compact javascript to navigate
Compact pagination for Foundation: it returns the html with the series of links to the pages
def pagy_foundation_compact_nav(pagy, id=pagy_id)
  html, link, p_prev, p_next, p_page, p_pages = +'', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
  html << %(<nav id="#{id}" class="pagy-nav-compact-foundation pagy-foundation-compact-nav" role="navigation" aria-label="Pagination">)
    html << link.call(MARKER, '', %(style="display: none;" ))
    (html << link.call(1, '', %(style="display: none;" ))) if defined?(TRIM)
    html << %(<div class="input-group">)
    html << (p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'), 'style="margin-bottom: 0px;" aria-label="previous" class="prev button primary"')
                    : %(<a style="margin-bottom: 0px;" class="prev button primary disabled" href="#">#{pagy_t('pagy.nav.prev')}</a>))
    input = %(<input class="input-group-field cell shrink" type="number" min="1" max="#{p_pages}" value="#{p_page}" style="width: #{p_pages.to_s.length+1}rem; padding: 0 0.3rem; margin: 0 0.3rem;">)
    html << %(<span class="input-group-label">#{pagy_t('pagy.compact', page_input: input, count: p_page, pages: p_pages)}</span>)
    html << (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'style="margin-bottom: 0px;" aria-label="next" class="next button primary"')
                    : %(<a style="margin-bottom: 0px;" class="next button primary disabled" href="#">#{pagy_t('pagy.nav.next')}</a>))
  html << %(</div></nav>#{pagy_json_tag(:compact, id, MARKER, p_page, !!defined?(TRIM))})
end

def pagy_foundation_nav(pagy)

Pagination for Foundation: it returns the html with the series of links to the pages
def pagy_foundation_nav(pagy)
  html, link, p_prev, p_next = +'', pagy_link_proc(pagy), pagy.prev, pagy.next
  html << (p_prev ? %(<li class="prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
                  : %(<li class="prev disabled">#{pagy_t('pagy.nav.prev')}</li>))
  pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    html << if    item.is_a?(Integer); %(<li>#{link.call item}</li>)                        # page link
            elsif item.is_a?(String) ; %(<li class="current">#{item}</li>)                  # active page
            elsif item == :gap       ; %(<li class="ellipsis gap" aria-hidden="true"></li>) # page gap
            end
  end
  html << (p_next ? %(<li class="next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
                  : %(<li class="next disabled">#{pagy_t('pagy.nav.next')}</li>))
  %(<nav class="pagy-nav-foundation pagy-foundation-nav" role="navigation" aria-label="Pagination"><ul class="pagination">#{html}</ul></nav>)
end

def pagy_foundation_responsive_nav(pagy, id=pagy_id)

rendered by the Pagy.responsive javascript
Responsive pagination for Foundation: it returns the html with the series of links to the pages
def pagy_foundation_responsive_nav(pagy, id=pagy_id)
  tags, link, p_prev, p_next, responsive = {}, pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.responsive
  tags['before'] = +'<ul class="pagination">'
  tags['before'] << (p_prev ? %(<li class="prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
                            : %(<li class="prev disabled">#{pagy_t('pagy.nav.prev')}</li>))
  responsive[:items].each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    tags[item.to_s] = if    item.is_a?(Integer); %(<li>#{link.call item}</li>)                        # page link
                      elsif item.is_a?(String) ; %(<li class="current">#{item}</li>)                  # active page
                      elsif item == :gap       ; %(<li class="ellipsis gap" aria-hidden="true"></li>) # page gap
                      end
  end
  tags['after'] = +(p_next ? %(<li class="next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
                           : %(<li class="next disabled">#{pagy_t('pagy.nav.next')}</li>))
  tags['after'] << '</ul>'
  script = pagy_json_tag(:responsive, id, tags,  responsive[:widths], responsive[:series])
  %(<nav id="#{id}" class="pagy-nav-responsive-foundation pagy-foundation-responsive-nav" aria-label="Pagination"></nav>#{script})
end

def pagy_get_params(params) params end

Sub-method called only by #pagy_url_for: here for easy customization of params by overriding
def pagy_get_params(params) params end

def pagy_id

def pagy_id
  # SHA1 is the fastest on modern ruby
  "pagy-#{Digest::SHA1.hexdigest(caller(2..2)[0].split(':in')[0])}"
end

def pagy_info(pagy)

Return examples: "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
def pagy_info(pagy)
  name = pagy_t(pagy.vars[:item_path], count: pagy.count)
  path = pagy.pages == 1 ? 'pagy.info.single_page' : 'pagy.info.multiple_pages'
  pagy_t(path, item_name: name, count: pagy.count, from: pagy.from, to: pagy.to)
end

def pagy_items_selector(pagy, id=pagy_id)

Return the items selector HTML. For example "Show [20] items per page"
def pagy_items_selector(pagy, id=pagy_id)
  p_vars = pagy.vars; p_items = p_vars[:items]; p_vars[:items] = "#{MARKER}-items-"
  html = +%(<span id="#{id}">)
    html << %(<a href="#{pagy_url_for("#{MARKER}-page-", pagy)}"></a>)
    p_vars[:items] = p_items # restore the items
    input = %(<input type="number" min="1" max="#{p_vars[:max_items]}" value="#{p_items}" style="padding: 0; text-align: center; width: #{p_items.to_s.length+1}rem;">)
    html << %(#{pagy_t('pagy.items', items_input: input, count: p_items)})
  html << %(</span>#{pagy_json_tag(:items, id, MARKER, pagy.from)})
end

def pagy_json_tag(*args)

def pagy_json_tag(*args)
  %(<script type="application/json" class="pagy-json">#{args.to_json}</script>)
end

def pagy_link_proc(pagy, link_extra='')

Benchmarked on a 20 link nav: it is ~27x faster and uses ~13x less memory than rails' link_to
Returns a performance optimized proc to generate the HTML links
def pagy_link_proc(pagy, link_extra='')
  p_prev, p_next = pagy.prev, pagy.next
  a, b = %(<a href="#{pagy_url_for(MARKER, pagy)}" #{pagy.vars[:link_extra]} #{link_extra}).split(MARKER, 2)
  -> (n, text=n, extra='') { "#{a}#{n}#{b}#{ if    n == p_prev ; ' rel="prev"'
                                             elsif n == p_next ; ' rel="next"'
                                             else                           '' end } #{extra}>#{text}</a>" }
end

def pagy_link_proc_with_trim(pagy, link_extra='')

def pagy_link_proc_with_trim(pagy, link_extra='')
  p_prev, p_next, p_vars = pagy.prev, pagy.next, pagy.vars
  marker_url = pagy_url_for(MARKER, pagy)
  page1_url  = pagy_trim_url(marker_url, "#{p_vars[:page_param]}=#{MARKER}")
  page1_link = %(<a href="#{page1_url}" #{p_vars[:link_extra]} #{link_extra})
  a, b = %(<a href="#{marker_url}" #{p_vars[:link_extra]} #{link_extra}).split(MARKER, 2)
  -> (n, text=n, extra='') { start = n.to_i == 1 ? page1_link : "#{a}#{n}#{b}"
                             "#{start}#{ if    n == p_prev ; ' rel="prev"'
                                         elsif n == p_next ; ' rel="next"'
                                         else                           '' end } #{extra}>#{text}</a>" }
end

def pagy_materialize_compact_nav(pagy, id=pagy_id)

we use a numeric input tag to set the page and the Pagy.compact javascript to navigate
Compact pagination for materialize: it returns the html with the series of links to the pages
def pagy_materialize_compact_nav(pagy, id=pagy_id)
  html, link, p_prev, p_next, p_page, p_pages = +'', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
  html << %(<div id="#{id}" class="pagy-nav-compact-materialize pagy-materialize-compact-nav pagination" role="navigation" aria-label="pager">)
  html << link.call(MARKER, '', %(style="display: none;" ))
  (html << link.call(1, '', %(style="display: none;" ))) if defined?(TRIM)
  html << %(<div class="pagy-compact-chip role="group" style="height: 35px; border-radius: 18px; background: #e4e4e4; display: inline-block;">)
  html << '<ul class="pagination" style="margin: 0px;">'
  li_style = 'style="vertical-align: middle;"'
  html << (p_prev ? %(<li class="waves-effect prev" #{li_style}>#{link.call p_prev, '<i class="material-icons">chevron_left</i>', 'aria-label="previous"'}</li>)
           : %(<li class="prev disabled" #{li_style}><a href="#"><i class="material-icons">chevron_left</i></a></li>))
  input = %(<input type="number" class="browser-default" min="1" max="#{p_pages}" value="#{p_page}" style="padding: 2px; border: none; border-radius: 2px; text-align: center; width: #{p_pages.to_s.length+1}rem;">)
  html << %(<div class="pagy-compact-input btn-flat" style="cursor: default; padding: 0px">#{pagy_t('pagy.compact', page_input: input, count: p_page, pages: p_pages)}</div>)
  html << (p_next ? %(<li class="waves-effect next" #{li_style}>#{link.call p_next, '<i class="material-icons">chevron_right</i>', 'aria-label="next"'}</li>)
           : %(<li class="next disabled" #{li_style}><a href="#"><i class="material-icons">chevron_right</i></a></li>))
  html << %(</ul></div>#{pagy_json_tag(:compact, id, MARKER, p_page, !!defined?(TRIM))})
end

def pagy_materialize_nav(pagy)

Pagination for materialize: it returns the html with the series of links to the pages
def pagy_materialize_nav(pagy)
  html, link, p_prev, p_next = +'', pagy_link_proc(pagy), pagy.prev, pagy.next
  html << (p_prev ? %(<li class="waves-effect prev">#{link.call p_prev, '<i class="material-icons">chevron_left</i>', 'aria-label="previous"'}</li>)
                  : %(<li class="prev disabled"><a href="#"><i class="material-icons">chevron_left</i></a></li>))
  pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    html << if    item.is_a?(Integer); %(<li class="waves-effect">#{link.call item}</li>)                                             # page link
            elsif item.is_a?(String) ; %(<li class="active">#{link.call item}</li>)                                                   # active page
            elsif item == :gap       ; %(<li class="gap disabled"><a href="#">#{pagy_t('pagy.nav.gap')}</a></li>)   # page gap
            end
  end
  html << (p_next ? %(<li class="waves-effect next">#{link.call p_next, '<i class="material-icons">chevron_right</i>', 'aria-label="next"'}</li>)
                  : %(<li class="next disabled"><a href="#"><i class="material-icons">chevron_right</i></a></li>))
  %(<div class="pagy-nav-materialize pagy-materialize-nav pagination" role="navigation" aria-label="pager"><ul class="pagination">#{html}</ul></div>)
end

def pagy_materialize_responsive_nav(pagy, id=pagy_id)

rendered by the Pagy.responsive javascript
Responsive pagination for Materialize: it returns the html with the series of links to the pages
def pagy_materialize_responsive_nav(pagy, id=pagy_id)
  tags, link, p_prev, p_next, responsive = {}, pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.responsive
  tags['before'] = +'<ul class="pagination">'
  tags['before'] << (p_prev  ? %(<li class="waves-effect prev">#{link.call p_prev, '<i class="material-icons">chevron_left</i>', 'aria-label="previous"'}</li>)
                             : %(<li class="prev disabled"><a href="#"><i class="material-icons">chevron_left</i></a></li>))
  responsive[:items].each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    tags[item.to_s] = if    item.is_a?(Integer); %(<li class="waves-effect">#{link.call item}</li>)                           # page link
                      elsif item.is_a?(String) ; %(<li class="active">#{link.call item}</li>)                                 # active page
                      elsif item == :gap       ; %(<li class="gap disabled"><a href="#">#{pagy_t('pagy.nav.gap')}</a></li>)   # page gap
                      end
  end
  tags['after'] = +(p_next ? %(<li class="waves-effect next">#{link.call p_next, '<i class="material-icons">chevron_right</i>', 'aria-label="next"'}</li>)
                           : %(<li class="next disabled"><a href="#"><i class="material-icons">chevron_right</i></a></li>))
  tags['after'] << '</ul>'
  script = pagy_json_tag(:responsive, id, tags,  responsive[:widths], responsive[:series])
  %(<div id="#{id}" class="pagy-nav-responsive-materialize pagy-materialize-responsive-nav pagination" role="navigation" aria-label="pager"></div>#{script})
end

def pagy_nav(pagy)

Generic pagination: it returns the html with the series of links to the pages
def pagy_nav(pagy)
  html, link, p_prev, p_next = +'', pagy_link_proc(pagy), pagy.prev, pagy.next
  html << (p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
                  : %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> ))
  pagy.series.each do |item|  # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    html << if    item.is_a?(Integer); %(<span class="page">#{link.call item}</span> )               # page link
            elsif item.is_a?(String) ; %(<span class="page active">#{item}</span> )                  # current page
            elsif item == :gap       ; %(<span class="page gap">#{pagy_t('pagy.nav.gap')}</span> )   # page gap
            end
  end
  html << (p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
                  : %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>))
  %(<nav class="pagy-nav pagination" role="navigation" aria-label="pager">#{html}</nav>)
end

def pagy_next_link(pagy, text = pagy_t('pagy.nav.next'), link_extra = '')

def pagy_next_link(pagy, text = pagy_t('pagy.nav.next'), link_extra = '')
  pagy.next ? %(<span class="page next"><a href="#{pagy_next_url(pagy)}" rel="next" aria-label="next" #{pagy.vars[:link_extra]} #{link_extra}>#{text}</a></span>)
            : %(<span class="page next disabled">#{text}</span>)
end

def pagy_next_url(pagy)

def pagy_next_url(pagy)
  pagy_url_for(pagy.next, pagy) if pagy.next
end

def pagy_plain_compact_nav(pagy, id=pagy_id)

we use a numeric input tag to set the page and the Pagy.compact javascript to navigate
Plain compact pagination: it returns the html with the series of links to the pages
def pagy_plain_compact_nav(pagy, id=pagy_id)
  html, link, p_prev, p_next, p_page, p_pages = +'', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
  html << %(<nav id="#{id}" class="pagy-nav-compact pagy-plain-compact-nav pagination" role="navigation" aria-label="pager">)
    html << link.call(MARKER, '', %(style="display: none;" ))
    (html << link.call(1, '', %(style="display: none;" ))) if defined?(TRIM)
    html << (p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
                    : %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> ))
    input = %(<input type="number" min="1" max="#{p_pages}" value="#{p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length+1}rem;">)
    html << %(<span class="pagy-compact-input" style="margin: 0 0.6rem;">#{pagy_t('pagy.compact', page_input: input, count: p_page, pages: p_pages)}</span> )
    html << (p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
                    : %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>))
  html << %(</nav>#{pagy_json_tag(:compact, id, MARKER, p_page, !!defined?(TRIM))})
end

def pagy_plain_responsive_nav(pagy, id=pagy_id)

rendered by the Pagy.responsive javascript
Plain responsive pagination: it returns the html with the series of links to the pages
def pagy_plain_responsive_nav(pagy, id=pagy_id)
  tags, link, p_prev, p_next, responsive = {}, pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.responsive
  tags['before'] = (p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
                           : %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> ))
  responsive[:items].each do |item|  # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    tags[item.to_s] = if    item.is_a?(Integer); %(<span class="page">#{link.call item}</span> )             # page link
                      elsif item.is_a?(String) ; %(<span class="page active">#{item}</span> )                # current page
                      elsif item == :gap       ; %(<span class="page gap">#{pagy_t('pagy.nav.gap')}</span> ) # page gap
                      end
  end
  tags['after'] = (p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
                          : %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>))
  script = pagy_json_tag(:responsive, id, tags,  responsive[:widths], responsive[:series])
  %(<nav id="#{id}" class="pagy-nav-responsive pagy-plain-responsive-nav pagination" role="navigation" aria-label="pager"></nav>#{script})
end

def pagy_prev_link(pagy, text = pagy_t('pagy.nav.prev'), link_extra = '')

def pagy_prev_link(pagy, text = pagy_t('pagy.nav.prev'), link_extra = '')
  pagy.prev ? %(<span class="page prev"><a href="#{pagy_prev_url(pagy)}" rel="next" aria-label="next" #{pagy.vars[:link_extra]} #{link_extra}>#{text}</a></span>)
            : %(<span class="page prev disabled">#{text}</span>)
end

def pagy_prev_url(pagy)

def pagy_prev_url(pagy)
  pagy_url_for(pagy.prev, pagy) if pagy.prev
end

def pagy_semantic_compact_nav(pagy, id=pagy_id)

we use a numeric input tag to set the page and the Pagy.compact javascript to navigate
Compact pagination for semantic: it returns the html with the series of links to the pages
def pagy_semantic_compact_nav(pagy, id=pagy_id)
  html, link, p_prev, p_next, p_page, p_pages = +'', pagy_link_proc(pagy, 'class="item"'), pagy.prev, pagy.next, pagy.page, pagy.pages
  html << %(<div id="#{id}" class="pagy-nav-compact-semantic pagy-semantic-compact-nav ui compact menu" role="navigation" aria-label="pager">)
    html << link.call(MARKER, '', %(style="display: none;" ))
    (html << link.call(1, '', %(style="display: none;" ))) if defined?(TRIM)
    html << (p_prev ? %(#{link.call p_prev, '<i class="left small chevron icon"></i>', 'aria-label="previous"'})
                    : %(<div class="item disabled"><i class="left small chevron icon"></i></div>))
    input = %(<input type="number" min="1" max="#{p_pages}" value="#{p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length+1}rem; margin: 0 0.3rem">)
    html << %(<div class="pagy-compact-input item">#{pagy_t('pagy.compact', page_input: input, count: p_page, pages: p_pages)}</div> )
    html << (p_next ? %(#{link.call p_next, '<i class="right small chevron icon"></i>', 'aria-label="next"'})
                    : %(<div class="item disabled"><i class="right small chevron icon"></i></div>))
  html << %(</div>#{pagy_json_tag(:compact, id, MARKER, p_page, !!defined?(TRIM))})
end

def pagy_semantic_nav(pagy)

Pagination for semantic-ui: it returns the html with the series of links to the pages
def pagy_semantic_nav(pagy)
  html, link, p_prev, p_next = +'', pagy_link_proc(pagy, 'class="item"'), pagy.prev, pagy.next
  html << (p_prev ? %(#{link.call p_prev, '<i class="left small chevron icon"></i>', 'aria-label="previous"'})
                  : %(<div class="item disabled"><i class="left small chevron icon"></i></div>))
  pagy.series.each do |item|  # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    html << if    item.is_a?(Integer); %(#{link.call item})                      # page link
            elsif item.is_a?(String) ; %(<a class="item active">#{item}</a>)     # current page
            elsif item == :gap       ; %(<div class="disabled item">...</div>)   # page gap
            end
  end
  html << (p_next ? %(#{link.call p_next, '<i class="right small chevron icon"></i>', 'aria-label="next"'})
                  : %(<div class="item disabled"><i class="right small chevron icon"></i></div>))
  %(<div class="pagy-nav-semantic pagy-semantic-nav ui pagination menu" aria-label="pager">#{html}</div>)
end

def pagy_semantic_responsive_nav(pagy, id=pagy_id)

rendered by the Pagy.responsive javascript
Responsive pagination for semantic: it returns the html with the series of links to the pages
def pagy_semantic_responsive_nav(pagy, id=pagy_id)
  tags, link, p_prev, p_next, responsive = {}, pagy_link_proc(pagy, 'class="item"'), pagy.prev, pagy.next, pagy.responsive
  tags['before'] = (p_prev ? %(#{link.call p_prev, '<i class="left small chevron icon"></i>', 'aria-label="previous"'})
                           : %(<div class="item disabled"><i class="left small chevron icon"></i></div>))
  responsive[:items].each do |item|  # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
    tags[item.to_s] = if    item.is_a?(Integer); %(#{link.call item})                      # page link
                      elsif item.is_a?(String) ; %(<a class="item active">#{item}</a>)     # current page
                      elsif item == :gap       ; %(<div class="disabled item">...</div>)   # page gap
                      end
  end
  tags['after'] = (p_next ? %(#{link.call p_next, '<i class="right small chevron icon"></i>', 'aria-label="next"'})
                          : %(<div class="item disabled"><i class="right small chevron icon"></i></div>))
  script = pagy_json_tag(:responsive, id, tags,  responsive[:widths], responsive[:series])
  %(<div id="#{id}" class="pagy-nav-responsive-semantic pagy-semantic-responsive-nav ui pagination menu" role="navigation" aria-label="pager"></div>#{script})
end

def pagy_serialized(pagy)

def pagy_serialized(pagy)
  pagy.to_h.merge(prev_url: pagy_prev_url(pagy), next_url: pagy_next_url(pagy))
end

def pagy_t(*args)

Override the built-in pagy_t
def pagy_t(*args)
  ::I18n.t(*args)
end

def pagy_t(path, vars={})

See also https://ddnexus.github.io/pagy/extras/i18n if you need to use the standard I18n gem instead
It is specialized for Pagy and 5x faster than I18n.t (see https://ddnexus.github.io/pagy/api/frontend#i18n)
Similar to I18n.t for streamlined interpolation and pluralization but without dynamic translation.
def pagy_t(path, vars={})
  value = I18N[:data].dig(*path.split('.')) or return %(translation missing: "#{path}")
  if value.is_a?(Hash)
    vars.key?(:count) or return value
    plural = I18N[:plural].call(vars[:count])
    value.key?(plural) or return %(invalid pluralization data: "#{path}" cannot be used with count: #{vars[:count]}; key "#{plural}" is missing.)
    value = value[plural] or return %(translation missing: "#{path}")
  end
  sprintf value, Hash.new{|_,k| "%{#{k}}"}.merge!(vars)    # interpolation
end

def pagy_trim_url(url, param_string)

separate method easier to test
def pagy_trim_url(url, param_string)
  url.sub(/((?:[?&])#{param_string}\z|\b(?<=[?&])#{param_string}&)/, '')
end

def pagy_url_for(page, pagy)

This works with all Rack-based frameworks (Sinatra, Padrino, Rails, ...)
def pagy_url_for(page, pagy)
  p_vars = pagy.vars; params = request.GET.merge(p_vars[:page_param].to_s => page).merge!(p_vars[:params])
  "#{request.path}?#{Rack::Utils.build_nested_query(pagy_get_params(params))}#{p_vars[:anchor]}"
end

def pagy_url_for_with_items(page, pagy)

def pagy_url_for_with_items(page, pagy)
  p_vars = pagy.vars; params = request.GET.merge(p_vars[:page_param] => page, p_vars[:items_param] => p_vars[:items]).merge!(p_vars[:params])
  "#{request.path}?#{Rack::Utils.build_nested_query(pagy_get_params(params))}#{p_vars[:anchor]}"
end