File Coverage

blib/lib/HTML/ActiveLink.pm
Criterion Covered Total %
statement 12 60 20.0
branch 0 22 0.0
condition 0 13 0.0
subroutine 4 11 36.3
pod 2 7 28.5
total 18 113 15.9


line stmt bran cond sub pod time code
1              
2             package HTML::ActiveLink;
3 1     1   689 use 5.004;
  1         4  
  1         36  
4              
5 1     1   6 use Carp;
  1         1  
  1         96  
6 1     1   5 use strict;
  1         4  
  1         37  
7 1     1   10 use vars qw($VERSION);
  1         1  
  1         1374  
8             $VERSION = do { my @r=(q$Revision: 1.2 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
9              
10             # These are default option settings. We use an array so
11             # we can coax it into a hash easily - see new().
12             my @OPT = (
13             image => 1,
14             image_prefix => '',
15             image_suffix => '_on',
16             image_rmlink => 1,
17             text => 1,
18             text_prefix => '',
19             text_suffix => '',
20             text_rmlink => 1,
21             imagemap => 1,
22             imagemap_prefix => '',
23             imagemap_suffix => '_on',
24             imagemap_joinchar => '_',
25             imagemap_rootname => 'home',
26             imagemap_dirdepth => 2
27             );
28              
29             =head1 NAME
30              
31             HTML::ActiveLink - dynamically activate HTML links based on URL
32              
33             =head1 SYNOPSIS
34              
35             use HTML::ActiveLink;
36              
37             my $al = new HTML::ActiveLink;
38              
39             print $al->activelink(@html_doc);
40              
41             =head1 DESCRIPTION
42              
43             I don't know about you, but one of the main problems I have with
44             HTML content is getting images and links to "turn on" depending
45             on the current URL location. That is, I like authoring one set
46             of templates, something like this:
47              
48             [ Home | FAQ
49             | About Us ]
50              
51             And then having the appropriate link turned on, so that if I'm
52             running inside the /home/ directory, the above turns into this:
53              
54             [ Home | FAQ
55             | About Us ]
56              
57             Without having to write a whole bunch of if's, or writing a
58             bunch of different sets of templates, etc.
59              
60             This module handles the above process automatically. By default,
61             it will activate any text or images with Ea hrefE tags
62             around them by stripping the link off and changing the appearance
63             of text and names of images. All transformations are fully customizable,
64             allowing you to choose how your active text should look. HTML::ActiveLink
65             can even automatically construct imagemaps depending on your location.
66              
67             In the simplest case, all you have to do is create a new object
68             by a call to new(), and then call the main activelink() function
69             which takes care of the transformation. To customize what the
70             output HTML looks like, keep reading...
71              
72             =head1 FUNCTIONS
73              
74             =head2 new()
75              
76             This is the constructor method, and it takes a number of parameters
77             that determine how the output HTML looks:
78              
79             text - transform text links? [1]
80             text_prefix - prefix to add to text []
81             text_suffix - suffix to add to text []
82             text_rmlink - remove tag? [1]
83              
84             image - transform image links? [1]
85             image_prefix - prefix to add to image []
86             image_suffix - suffix to add to image [_on]
87             image_rmlink - remove tag? [1]
88              
89             imagemap - create URL imagemaps? [1]
90             imagemap_prefix - prefix for imagemaps []
91             imagemap_suffix - suffix for imagemaps [_on]
92             imagemap_joinchar - join parts with char [_]
93             imagemap_rootname - imagemap name for / [home]
94             imagemap_dirdepth - max dir levels to use [2]
95              
96             The first set of args determines how to transform text links. By
97             default, any text links will be changed into red text when you're
98             in the directory or document that they point to (see below for more
99             explicit details). To change this, just change the prefix and suffix,
100             for example:
101              
102             my $al = HTML::ActiveLink->new(text_prefix => '',
103             text_suffix => ' >');
104              
105             This will make the active links bold, with a E sign after them
106             as well. A similar principle works for images. By default, an image
107             link like so:
108              
109            
110              
111             Will be transformed to:
112              
113            
114              
115             Notice that the file type suffix is preserved, and that the image
116             suffix is properly applied to the name of the image. Again, to
117             change the suffix or prefix simply change the image_ parameters.
118              
119             Finally, this module will automatically B imagemaps
120             based on the current URL. Unlike the two above methods, which
121             involve parsing and modifying existing content, the imagemap
122             creation instead creates the name of the imagemap dynamically.
123             This is done since imagemaps contain multiple links, so each
124             one represents many areas to click on.
125              
126             For example, if you are running in the directory /faq/, and you
127             have an imagemap that looks like this:
128              
129            
130              
131             Then the image src will be rewritten as:
132              
133            
134              
135             Here, the name of the imagemap is rewritten similarly to images,
136             only depending on your location. The directory information is inserted
137             in after the name of the image that exists, along with the suffix. The
138             imagemap name is created by joining together the directory name(s) for
139             your current location, up to 2 deep by default. More examples:
140              
141             /faq/ = tab_faq_on.gif
142             / = tab_home_on.gif (depending on _rootname)
143             /name/g.html = tab_name_on.gif
144             /id/N/NW/NWIGER/ = tab_id_N_on.gif (note only first 2 used)
145              
146             The second one depends on what you've set imagemap_rootname to,
147             since this is what is used to determine the name for /. In the
148             last example, notice that only 2 dir levels are used by default,
149             meaning that huge dir trees do not result in tons of different
150             imagemap names. To change this, set imagemap_dirdepth.
151              
152             =cut
153              
154             ######################
155             # PUBLIC FUNCTIONS
156             ######################
157              
158             sub new {
159 0     0 1   my $class = shift;
160 0           my %opt = (@OPT, @_);
161 0           return bless \%opt, $class;
162             }
163              
164             =head2 activelink()
165              
166             This is the function that actually parses the document and
167             activates all the necessary links. It joins its arguments into
168             a scalar representation of the file and returns that, which
169             can then be printed out or manipulated further. Examples:
170              
171             print $al->activelink(@doc);
172             print $al->activelink($part1, $part2, $part3);
173             $doc = $al->activelink();
174              
175             And so on. To change how it works, pass different values to
176             the new() function described above.
177              
178             The activelink() function uses regular expressions to match
179             the location so that anything deeper than a link is activated.
180             So, assuming this link:
181              
182             Today's News
183              
184             Then any of the following locations would cause it to be active:
185              
186             /news/today/
187             /news/today/presidential_election_still_undecided.html
188             /news/today/regional/san_diego_headlines.html
189              
190             But none of these would:
191              
192             /news/
193             /news/today.html
194             /news/today
195              
196             Just like with Apache configs, the path needs to be matched
197             completely, and then anything beneath that path works as well.
198              
199             =cut
200              
201             sub activelink {
202              
203 0     0 1   my $self = shift;
204 0           my $document = join '', @_; # scalar easier
205            
206             # first get info about our server to make sure that the links really match
207 0           my $server_port = $self->server_port;
208 0           my $server_name = $self->server_name;
209 0 0         my $http = ($server_port == 443) ? 'https' : 'http';
210 0 0 0       my $port = ($server_port == 443 || $server_port == 80 || $self->{ignore_port})
211             ? "(?:\:$server_port)?" : ":$server_port";
212              
213             # urls as listed in an actual HTML document might be relative,
214             # so we need to check for that
215             # also need to compare the other way, since the actual url we
216             # might be viewing could be MORE specific than what we're
217             # trying to match (hence the $self->match_path() function)
218 0           my $self_url = $self->self_url;
219              
220 0 0         if ($self->{image}) {
221 0           my $imageonprefix = $self->{image_prefix};
222 0           my $imageonsuffix = $self->{image_suffix};
223 0           $document =~ s#(<\s*a.*?href\s*=\s*"?)([^">]*)(\"?[^>]*>)(<\s*img.*?src\s*=\s*"?)(.*/?)(.+?)(\.[\w]+"?.*?>)#
224 0           $self->match_path($2, $self->self_url, "$1$2$3$4$5$6$7",
225             "$1$2$3$4$5$imageonprefix$6$imageonsuffix$7")#gie;
226             }
227              
228 0 0         if ($self->{text}) {
229 0           my $textonprefix = $self->{text_prefix};
230 0           my $textonsuffix = $self->{text_suffix};
231 0 0         if ($self->{text_rmlink}) {
232 0           $document =~ s#(<\s*a.*?href\s*=\s*"?)([^">]*)("?[^>]*>)(<.*?>)?([^<]*)(<.*?>)?(<\s*/a\s*>)#
233 0           $self->match_path($2, $self->self_url, "$1$2$3$4$5$6$7",
234             "$4$textonprefix$5$textonsuffix$6")#gie;
235             } else {
236 0           $document =~ s#(<\s*a.*?href\s*=\s*"?)([^">]*)("?.*>)(.+)(<\s*/a\s*>)#
237 0           $self->match_path($2, $self->self_url, "$1$2$3$4$5",
238             "$1$2$3$textonprefix$4$textonsuffix$5")#gie;
239             }
240             }
241              
242 0 0         if ($self->{imagemap}) {
243             # look for "usemap" or "ismap" in any tags
244 0           my $imagemapprefix = $self->{imagemap_prefix};
245 0           my $imagemapsuffix = $self->{imagemap_suffix};
246 0           my $char = $self->{imagemap_joinchar};
247 0           (my $imagemapname = $self->script_name) =~ s#\W+#$char#g;
248 0 0         $imagemapname .= $self->{imagemap_rootname} if $imagemapname eq $char; # / dir
249 0 0         if (my $levels = $self->{imagemap_dirdepth}) {
250             # chop down the imagemapname
251 0           my @save = split $char, $imagemapname, $levels + 2; # null leader and junk trailer
252 0           pop @save;
253 0           $imagemapname = join $char, @save;
254             }
255 0           $document =~ s#(<\s*img.*?src\s*=\s*"?)(.*/)(.*?)(\.[\w]+"?.*?)(usemap|ismap)(.*?>)#$1$2$imagemapprefix$3$imagemapname$imagemapsuffix$4$5$6#gi;
256             }
257              
258 0           return $document;
259             }
260              
261             ######################
262             # PRIVATE FUNCTIONS
263             ######################
264              
265             # Usage: $self->match_path($path, $test, $default, $ifmatches)
266             #
267             # Private function for matching and transforming urls on the fly.
268             # External usage is not allowed (please).
269              
270             sub match_path {
271              
272             # This takes four arguments
273 0     0 0   my $self = shift;
274 0           my($path, $test, $default, $ifmatches) = @_;
275              
276             # Strip trailing / present on directories
277 0           $path =~ s#(\w+)/+$#$1#;
278              
279             # Special catch for /, since that will match everything
280 0 0         if ($path eq '/') {
281 0 0         return $ifmatches if ($test =~ m#^/[^/]*$#);
282 0           return $default;
283             }
284              
285             # Simple match - don't use eq because we want it to
286             # match /dir, /dir/, and /dir/index.html
287 0 0         if ($test =~ m#^$path#) {
288 0           return $ifmatches;
289             }
290              
291 0           return $default; # no matches above
292             }
293              
294             # Extremely stripped down versions from CGI.pm
295             # Hah, calling them stripped down is probably inaccurate!! :-)
296              
297             sub script_name {
298 0   0 0 0   return $ENV{SCRIPT_NAME} || $0;
299             }
300              
301             sub self_url {
302 0   0 0 0   return $ENV{REQUEST_URI} || $ENV{SCRIPT_NAME} || $0;
303             }
304              
305             sub server_name {
306 0   0 0 0   return $ENV{HTTP_HOST} || $ENV{SERVER_NAME} || 'localhost';
307             }
308              
309             sub server_port {
310 0   0 0 0   return $ENV{HTTP_PORT} || $ENV{SERVER_PORT} || '80';
311             }
312              
313             =head1 APPLICATIONS
314              
315             One simple use of this module that I like is creating a simple
316             script called "header.cgi" that just looks something like this:
317              
318             use HTML::ActiveLink;
319             my $al = HTML::ActiveLink->new(text_prefix => '',
320             text_suffix => '');
321              
322             my $header = '/path/to/header.html';
323             open(HEADER, "<$header") or die $!;
324             print $al->activelink(
);
325              
326             Then, I can use this in my SSI documents like so:
327              
328            
329              
330             And presto! All my SSI .shtml documents have a header which has
331             links that are automatically activated based on the document location.
332             You could, of course, beef up the "header.cgi" script so that it
333             used the name of a file passed as a parameter, etc, depending on
334             what you want to do.
335              
336             =head1 VERSION
337              
338             $Id: ActiveLink.pm,v 1.2 2000/11/27 23:46:29 nwiger Exp $
339              
340             =head1 AUTHOR
341              
342             Copyright (c) 2000 Nathan Wiger, Nateware, Inc. .
343             All Rights Reserved.
344              
345             This module is free software; you may copy this under the terms of
346             the GNU General Public License, or the Artistic License, copies of
347             which should have accompanied your Perl kit.
348              
349             =cut
350