| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Data::SpreadPagination; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 42650 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 42 |  | 
| 4 | 1 |  |  | 1 |  | 6 | use Carp; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 94 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 1 |  |  | 1 |  | 1078 | use Data::Page; | 
|  | 1 |  |  |  |  | 12296 |  | 
|  | 1 |  |  |  |  | 12 |  | 
| 7 | 1 |  |  | 1 |  | 1848 | use POSIX qw(ceil floor); | 
|  | 1 |  |  |  |  | 18258 |  | 
|  | 1 |  |  |  |  | 10 |  | 
| 8 | 1 |  |  | 1 |  | 5053 | use Math::Round qw(round); | 
|  | 1 |  |  |  |  | 4776 |  | 
|  | 1 |  |  |  |  | 239 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 1 |  |  | 1 |  | 9 | use vars qw(@ISA $VERSION); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 1441 |  | 
| 11 |  |  |  |  |  |  | @ISA = qw(Data::Page); | 
| 12 |  |  |  |  |  |  | $VERSION = '0.1.2'; | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | =head1 NAME | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | Data::SpreadPagination - Page numbering and spread pagination | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | use Data::SpreadPagination; | 
| 21 |  |  |  |  |  |  | my $pageInfo = Data::SpreadPagination->new({ | 
| 22 |  |  |  |  |  |  | totalEntries      => $totalEntries, | 
| 23 |  |  |  |  |  |  | entriesPerPage    => $entriesPerPage, | 
| 24 |  |  |  |  |  |  | # Optional, will use defaults otherwise. | 
| 25 |  |  |  |  |  |  | # only 1 of currentPage / startEntry can be provided. | 
| 26 |  |  |  |  |  |  | currentPage       => $currentPage, | 
| 27 |  |  |  |  |  |  | startEntry        => $startEntry, | 
| 28 |  |  |  |  |  |  | maxPages          => $maxPages, | 
| 29 |  |  |  |  |  |  | }); | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | # General page information | 
| 32 |  |  |  |  |  |  | print "         First page: ", $pageInfo->first_page, "\n"; | 
| 33 |  |  |  |  |  |  | print "          Last page: ", $pageInfo->last_page, "\n"; | 
| 34 |  |  |  |  |  |  | print "          Next page: ", $pageInfo->next_page, "\n"; | 
| 35 |  |  |  |  |  |  | print "      Previous page: ", $pageInfo->previous_page, "\n"; | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | # Results on current page | 
| 38 |  |  |  |  |  |  | print "First entry on page: ", $pageInfo->first, "\n"; | 
| 39 |  |  |  |  |  |  | print " Last entry on page: ", $pageInfo->last, "\n"; | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | # Page range information | 
| 42 |  |  |  |  |  |  | my $pageRanges = $pageInfo->page_ranges; | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | # Print out the page spread | 
| 45 |  |  |  |  |  |  | foreach my $page ($pageInfo->pages_in_spread()) { | 
| 46 |  |  |  |  |  |  | if (!defined $page) { | 
| 47 |  |  |  |  |  |  | print "... "; | 
| 48 |  |  |  |  |  |  | } elsif ($page == $pageInfo->current_page) { | 
| 49 |  |  |  |  |  |  | print "$page "; | 
| 50 |  |  |  |  |  |  | } else { | 
| 51 |  |  |  |  |  |  | print "$page "; | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  | } | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | The object produced by Data::SpreadPagination can be used to create | 
| 58 |  |  |  |  |  |  | a spread pagination navigator. It inherits from Data::Page, and has | 
| 59 |  |  |  |  |  |  | access to all of the methods from this object. | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | In addition, it also provides methods for creating a pagination spread, | 
| 62 |  |  |  |  |  |  | to allow for keeping the number of pagenumbers displayed within a sensible | 
| 63 |  |  |  |  |  |  | limit, but at the same time allowing easy navigation. | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | The object can easily be passed to a templating system | 
| 66 |  |  |  |  |  |  | such as Template Toolkit or be used within a script. | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | =head1 METHODS | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | =head2 new() | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | my $pageInfo = Data::SpreadPagination->new({ | 
| 73 |  |  |  |  |  |  | totalEntries      => $totalEntries, | 
| 74 |  |  |  |  |  |  | entriesPerPage    => $entriesPerPage, | 
| 75 |  |  |  |  |  |  | # Optional, will use defaults otherwise. | 
| 76 |  |  |  |  |  |  | # only 1 of currentPage / startEntry can be provided. | 
| 77 |  |  |  |  |  |  | currentPage       => $currentPage, | 
| 78 |  |  |  |  |  |  | startEntry        => $startEntry, | 
| 79 |  |  |  |  |  |  | maxPages          => $maxPages, | 
| 80 |  |  |  |  |  |  | }); | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | This is the constructor of the object. It requires an anonymous | 
| 83 |  |  |  |  |  |  | hash containing the 'totalEntries', how many data units you have, | 
| 84 |  |  |  |  |  |  | and the number of 'entriesPerPage' to display. Optionally the | 
| 85 |  |  |  |  |  |  | 'currentPage' / 'startEntry' (defaults to page/entry 1) and | 
| 86 |  |  |  |  |  |  | 'maxPages' (how many pages to display in addition to the current | 
| 87 |  |  |  |  |  |  | page) can be added. | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | =cut | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | sub new { | 
| 92 | 59 |  |  | 59 | 1 | 44355 | my $class = shift; | 
| 93 | 59 |  |  |  |  | 92 | my %params = %{shift()}; | 
|  | 59 |  |  |  |  | 327 |  | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 59 | 100 | 100 |  |  | 960 | croak "totalEntries and entriesPerPage must be supplied" | 
| 96 |  |  |  |  |  |  | unless defined $params{totalEntries} and defined $params{entriesPerPage}; | 
| 97 |  |  |  |  |  |  |  | 
| 98 | 56 | 100 | 100 |  |  | 431 | croak "currentPage and startEntry can not both be supplied" | 
| 99 |  |  |  |  |  |  | if defined $params{currentPage} and defined $params{startEntry}; | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 55 | 100 | 100 |  |  | 148 | $params{currentPage} = 1 | 
| 102 |  |  |  |  |  |  | unless defined $params{currentPage} or defined $params{startEntry}; | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 55 | 100 |  |  |  | 203 | $params{currentPage} = int( ($params{startEntry} - 1) / $params{entriesPerPage} ) + 1 | 
| 105 |  |  |  |  |  |  | if defined $params{startEntry}; | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 55 |  |  |  |  | 413 | my $self = $class->SUPER::new($params{totalEntries}, $params{entriesPerPage}, $params{currentPage}); | 
| 108 |  |  |  |  |  |  |  | 
| 109 | 55 | 100 |  |  |  | 2183 | $params{maxPages} = ceil( $params{totalEntries} / $params{entriesPerPage} ) - 1 | 
| 110 |  |  |  |  |  |  | unless defined $params{maxPages}; | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 55 |  |  |  |  | 120 | $self->{MAX_PAGES} = $params{maxPages}; | 
| 113 | 55 |  |  |  |  | 169 | $self->_do_pagination( @params{qw(totalEntries entriesPerPage currentPage maxPages)} ); | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 55 |  |  |  |  | 178 | return $self; | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | =head2 max_pages() | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | print "Maximum additional pages to display is ", $pageInfo->max_pages(), "\n"; | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | This method returns the maximum number of pages that are included in the | 
| 123 |  |  |  |  |  |  | spread pagination in addition to the current page. | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | =cut | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | sub max_pages { | 
| 128 | 56 |  |  | 56 | 1 | 1019 | my $self = shift; | 
| 129 |  |  |  |  |  |  |  | 
| 130 | 56 |  |  |  |  | 119 | return $self->{MAX_PAGES}; | 
| 131 |  |  |  |  |  |  | } | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | =head2 page_ranges() | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | $ranges = $pageInfo->page_ranges(); | 
| 136 |  |  |  |  |  |  | for my $qtr (1..4) { | 
| 137 |  |  |  |  |  |  | my $range = $ranges->[$qtr-1]; | 
| 138 |  |  |  |  |  |  | if (defined $range) { | 
| 139 |  |  |  |  |  |  | print "Qtr $qtr: no pages\n"; | 
| 140 |  |  |  |  |  |  | } else { | 
| 141 |  |  |  |  |  |  | print "Qtr $qtr: pages " . $range->[0] . " to " . $range->[1] . "\n"; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  | } | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | This method returns either an array or an arrayref (based upon context) | 
| 146 |  |  |  |  |  |  | of the page ranges for each of the four quarters in the spread. Each range | 
| 147 |  |  |  |  |  |  | is either undef for an empty quarter, or an array of the lower and upper | 
| 148 |  |  |  |  |  |  | pages in the range. | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | =cut | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | sub page_ranges { | 
| 153 | 100 |  |  | 100 | 1 | 25633 | my $self = shift; | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 100 | 100 |  |  |  | 274 | return wantarray ? @{ $self->{PAGE_RANGES} } : $self->{PAGE_RANGES}; | 
|  | 50 |  |  |  |  | 480 |  | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | =head2 pages_in_spread_raw() | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | # Print out the page spread | 
| 161 |  |  |  |  |  |  | foreach my $page ($pageInfo->pages_in_spread_raw()) { | 
| 162 |  |  |  |  |  |  | if ($page == $pageInfo->current_page) { | 
| 163 |  |  |  |  |  |  | print "$page "; | 
| 164 |  |  |  |  |  |  | } else { | 
| 165 |  |  |  |  |  |  | print "$page "; | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | This method returns either an array or an arrayref (based upon context) | 
| 170 |  |  |  |  |  |  | of purely the page numbers within the spread. | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | =cut | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | sub pages_in_spread_raw { | 
| 175 | 100 |  |  | 100 | 1 | 26547 | my $self = shift; | 
| 176 | 100 |  |  |  |  | 161 | my $pages = []; | 
| 177 |  |  |  |  |  |  |  | 
| 178 | 100 |  |  |  |  | 216 | for (0..3) { | 
| 179 | 400 | 100 |  |  |  | 5884 | push @$pages, $self->{PAGE_RANGES}[$_][0] .. $self->{PAGE_RANGES}[$_][1] | 
| 180 |  |  |  |  |  |  | if defined $self->{PAGE_RANGES}[$_]; | 
| 181 |  |  |  |  |  |  |  | 
| 182 | 400 | 100 |  |  |  | 1212 | push @$pages, $self->current_page() | 
| 183 |  |  |  |  |  |  | if $_ == 1; | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  |  | 
| 186 | 100 | 100 |  |  |  | 472 | return wantarray ? @{ $pages } : $pages; | 
|  | 50 |  |  |  |  | 363 |  | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =head2 pages_in_spread() | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | # Print out the page spread | 
| 192 |  |  |  |  |  |  | foreach my $page ($pageInfo->pages_in_spread()) { | 
| 193 |  |  |  |  |  |  | if (!defined $page) { | 
| 194 |  |  |  |  |  |  | print "... "; | 
| 195 |  |  |  |  |  |  | } elsif ($page == $pageInfo->current_page) { | 
| 196 |  |  |  |  |  |  | print "$page "; | 
| 197 |  |  |  |  |  |  | } else { | 
| 198 |  |  |  |  |  |  | print "$page "; | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | This method returns either an array or an arrayref (based upon context) | 
| 203 |  |  |  |  |  |  | of the page numbers within the spread. Breaks in the sequence are | 
| 204 |  |  |  |  |  |  | indicated with undef's. | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | =cut | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | sub pages_in_spread { | 
| 209 | 100 |  |  | 100 | 1 | 24590 | my $self = shift; | 
| 210 | 100 |  |  |  |  | 147 | my $ranges = $self->{PAGE_RANGES}; | 
| 211 | 100 |  |  |  |  | 151 | my $pages = []; | 
| 212 |  |  |  |  |  |  |  | 
| 213 | 100 | 100 |  |  |  | 284 | if (!defined $ranges->[0]) { | 
| 214 | 44 | 100 |  |  |  | 118 | push @$pages, undef if $self->current_page > 1; | 
| 215 |  |  |  |  |  |  | } else { | 
| 216 | 56 |  |  |  |  | 161 | push @$pages, $ranges->[0][0] .. $ranges->[0][1]; | 
| 217 | 56 | 100 | 100 |  |  | 280 | push @$pages, undef if defined $ranges->[1] and ($ranges->[1][0] - $ranges->[0][1]) > 1; | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  |  | 
| 220 | 100 | 100 |  |  |  | 2118 | push @$pages, $ranges->[1][0] .. $ranges->[1][1] if defined $ranges->[1]; | 
| 221 | 100 |  |  |  |  | 245 | push @$pages, $self->current_page; | 
| 222 | 100 | 100 |  |  |  | 4412 | push @$pages, $ranges->[2][0] .. $ranges->[2][1] if defined $ranges->[2]; | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 100 | 100 |  |  |  | 182 | if (!defined $ranges->[3]) { | 
| 225 | 44 | 100 |  |  |  | 115 | push @$pages, undef if $self->current_page < $self->last_page; | 
| 226 |  |  |  |  |  |  | } else { | 
| 227 | 56 | 100 | 100 |  |  | 222 | push @$pages, undef if defined $ranges->[2] and ($ranges->[3][0] - $ranges->[2][1]) > 1; | 
| 228 | 56 |  |  |  |  | 236 | push @$pages, $ranges->[3][0] .. $ranges->[3][1]; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 | 100 | 100 |  |  |  | 2749 | return wantarray ? @{ $pages } : $pages; | 
|  | 50 |  |  |  |  | 162 |  | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | # Carry out the pagination calculations | 
| 235 |  |  |  |  |  |  | # Algorithm description reverse-engineered from Squirrelmail | 
| 236 |  |  |  |  |  |  | # Reimplemented from description only by Alex Gough | 
| 237 |  |  |  |  |  |  | sub _do_pagination { | 
| 238 | 55 |  |  | 55 |  | 110 | my $self= shift; | 
| 239 | 55 |  |  |  |  | 132 | my $total_entries = $self->total_entries; | 
| 240 | 55 |  |  |  |  | 477 | my $entries_per_page = $self->entries_per_page; | 
| 241 | 55 |  |  |  |  | 478 | my $current_page = $self->current_page; | 
| 242 | 55 |  |  |  |  | 2179 | my $max_pages = $self->max_pages; | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | # qNsizes | 
| 245 | 55 |  |  |  |  | 85 | my @q_size = (); | 
| 246 | 55 |  |  |  |  | 61 | my ($add_pages, $adj); | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | # step 2 | 
| 249 | 55 |  |  |  |  | 263 | my $total_pages = ceil($total_entries / $entries_per_page); | 
| 250 | 55 | 100 |  |  |  | 200 | my $visible_pages = $max_pages < ($total_pages-1) | 
| 251 |  |  |  |  |  |  | ? $max_pages | 
| 252 |  |  |  |  |  |  | : $total_pages - 1; | 
| 253 | 55 | 100 |  |  |  | 113 | if ($total_pages - 1 <= $max_pages) { | 
| 254 | 15 |  |  |  |  | 39 | @q_size = ($current_page - 1, 0, 0, $total_pages - $current_page); | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  | else { | 
| 257 | 40 |  |  |  |  | 199 | @q_size = (floor($visible_pages / 4), | 
| 258 |  |  |  |  |  |  | round($visible_pages / 4), | 
| 259 |  |  |  |  |  |  | ceil($visible_pages / 4), | 
| 260 |  |  |  |  |  |  | round( ($visible_pages - round($visible_pages/4) )/3) ); | 
| 261 | 40 | 100 |  |  |  | 1059 | if ($current_page - $q_size[0] < 1) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 262 | 2 |  |  |  |  | 6 | $add_pages = $q_size[0] + $q_size[1] - $current_page +1; | 
| 263 | 2 |  |  |  |  | 18 | @q_size = ($current_page -1, 0, $q_size[2] + ceil($add_pages/2), | 
| 264 |  |  |  |  |  |  | $q_size[3] + floor($add_pages /2)); | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  | elsif ($current_page - $q_size[1] - ceil($q_size[1] / 3)<=$q_size[0]) { | 
| 267 | 6 |  |  |  |  | 17 | $adj = ceil((3*($current_page - $q_size[0] - 1))/4); | 
| 268 | 6 |  |  |  |  | 9 | $add_pages = $q_size[1] - $adj; | 
| 269 | 6 |  |  |  |  | 35 | @q_size = ($q_size[0], $adj, $q_size[2] + ceil($add_pages/2), | 
| 270 |  |  |  |  |  |  | $q_size[3] + floor($add_pages/2)); | 
| 271 |  |  |  |  |  |  | } | 
| 272 |  |  |  |  |  |  | elsif ($current_page + $q_size[3] >= $total_pages) { | 
| 273 | 5 |  |  |  |  | 12 | $add_pages = $q_size[2] + $q_size[3] - $total_pages +$current_page; | 
| 274 | 5 |  |  |  |  | 26 | @q_size = ($q_size[0] + floor($add_pages / 2), | 
| 275 |  |  |  |  |  |  | $q_size[1] + ceil($add_pages / 2), 0, | 
| 276 |  |  |  |  |  |  | $total_pages - $current_page); | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  | elsif ($current_page + $q_size[2] >= $total_pages - $q_size[3]) { | 
| 279 | 4 |  |  |  |  | 15 | $adj = ceil((3*($total_pages - $current_page - $q_size[3]))/4); | 
| 280 | 4 |  |  |  |  | 5 | $add_pages = $q_size[2] - $adj; | 
| 281 | 4 |  |  |  |  | 20 | @q_size = ($q_size[0] + floor($add_pages/2), | 
| 282 |  |  |  |  |  |  | $q_size[1] + ceil($add_pages/2), $adj, $q_size[3]); | 
| 283 |  |  |  |  |  |  | } | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  | # step 3 (PROFIT) | 
| 286 | 55 | 100 |  |  |  | 464 | $self->{PAGE_RANGES} = [ $q_size[0] == 0 ? undef | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | : [1,$q_size[0]], | 
| 288 |  |  |  |  |  |  | $q_size[1] == 0 ? undef | 
| 289 |  |  |  |  |  |  | : [$current_page - $q_size[1], $current_page-1], | 
| 290 |  |  |  |  |  |  | $q_size[2] == 0 ? undef | 
| 291 |  |  |  |  |  |  | : [$current_page+1, $current_page+$q_size[2]], | 
| 292 |  |  |  |  |  |  | $q_size[3] == 0 ? undef | 
| 293 |  |  |  |  |  |  | : [$total_pages - $q_size[3] + 1, $total_pages], | 
| 294 |  |  |  |  |  |  | ]; | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | } | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | =head1 BUGS | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | Hopefully there aren't any nasty bugs lurking in here anywhere. | 
| 301 |  |  |  |  |  |  | However, if you do find one, please report it via RT. | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | =head1 ALGORITHM | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | The algorithm used to create the pagination spread was reverse-engineered | 
| 306 |  |  |  |  |  |  | out of Squirrelmail by myself, and then reimplemented from description | 
| 307 |  |  |  |  |  |  | only by Alex Gough. | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | =head1 THANKS, MANY | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | Alex Gough for implementing the central algorithm from my description. | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | =head1 AUTHOR | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | Jody Belka C | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | L. | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | Copyright 2004 by Jody Belka | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify | 
| 326 |  |  |  |  |  |  | it under the same terms as Perl itself. | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | =cut | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | < 
 | 
| 331 |  |  |  |  |  |  | Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about | 
| 332 |  |  |  |  |  |  | being on some really good drugs -- you know, there is no spoon. - flyingmoose | 
| 333 |  |  |  |  |  |  | QUOTE |