File Coverage

blib/lib/Mojolicious/Plugin/TagHelpers/Pagination.pm
Criterion Covered Total %
statement 110 113 97.3
branch 57 66 86.3
condition 47 58 81.0
subroutine 9 9 100.0
pod 2 3 66.6
total 225 249 90.3


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::TagHelpers::Pagination;
2 1     1   1057 use Mojo::Base 'Mojolicious::Plugin';
  1         2  
  1         9  
3 1     1   213 use Mojo::ByteStream 'b';
  1         12  
  1         54  
4 1     1   6 use Scalar::Util 'blessed';
  1         2  
  1         45  
5 1     1   6 use POSIX 'ceil';
  1         2  
  1         7  
6              
7             our $VERSION = '0.10';
8              
9             our @value_list =
10             qw/prev
11             next
12             current_start
13             current_end
14             page_start
15             page_end
16             separator
17             ellipsis
18             placeholder
19             num_format/;
20              
21             # Register plugin
22             sub register {
23 3     3 1 4852 my ($plugin, $mojo, $param) = @_;
24              
25 3   50     12 $param ||= {};
26              
27             # Load parameter from Config file
28 3 50       15 if (my $config_param = $mojo->config('TagHelpers-Pagination')) {
29 0         0 $param = { %$param, %$config_param };
30             };
31              
32 3         46 foreach (@value_list) {
33 30 100       69 $plugin->{$_} = $param->{$_} if defined $param->{$_};
34             };
35              
36             # Set 'current_start' and 'current_end' symbols,
37             # if 'current' template is available.
38             # Same for 'page'.
39 3         7 foreach (qw/page current/) {
40 6 100       16 if (defined $param->{$_}) {
41 1         17 @{$plugin}{"${_}_start", "${_}_end"} = split("{$_}", $param->{$_});
  1         5  
42 1   50     5 $plugin->{"${_}_end"} ||= '';
43             };
44             };
45              
46             # Default current start and current end symbols
47 3         6 for ($plugin) {
48 3   100     20 $_->{current_start} //= '[';
49 3   100     13 $_->{current_end} //= ']';
50 3   50     16 $_->{page_start} //= '';
51 3   50     13 $_->{page_end} //= '';
52 3   100     15 $_->{prev} //= '<';
53 3   100     13 $_->{next} //= '>';
54 3   100     11 $_->{separator} //= ' ';
55 3   100     13 $_->{ellipsis} //= '...';
56 3   100     10 $_->{placeholder} //= 'page';
57             };
58              
59             # Establish pagination helper
60             $mojo->helper(
61             pagination => sub {
62 61     61   39267 return b( $plugin->pagination( @_ ) );
63 3         20 });
64             };
65              
66              
67             # Pagination helper
68             sub pagination {
69 61     61 1 134 my $self = shift;
70 61         104 my $c = shift;
71              
72             # $_[0] = current page
73             # $_[1] = page count or -1
74             # $_[2] = template or Mojo::URL or override types hash
75             # $_[3] = override types
76              
77 61 100       181 return '' unless $_[1];
78              
79             # No valid count given
80 57 50       306 local $_[1] = !$_[1] ? 1 : ceil($_[1]);
81 57   100     162 local $_[0] = $_[0] // 1;
82              
83             # New parameter hash
84             my %values =
85 57         142 map { $_ => $self->{$_} } @value_list;
  570         1394  
86              
87             # Overwrite plugin defaults
88 57         121 my $overwrite;
89 57         112 my $t = $_[2]; # Template
90 57 100 66     292 if ($_[3] && ref $_[3] eq 'HASH') {
    100 100        
91 20         40 $overwrite = $_[3];
92             } elsif ($_[2] && ref $_[2] eq 'HASH') {
93 12         25 $overwrite = $_[2];
94 12         24 $t = undef;
95             };
96              
97             # Values need to be overwritten
98 57 100       163 if ($overwrite) {
99 32         77 foreach (@value_list) {
100 320 100       760 $values{$_} = $overwrite->{$_} if defined $overwrite->{$_};
101             };
102              
103 32         65 foreach (qw/page current/) {
104 64 100       150 if (defined $overwrite->{$_}) {
105 31         537 @values{$_ . '_start', $_ . '_end'} = split("{$_}", $overwrite->{$_});
106 31   50     136 $values{$_ . '_end'} ||= '';
107             };
108             };
109             };
110              
111 57         104 my $nf;
112 57 100 66     249 if (exists $values{num_format} && ref $values{num_format} ne 'CODE') {
113 35         74 delete $values{num_format};
114             } else {
115             $nf = $values{num_format}
116 22         44 };
117              
118             # Establish string variables
119 57         252 my ($p, $n, $cs, $ce, $ps, $pe, $s, $el, $ph) = @values{@value_list};
120             # prev next current_start current_end
121             # page_start page_end separator ellipsis placeholder
122              
123             # Template
124 57 100 66     209 if (blessed $t && blessed $t eq 'Mojo::URL') {
125 1         4 $t = $t->to_string;
126 1         879 $t =~ s/\%7[bB]$ph\%7[dD]/{$ph}/g;
127             };
128              
129 57 50       133 my $sub = sublink_gen($c, $t, $ps, $pe, $ph) or return '';
130              
131             # Pagination string
132 57         110 my $e;
133 57         113 my $counter = 1;
134              
135             # More than seven pages
136 57 100 100     328 if ($_[1] >= 7 ||
      100        
137              
138             # Or the number of pages is unknown
139             ($_[1] == -1 && $_[0] > 4)){
140              
141             # < [1] #2 #3
142             # The current page is 1
143 21 100       59 if ($_[0] == 1){
    100          
144 3 50       90 $e .= $sub->(undef, [$p, 'prev']) . $s .
145             $sub->(undef, [$cs . ($nf ? $nf->(1) : 1) . $ce, 'self']) . $s .
146             $sub->('2', $nf) . $s .
147             $sub->('3', $nf) . $s;
148             }
149              
150             # < #1 #2 #3
151             # The current page is 0
152             elsif ($_[0] == 0) {
153 2         62 $e .= $sub->(undef, [$p, 'prev']) . $s;
154 2         61 $e .= $sub->($_, $nf) . $s foreach (1 .. 3);
155             }
156              
157             # #< #1
158             # The current page is anywhere
159             else {
160 16         499 $e .= $sub->(($_[0] - 1), [$p, 'prev']) . $s .
161             $sub->('1', $nf) . $s;
162             };
163              
164             # [2] #3
165             # The current page is 2
166 21 50       86 if ($_[0] == 2) {
    100          
167 0 0       0 $e .= $sub->(undef, [$cs . ($nf ? $nf->(2) : 2) . $ce, 'self']) . $s .
168             $sub->('3', $nf) . $s;
169             }
170              
171             # ...
172             # The current page is beyond 3
173             elsif ($_[0] > 3) {
174 14         31 $e .= $el . $s;
175             };
176              
177             # The current symbol
178 21 100       66 my $csym = $cs . ($nf ? $nf->($_[0]) : $_[0]) . $ce;
179              
180             # #x-1 [x] #x+1
181             # The current page is beyond 2 and there are at least 2 pages to go
182 21 100 100     97 if (($_[0] >= 3) && ($_[0] <= ($_[1] - 2))) {
183 7         202 $e .= $sub->($_[0] - 1, $nf) . $s .
184             $sub->(undef, [$csym, 'self']) . $s .
185             $sub->($_[0] + 1, $nf) . $s;
186             };
187              
188             # ...
189             # There are at least 2 pages following the current page
190 21 100       59 if ($_[0] < ($_[1] - 2)){
191 12         27 $e .= $el . $s;
192             };
193              
194             # The current page is prefinal
195 21 100       83 if ($_[0] == ($_[1] - 1)){
    50          
    100          
196 1         35 $e .= $sub->($_[1] - 2, $nf) . $s .
197             $sub->(undef, [$csym, 'self']) . $s .
198             $sub->($_[1], $nf) . $s .
199             $sub->($_[1], [$n, 'next']);
200             }
201              
202             # The current page is final
203             elsif ($_[0] == $_[1]) {
204 0         0 $e .= $sub->($_[0] - 1, $nf) . $s .
205             $sub->(undef, [$csym, 'self']) . $s .
206             $sub->(undef, [$n, 'next']);
207             }
208              
209             # Number is unknown
210             elsif ($_[1] == -1) {
211 8         234 $e .= $sub->($_[0] - 1, $nf) . $s .
212             $sub->(undef, [$csym, 'self']) . $s .
213             $el . $s .
214             $sub->($_[0] + 1, [$n, 'next']);
215             }
216              
217             # Number is anywhere in between
218             else {
219 12         338 $e .= $sub->($_[1], $nf) . $s .
220             $sub->($_[0] + 1, [$n, 'next']);
221             };
222             }
223              
224             # Counter < 7
225             else {
226              
227             # Previous
228 36 100       92 if ($_[0] > 1){
229 15         458 $e .= $sub->($_[0] - 1, [$p, 'prev']) . $s;
230             } else {
231 21         670 $e .= $sub->(undef, [$p, 'prev']) . $s;
232             };
233              
234             # All numbers in between
235 36   100     182 while ($counter <= $_[1] || ($_[1] == -1 && $counter <= $_[0])){
      100        
236 79 100       167 if ($_[0] != $counter) {
237 55         1549 $e .= $sub->($counter, $nf) . $s;
238             }
239              
240             # Current
241             else {
242 24 100       444 $e .= $sub->(
243             undef,
244             [$cs . ($nf ? $nf->($counter) : $counter) . $ce, 'self']
245             ) . $s;
246             };
247              
248 79         316 $counter++;
249             };
250              
251             # Ellipsis in case the number is not known
252 36 100       86 $e .= $el . $s if $_[1] == -1;
253              
254             # Next
255 36 100       74 if ($_[0] != $_[1]){
256 29         814 $e .= $sub->($_[0] + 1, [$n, 'next']);
257             }
258              
259             else {
260 7         195 $e .= $sub->(undef, [$n, 'next']);
261             };
262             };
263              
264             # Pagination string
265 57         1953 $e;
266             };
267              
268              
269             # Sublink function generator
270             sub sublink_gen {
271 57     57 0 113 my $c = shift;
272 57         132 my ($url, $ps, $pe, $ph) = @_;
273              
274 57         129 my $s = 'sub{';
275             # $_[0] = number
276             # $_[1] = number_shown
277              
278             # Url is template
279 57 100 66     185 if ($url && length($url) > 0) {
280 28         70 $s .= 'my $url=' . _quote($url) . ';';
281 28         71 $s .= 'if($_[0]){$url=~s/\{' . $ph . '\}/$_[0]/g}else{$url=undef};';
282             }
283              
284             # No template given
285             else {
286 29         61 $s .= 'my $url = $_[0];';
287             };
288              
289 57         115 $s .= q!my$n=$_[1]||! . _quote($ps) . '.$_[0].' . _quote($pe) . ';' .
290             q{my $rel='';} .
291             q'if(ref $n){' .
292             q!if(ref $n eq'ARRAY'){!.
293             q!$rel=' rel="'.$n->[1].'"';$n=$n->[0]! .
294             q!}else{$n=! . _quote($ps) . '.$n->($_[0]).' . _quote($pe) . '}};' .
295             q!if($url){$url=~s/&/&/g;! .
296             q{$url=~s/
297             q{$url=~s/>/>/g;} .
298             q{$url=~s/"/"/g;} .
299             q!$url=~s/'/'/g;};!;
300              
301             # Create sublink
302 57         126 $s .= q!return '' . $n . '';}!;
303              
304 57         19629 my $x = eval $s;
305              
306             # Log evaluation error and return
307 57 50 0     339 $c->app->log->warn($@) and return if $@;
308              
309 57         217 $x;
310             };
311              
312              
313             # Quote with a single quote
314             sub _quote {
315 256     256   397 my $str = shift;
316 256         467 $str =~ s/(['\\])/\\$1/g;
317 256         700 return qq{'$str'};
318             };
319              
320             1;
321              
322              
323             __END__