File Coverage

blib/lib/CGI/ProgressBar.pm
Criterion Covered Total %
statement 77 90 85.5
branch 17 34 50.0
condition 1 3 33.3
subroutine 13 13 100.0
pod 3 3 100.0
total 111 143 77.6


line stmt bran cond sub pod time code
1 1     1   9548 use strict;
  1         2  
  1         48  
2 1     1   6 use warnings;
  1         3  
  1         57  
3              
4             package CGI::ProgressBar;
5            
6             =head1 NAME
7            
8             CGI::ProgressBar - CGI.pm sub-class with a progress bar object
9            
10             =head1 SYNOPSIS
11            
12             use strict;
13             use warnings;
14             use CGI::ProgressBar qw/:standard/;
15             $| = 1; # Do not buffer output
16             print header,
17             start_html(
18             -title=>'A Simple Example',
19             -style=>{
20             -src => '', # You can override the bar style here
21             -code => '', # or inline, here.
22             }
23             ),
24             h1('A Simple Example'),
25             p('This example will update a JS/CSS progress bar.'),
26             progress_bar( -from=>1, -to=>100 );
27             # We're set to go.
28             for (1..10){
29             print update_progress_bar;
30             # Simulate being busy:
31             sleep 1;
32             }
33             # Now we're done, get rid of the bar:
34             print hide_progress_bar;
35             print p('All done.');
36             print end_html;
37             exit;
38              
39             =head1 DESCRIPTION
40            
41             This module provides an HTML/JS progress bar for web browsers, to keep end-users occupied when otherwise
42             nothing would appear to be happening.
43            
44             It aims to require that the recipient client have a minimum
45             of JavaScript 1.0, HTML 4.0, and CSS/1.
46            
47             All feedback would be most welcome. Address at the end of the POD.
48            
49             =cut
50            
51 1     1   22 use 5.004;
  1         9  
  1         67  
52            
53             =head2 DEPENDENCIES
54            
55             CGI
56            
57             =cut
58              
59             our $VERSION = '0.05';
60              
61             BEGIN {
62 1     1   794 use CGI::Util; # qw(rearrange);
  1         11222  
  1         73  
63 1     1   10 use base 'CGI';
  1         3  
  1         19355  
64            
65             =head2 EXPORT
66            
67             progress_bar
68             update_progress_bar
69             hide_progress_bar
70            
71             =cut
72            
73 1     1   17796 no strict 'refs';
  1         3  
  1         292  
74 1     1   3 foreach (qw/ progress_bar update_progress_bar hide_progress_bar/){
75 3         5 *{caller(0).'::'.$_} = \&{__PACKAGE__.'::'.$_};
  3         1527  
  3         17  
76             }
77 1     1   5 use strict 'refs';
  1         2  
  1         25  
78             }
79            
80             =head1 USE
81            
82             The module sub-classes CGI.pm, providing three additional methods (or
83             functions, depending on your taste), each of which are detailed below.
84            
85             Simply replace your "use CGI qw//;" with "use CGI::ProgressBar qw//;".
86              
87             Make sure you are aware of your output buffer size: C<$|=$smothingsmall>.
88            
89             Treat each new function as any other CGI.pm HTML-producing routine with
90             the exception that the arguments should be supplied as in OOP form. In
91             other words, the following are all the same:
92            
93             my $html = $query->progress_bar;
94             my $html = progress_bar;
95             my $html = progress_bar(from=>1, to=>10);
96             my $html = $query->progress_bar(from=>1, to=>10);
97             my $html = $query->progress_bar(-to=>10);
98            
99             This will probably change if someone would like it to.
100            
101             =head1 FUNCTIONS/METHODS
102            
103             =head2 FUNCTION/METHOD progress_bar
104            
105             Returns mark-up that instantiates a progress bar.
106             Currently that is HTML and JS, but perhaps the JS
107             ought to go into the head.
108            
109             The progress bar itself is an object in this class,
110             stored in the calling (C) object - specifically
111             in the field C, which we create.
112             (TODO: Make this field an array to allow multiple bars per page.)
113            
114             =over 4
115            
116             =item from
117            
118             =item to
119            
120             Values which the progress bar spans.
121             Defaults: 0, 100.
122              
123             =item orientation
124              
125             If set to C displays the bar as a strip down the screen; otherwise,
126             places it across the screen.
127              
128             =item width
129            
130             =item height
131            
132             The width and height of the progress bar, in pixels. Cannot accept
133             percentages (yet). Defaults: 400, 20, unless you specify C
134             as C, in which case this is reversed.
135            
136             =item blocks
137              
138             The number of blocks to appear in the progress bar.
139             Default: 10. You probably want to link this to C and C
140             or better still, leave it well alone: it may have been a mistake to even include it.
141             C is an alias for this attribute.
142              
143             =item label
144            
145             Supply this parameter with a true value to have a numerical
146             display of progress. Default is not to display it.
147            
148             =item layer_id
149            
150             Most HTML elements on the page have C attributes. These
151             can be accessed through the C field, which is a hash
152             with the follwoing keys relating to the C value:
153              
154             =item mycss
155              
156             Custom CSS to be written inline (ugh) after any system CSS.
157            
158             =over 4
159            
160             =item form
161            
162             The C
which contains everything we display.
163            
164             =item container
165            
166             The C
containing everything we display.
167            
168             =item block
169            
170             This value is used as a prefixed for the C of each block of the bar,
171             with the suffix being a number incremented from C<1>.
172            
173             =item number
174            
175             The digits being updated as the bar progresses, if the option is enabled.
176            
177             =back
178            
179             =back
180            
181             =cut
182            
183             our $CSS = '';
184              
185             sub progress_bar {
186 5     5 1 10863 local $_;
187 5         9 my ($self,%args);
188 5         173 ($self,@_) = &CGI::self_or_default(@_);
189            
190 5         118 my $pb = bless {
191             _updates=> 0, debug => undef,
192             mycss => '', orientation => 'horizontal',
193             from => 1, to => 100, width => '400',
194             height => '20', blocks => 10,
195             label => 0, colors => [100,'blue'],
196             },__PACKAGE__;
197              
198 5 50       20 if (ref $_[0] eq 'HASH'){ %args = %{$_[0]} }
  0 50       0  
  0         0  
199 5         14 elsif (not ref $_[0]){ %args = @_ }
200             else {
201 0         0 warn "Usage: \$class->new( keys=>values, )";
202 0         0 return undef;
203             }
204 5         15 foreach my $k (keys %args){
205 3         5 my $nk = $k;
206 3         7 $nk =~ s/^-(.*)$/$1/;
207 3         9 $pb->{$nk} = $args{$k};
208             }
209 5 50       16 $pb->{blocks} = $args{steps} if $args{steps};
210              
211 5 50       15 $pb->{orientation} = 'vertical' if $pb->{orientation} eq 'v';
212 5 50       13 if ($pb->{orientation} eq 'vertical'){
213 0         0 my $w = $pb->{width};
214 0         0 $pb->{width} = $pb->{height};
215 0         0 $pb->{height} = $w;
216             }
217            
218 5 50       11 $pb->{colors} = $pb->{colors}? {@{$pb->{colors}}} : {100=>'blue'};
  5         20  
219 5         17 $pb->{_length} = $pb->{to} - $pb->{from}; # Units in the bar
220 5         8 $pb->{_interval} = 1; # publicise?
221             # $pb->{_interval} = $pb->{_length}>0? ($pb->{_length}/$pb->{blocks}) : 0;
222 5         22 $pb->{block_wi} = int( $pb->{width} / $pb->{blocks} ) -2;
223             # IN A LATER VERSION....Store ourself in caller's progress_bar array
224             # push @{ $self->{progress_bar} },$pb;
225 5         9 $self->{progress_bar} = $pb;
226              
227 5         19 for my $k (qw[ from to blocks _interval ]){
228 20         61 $pb->{$k} = int($pb->{$k});
229             }
230              
231 5 50       15 if ($pb->{debug}){
232 0         0 require Data::Dumper; import Data::Dumper;
  0         0  
233 0         0 warn 'New CGI::ProgressBar '.Dumper($pb);
234 0         0 warn 'Total blocks='.($pb->{blocks});
235 0         0 warn 'Expected total calls='.($pb->{to}/$pb->{_interval});
236             }
237            
238 5         18 return $self->_pb_init();
239             }
240              
241              
242            
243             =head2 FUNCTION/METHOD update_progress_bar
244            
245             Updates the progress bar.
246            
247             =cut
248            
249             sub update_progress_bar {
250             # my ($self, @crud) = CGI::self_or_default;
251 1     1 1 568 return "\n";
253             }
254            
255             =head2 FUNCTION/METHOD hide_progress_bar
256            
257             Hides the progress bar.
258            
259             =cut
260            
261             sub hide_progress_bar {
262 1     1 1 231 my ($self, @crud) = CGI::self_or_default;
263             #my $pb = $self->{progress_bar}[$#{$self->{progress_bar}}];
264              
265 1 50       20 return $self->{progress_bar}?
266             "\n"
268             : '' ;
269             }
270            
271             =head1 CSS STYLE CLASS EMPLOYED
272              
273             You can add CSS to be output into the page body (ugh) in the C field.
274             Bear in mind that the width and height settings are programatically assigned.
275              
276             =item pblib_bar
277            
278             A C
containing the whole progress bar, including any
279             accessories (such as the label). The only attribute used
280             by this module is C, which is set dynamically.
281             The rest is up to you. A good start is:
282            
283             padding: 2 px;
284             border: solid black 1px;
285             text-align: center;
286            
287             =item pblib_block_off, pblib_block_on
288            
289             An individual block within the status bar. The following
290             attributes are set dynamically: C, C,
291             C.
292            
293             =item pblib_number
294            
295             Formatting for the C
296             an C element. C and C
297             are used here, and the whole appears centred within a C.
298            
299             =cut
300            
301             sub CGI::_pb_init {
302 5     5   16 my ($self, @crud) = CGI::self_or_default;
303 5         44 my $html = "";
304             # my $pb = $self->{progress_bar}[$#{$self->{progress_bar}}];
305 5 50 33     35 $self->{progress_bar}->{block_wi} = 1 if not $self->{progress_bar}->{block_wi} or $self->{progress_bar}->{block_wi} < 1 ;
306              
307 5         45 $self->{progress_bar}->{layer_id} = {
308             container => 'pb_cont'.time,
309             form => 'pb_form'.time,
310             block => 'b'.time,
311             number => 'n'.time,
312             };
313 5         23 $self->CGI::_init_css;
314 5         18 $html .= "\n";
315 5 50       24 $html .= "\n" if $^W;
316 5         12 $html .= "\n
\n";
317 5 50       14 $html .= "\t\n\t
\n
" if $self->{progress_bar}->{label};
318            
319 5         8 $html .= "\t
\n\t";
320 5         11 foreach my $i (1 .. $self->{progress_bar}->{blocks}){
321 60         126 $html .= " ";
322             }
323 5         8 $html .= "\n\t\n";
324 5 50       15 $html .= "
325            
326            
327             /> / $self->{progress_bar}->{to}
328            
329            
330            
" if $self->{progress_bar}->{label};
331 5         6 $html .= "\n";
332 5 50       20 $html .="\n\n" if $^W;
333 5         8 $html .= "\n\n";
353            
354 5         77 return $html;
355             }
356            
357             sub CGI::_init_css {
358 5     5   14 my ($self, @crud) = CGI::self_or_default;
359 5 50       67 $CSS = "
360             .pblib_bar {
361             border: 1px solid black;
362             padding: 1px;
363             background: white;
364             display: block;
365             text-align:left;
366             width: ".($self->{progress_bar}->{width})."px;
367             }
368             .pblib_block_on,
369             .pblib_block_off {
370             display: block;
371             ".( $self->{progress_bar}->{orientation} eq 'vertical'?
372             "float:none;
373             width: 100%;
374             height: ".($self->{progress_bar}->{block_wi})."px;"
375             : "float:left;
376             width: ".($self->{progress_bar}->{block_wi})."px;"
377             )."
378             }
379             .pblib_block_off { border:1px solid white; background: white; }
380             .pblib_block_on { border:1px solid blue; background: navy; }
381             ";
382 5 50       15 if ($self->{progress_bar}->{label}){
383 0         0 $CSS .=".pblib_number {
384             text-align: right;
385             border: 1px solid transparent;
386             }";
387             }
388 5         19 $self->{progress_bar}->{css} = $CSS;
389             }
390              
391             =head1 BUGS, CAVEATS, TODO
392            
393             =over 4
394            
395             =item One bar per page
396            
397             This may change.
398            
399             =item Parameter passing doesn't match F
400            
401             But it will in the next release if you ask me for it.
402            
403             =item C not implimented
404            
405             I'd like to see here something like the C;
406             not because I've ever used it, but because it might be cool.
407            
408             =item Horizontal orientation only
409            
410             You can get around this by adjusting the CSS, but you'd rather not.
411             And even if you did, the use of C<-label> might not look very nice
412             unless you did something quite fancy. So the next version (or so)
413             will support an C<-orientation> option.
414            
415             =item Inline CSS and JS
416            
417             Because it's easiest for me. I suppose some kind of over-loading of
418             the C would be possible, but then I'd have to check
419             it, and maybe update it, every time F was updated, which I
420             don't fancy.
421            
422             =cut
423            
424             1;
425             __END__