Matts asked about page set code, and use.perl.org decided that my reply was sufficiently compressible that it probably wasn't meaninfull content... so I'm posting it here to see if I'm allowed to put meaningless things in my own journal ;)
-matt
Given the size of a current result set (number of hits), the size of our result
pages (ie: showing a list of ten hits per page), and the number of our current
page (zero indexed: ie 0 for the first page in the result set), and the initial
URL (to which the zero-indexed page numbers will be appeneded)... Returns HTML for
links allowing the user to page through the result set. The links will include
a 'previous' and 'next' link where applicable, and will include links to jump
to up to eight other result pages by page number. Generally, the script will
prefer to show four pages before and four after, but for pages near either end
of the result set, that view may be shifted to continue showing eight other
pages.
For a general idea, here's a textual representation of different output for
all of the pages in a twenty-page result set (_underlined_ items are links
and whitespace is only added to hilight patterns in the output)
_next_ page[ 1 _2_ _3_ _4_ _5_ _6_ _7_ _8_ _9..._ ]
_prev_ , _next_ page[ _1_ 2 _3_ _4_ _5_ _6_ _7_ _8_ _9..._ ]
_prev_ , _next_ page[ _1_ _2_ 3 _4_ _5_ _6_ _7_ _8_ _9..._ ]
_prev_ , _next_ page[ _1_ _2_ _3_ 4 _5_ _6_ _7_ _8_ _9..._ ]
_prev_ , _next_ page[ _1_ _2_ _3_ _4_ 5 _6_ _7_ _8_ _9..._ ]
_prev_ , _next_ page[ _...2_ _3_ _4_ _5_ 6 _7_ _8_ _9_ _10..._ ]
_prev_ , _next_ page[ _...3_ _4_ _5_ _6_ 7 _8_ _9_ _10_ _11..._ ]
_prev_ , _next_ page[ _...4_ _5_ _6_ _7_ 8 _9_ _10_ _11_ _12..._ ]
_prev_ , _next_ page[ _...5_ _6_ _7_ _8_ 9 _10_ _11_ _12_ _13..._ ]
_prev_ , _next_ page[ _...6_ _7_ _8_ _9_ 10 _11_ _12_ _13_ _14..._ ]
_prev_ , _next_ page[ _...7_ _8_ _9_ _10_ 11 _12_ _13_ _14_ _15..._ ]
_prev_ , _next_ page[ _...8_ _9_ _10_ _11_ 12 _13_ _14_ _15_ _16..._ ]
_prev_ , _next_ page[ _...9_ _10_ _11_ _12_ 13 _14_ _15_ _16_ _17..._ ]
_prev_ , _next_ page[ _...10_ _11_ _12_ _13_ 14 _15_ _16_ _17_ _18..._ ]
_prev_ , _next_ page[ _...11_ _12_ _13_ _14_ 15 _16_ _17_ _18_ _19..._ ]
_prev_ , _next_ page[ _...12_ _13_ _14_ _15_ 16 _17_ _18_ _19_ _20_ ]
_prev_ , _next_ page[ _...12_ _13_ _14_ _15_ _16_ 17 _18_ _19_ _20_ ]
_prev_ , _next_ page[ _...12_ _13_ _14_ _15_ _16_ _17_ 18 _19_ _20_ ]
_prev_ , _next_ page[ _...12_ _13_ _14_ _15_ _16_ _17_ _18_ 19 _20_ ]
_prev_ page[ _...12_ _13_ _14_ _15_ _16_ _17_ _18_ _19_ 20 ]
Note that the output reflects one-based indices even though the input
page number is zero-based and the href values will reference zero-based
indices. Also note that the url prefix arg should be HTML escaped if it
needs it. This method just slaps an int on the end and considers the
result to be valid for an href value.
For sufficiently small result sets, fewer than eight links may be shown.
For results sets having only three pages, only the prev/next links need to be
shown for the second page (as numbered 1/3 links are overly redundant). Result
sets having only (one or) two pages need not show numbered pages links either.
Input is not validated. Caller is expected to have ensured sensible args.
The set size must be positive. The page size must be positive. The current
page must be non-negative and less than number of pages (which is calculated
from the set size and page size... and note that 'less than' is accurate
because the current page is zero-indexed).
=cut
sub prepare_resultset_paging {
my( $set_size, $page_size, $curr_page, $url ) = @_;
my $margin = 4; # in the midst of a large list, keep this many links on
# either side of our current page. Shift things about if we're near
# either end of our large list to keep 2*$margin links available
# for sufficiently short lists, the margin becomes less relevant
# and we just show as many links as there are
# determine how many pages are in the result set
my $num_pages = int( $set_size / $page_size );
$num_pages++ if $set_size % $page_size;
my $max_page = $num_pages - 1; # zero-indexed
return '' if $curr_page > $max_page; # docs warn that caller shouldn't do this
my $l_margin = $margin;
my $r_margin = $margin;
if ( $curr_page < $l_margin ) {
my $diff = $l_margin - $curr_page;
$l_margin -= $diff;
$r_margin += $diff;
}
if ( $curr_page + $r_margin > $max_page ) {
my $diff = $curr_page + $r_margin - $max_page;
$r_margin -= $diff;
while ( $diff-- and ( $curr_page-$l_margin > 0) ) {
$l_margin++;
}
}
my @window = ($curr_page - $l_margin) .. ($curr_page + $r_margin);
my( $win_links, $prev, $next ) = ( '','','' );
if ( @window > 3 or (@window == 3 and $curr_page != 1) ) {
$win_links = join( ' ', map {
$_ == $curr_page
? qq{@{[$_+1]}}
: qq{@{[$_+1]}}
} @window);
$win_links =~ s{^(]+>)}{$1...} if $window[0];
$win_links =~ s{\z}{...} if $window[-1] < $max_page;
$win_links = "; page[ $win_links ]";
}
$prev = qq{previous page} if $curr_page;
$next = qq{next page} if $curr_page < $max_page;
return join( ', ', grep {$_} ($prev, $next) ).$win_links
}