File Coverage

blib/lib/HTML/Template/Filters.pm
Criterion Covered Total %
statement 60 216 27.7
branch 0 116 0.0
condition 0 33 0.0
subroutine 20 47 42.5
pod 0 14 0.0
total 80 426 18.7


line stmt bran cond sub pod time code
1             package HTML::Template::Filters;
2 1     1   898 use strict;
  1         2  
  1         43  
3 1     1   5 use warnings FATAL => 'all';
  1         3  
  1         52  
4 1     1   1119 use utf8;
  1         13  
  1         9  
5 1     1   41 use Exporter;
  1         3  
  1         49  
6 1     1   6 use Carp;
  1         3  
  1         76  
7 1     1   7 use vars qw(@ISA @EXPORT);
  1         1  
  1         128  
8             our $VERSION = '0.05';
9              
10             @ISA = qw(Exporter);
11             @EXPORT = qw(
12             HT_FILTER_STRIP_TMPL_NEWLINE_WHITESPACE
13             HT_FILTER_VANGUARD_COMPATIBILITY
14             HT_FILTER_ALLOW_TRAILING_SLASH
15             HT_FILTER_SSI_INCLUDE_VIRTUAL
16             HT_FILTER_STRIP_TMPL_NEWLINE
17             HT_FILTER_GZIPPED_TEMPLATES
18             HT_FILTER_PERCENT_VARIABLES
19             HT_FILTER_STRIP_REDUNDANT
20             HT_FILTER_STRIP_NEWLINE
21             HT_FILTER_TMPL_CONSTANT
22             HT_FILTER_TMPL_COMMENT
23             HT_FILTER_TMPL_FIXME
24             HT_FILTER_TMPL_JOIN
25             HT_FILTER_MAC_OS
26             );
27              
28 1     1   6 use constant HT_FILTER_STRIP_TMPL_NEWLINE_WHITESPACE => 'strip_tmpl_newline_whitespace';
  1         1  
  1         77  
29 1     1   6 use constant HT_FILTER_VANGUARD_COMPATIBILITY => 'percent_variables';
  1         2  
  1         43  
30 1     1   4 use constant HT_FILTER_ALLOW_TRAILING_SLASH => 'allow_trailing_slash';
  1         8  
  1         39  
31 1     1   5 use constant HT_FILTER_SSI_INCLUDE_VIRTUAL => 'ssi_include_virtual';
  1         2  
  1         45  
32 1     1   5 use constant HT_FILTER_STRIP_TMPL_NEWLINE => 'strip_tmpl_newline';
  1         1  
  1         40  
33 1     1   5 use constant HT_FILTER_GZIPPED_TEMPLATES => 'gzipped_templates';
  1         1  
  1         52  
34 1     1   4 use constant HT_FILTER_PERCENT_VARIABLES => 'percent_variables';
  1         2  
  1         44  
35 1     1   4 use constant HT_FILTER_STRIP_REDUNDANT => 'strip_redundant';
  1         2  
  1         944  
36 1     1   6 use constant HT_FILTER_STRIP_NEWLINE => 'strip_newline';
  1         2  
  1         54  
37 1     1   5 use constant HT_FILTER_TMPL_CONSTANT => 'tmpl_constant';
  1         2  
  1         44  
38 1     1   4 use constant HT_FILTER_TMPL_COMMENT => 'tmpl_comment';
  1         2  
  1         41  
39 1     1   5 use constant HT_FILTER_TMPL_FIXME => 'tmpl_fixme';
  1         2  
  1         42  
40 1     1   5 use constant HT_FILTER_TMPL_JOIN => 'tmpl_join';
  1         1  
  1         44  
41 1     1   5 use constant HT_FILTER_MAC_OS => 'mac_os';
  1         16  
  1         5242  
42              
43             #
44             # Example: get_filters(HT_FILTER_ALLOW_TRAILING_SLASH,HT_FILTER_TMPL_COMMENT);
45             #
46             sub get_filters {
47 0 0   0 0   croak "Invalid arguments to HTML::Template::Filters->get_filters()" unless (@_ > 1);
48 0           my $pkg = shift;
49 0           my @wanted_filters = @_;
50              
51             # get the requested filters
52 0           my @filter_subs;
53 0           foreach my $wanted_filter (@wanted_filters) {
54 0 0 0       next unless (defined $wanted_filter and length $wanted_filter);
55 0 0         croak "Unknown filter: $wanted_filter" unless ($pkg->can($wanted_filter));
56 0           my ($filter,$format) = $pkg->$wanted_filter();
57 0 0 0       $format = 'scalar' if (not defined $format or $format ne 'array');
58 0           push @filter_subs, {
59             'sub' => $filter,
60             'format' => $format,
61             };
62             }
63              
64 0           return \@filter_subs;
65             }
66              
67             #
68             # allow trailing slash in tags
69             #
70             sub allow_trailing_slash {
71             my $filter = sub {
72 0     0     my $text_ref = shift;
73 0           my $match = qr/(<[Tt][Mm][Pp][Ll]_[^>]+)\/>/;
74 0           $$text_ref =~ s/$match/$1>/g;
75 0     0 0   };
76 0           return $filter;
77             }
78              
79             #
80             # Translate the SSI "include virtual" into a template include:
81             #
82             sub ssi_include_virtual {
83             my $filter = sub {
84 0     0     my $text_ref = shift;
85 0           my $match = qr//i;
86 0           $$text_ref =~ s/$match//g;
87 0     0 0   };
88 0           return $filter;
89             }
90              
91             #
92             # Decompress gzip-comressed templates
93             #
94             sub gzipped_templates {
95 0     0 0   eval { require Compress::Zlib; };
  0            
96 0 0         croak "To use gzip-compressed templates, you need into install Compress::Zlib" if ($@);
97             my $filter = sub {
98 0     0     my $text_ref = shift;
99 0           require Compress::Zlib;
100 0           $text_ref = Compress::Zlib::uncompress($text_ref);
101 0           };
102 0           return $filter;
103             }
104              
105             #
106             # Allow template variables to use %var% syntax
107             #
108             sub percent_variables {
109             my $filter = sub {
110 0     0     my $text_ref = shift;
111 0           my $match = qr/%([-\w\/\.+]+)%/;
112 0           $$text_ref =~ s/$match//g;
113 0     0 0   };
114 0           return $filter;
115             }
116              
117             #
118             # Strip newline's following TMPL_XXX statements
119             #
120             sub strip_tmpl_newline {
121             my $filter = sub {
122 0     0     my $text_ref = shift;
123 0           $$text_ref =~ s/(]>)[\r\n]+/$1/g;
124 0     0 0   };
125 0           return $filter;
126             }
127              
128              
129             # remove any space at the start of a line if it is immediately before a tmpl tag
130             sub strip_tmpl_newline_whitespace {
131             my $filter = sub {
132 0     0     my $text_ref = shift;
133 0           $$text_ref =~ s!^\s+(]+>)!$1!sg;
134 0           $$text_ref =~ s!(]>)[\r\n]+!$1!sg;
135 0     0 0   };
136 0           return $filter;
137             }
138              
139             ##
140             ## Concept taken from Compress::LeadingBlankSpaces to remove redundant data from the output stream
141             ##
142             ## -> Takes a string ref and returns string ref, so as to minimise data copying
143             ## -> skip over headers...
144             ## -> respects
...
tags
145             ## -> strips leading spaces
146             ## -> strips javascript comments
147             ## -> strips style comments
148             ## -> strips html comments
149             ## -> strips empty lines
150             ## -> Doesn't support multi-line stripping, as this complicates the issue somewhat
151             ##
152             sub strip_redundant {
153             my $filter = sub {
154 0     0     my $text_ref = shift;
155 0           my @buf = split('\n',$$text_ref);
156 0           my $output = '';
157 0           my $pre = 0;
158 0           my $script = 0;
159 0           my $style = 0;
160 0           my $headers = 1;
161            
162 0           LOOP: foreach (@buf) {
163            
164             ## skip over headers
165 0 0         if ($headers) {
166 0 0         /<[Hh][Tt][Mm][Ll]/o and $headers=0;
167 0 0         if ($headers) {
168 0           $output .= $_ ."\n";
169 0           next;
170             };
171             }
172            
173             ## find any
174 0 0         if (/<\/[Pr][Rr][Ee]>/o) {
175 0           $output .= $_ ."\n";
176 0           $pre=0;
177 0           next;
178             }
179 0 0         if ($pre) {
180 0           $output .= $_ ."\n";
181 0           next;
182             }
183            
184 0           chomp;
185 0 0         next unless length;
186            
187             ## javascript comments
188 0 0         /<[Ss][Cc][Rr][Ii][Pp][Tt]/o and $script=1;
189 0 0         /<\/[Ss][Cc][Rr][Ii][Pp][Tt]>/o and $script=0;
190 0 0         if ($script) {
191 0 0 0       /^\/\//o and not /-->/o and next;
192 0 0 0       /(.*)\/\/(.*)$/o and not ($1 =~ /http/o or $2 =~ /-->/o) and s/\s*\/\/.*$//o and next unless length;
      0        
      0        
      0        
193 0 0 0       s/\s*\/\*.*\*\/\s*//o and next unless length;
194             }
195            
196             ## support in-document styles
197 0 0         /<[Ss][Tt][Yy][Ll][Ee]/o and $style=1;
198 0 0         /<\/[Ss][Tt][Yy][Ll][Ee]>/o and $style=0;
199 0 0         if ($style) {
200 0 0 0       s/\s*\/\*.*\*\/\s*//o and next unless length;
201             }
202            
203             ## html comments
204 0 0 0       s///o unless ($style or $script);
205            
206             ## trailing white space
207 0           s/\s+$//o;
208            
209             ## leading white space
210 0           s/^\s+//o;
211            
212             ## all white space
213 0           s/^\s*$//o;
214            
215             ## if we got here and the line contains no content, dont do anything
216 0 0         next unless length;
217            
218             ## find any
 
219 0 0         /<[Pr][Rr][Ee]>/o and $pre++;
220            
221 0           $output .= $_ ."\n";
222             }
223            
224 0           $text_ref = \$output;
225 0     0 0   };
226 0           return $filter;
227             }
228              
229             #
230             # Simple newline strip
231             #
232             sub strip_newline {
233             my $filter = sub {
234 0     0     my $text_ref = shift;
235 0           $$text_ref =~ s/\n\| *//g;
236 0     0 0   };
237 0           return $filter;
238             }
239              
240             #
241             # strip out ... entries
242             #
243             sub tmpl_comment {
244             my $filter = sub {
245 0     0     my $text_ref = shift;
246 0           my $match = qr/<(?:\!--\s*)?[Tt][Mm][Pp][Ll]_[Cc][Oo][Mm][Mm][Ee][Nn][Tt]\s*(?:--)?>.*?<(?:\!--\s*)?\/[Tt][Mm][Pp][Ll]_[Cc][Oo][Mm][Mm][Ee][Nn][Tt]\s*(?:--)?>/s;
247 0           $$text_ref =~ s/$match//g;
248 0     0 0   };
249 0           return $filter;
250             }
251              
252             #
253             # strip out ... entries
254             #
255             sub tmpl_fixme {
256             my $filter = sub {
257 0     0     my $text_ref = shift;
258 0           my $match = qr/<(?:\!--\s*)?[Tt][Mm][Pp][Ll]_[Ff][Ii][Xx][Mm][Ee]\s*(?:--)?>.*?<(?:\!--\s*)?\/[Tt][Mm][Pp][Ll]_[Ff][Ii][Xx][Mm][Ee]\s*(?:--)?>/s;
259 0           $$text_ref =~ s/$match//g;
260 0     0 0   };
261 0           return $filter;
262             }
263              
264             #
265             # strip out entries
266             #
267             sub tmpl_join {
268             my $filter = sub {
269 0     0     my $text_ref = shift;
270 0           my $ht = shift;
271 0           my $options = $ht->{options};
272 0 0         die "TMPL_JOIN requires 'loop_context_vars' to be set" unless $options->{loop_context_vars};
273 0           my @chunks = split(m!(?=<(?:\!--\s*)?[Tt][Mm][Pp][Ll]_[Jj][Oo][Ii][Nn]\s)!, $$text_ref);
274 0           for (my $count = 0; $count < @chunks; $count++) {
275 0           my $chunk = $chunks[$count];
276 0 0         if ($chunk =~ /^<
277             (?:!--\s*)?
278             (?:
279             [Tt][Mm][Pp][Ll]_[Jj][Oo][Ii][Nn]
280             )
281              
282             \s+
283              
284             # ESCAPE attribute
285             (?:
286             [Ee][Ss][Cc][Aa][Pp][Ee]
287             \s*=\s*
288             (
289             (?:"[^"]*")
290             |
291             (?:'[^']*')
292             |
293             (?:[^\s]*)
294             ) # $1 => ESCAPE
295             )?
296            
297             \s*
298            
299             # FIELD attribute
300             (?:
301             [Ff][Ii][Ee][Ll][Dd]
302             \s*=\s*
303             (
304             (?:"[^"]*")
305             |
306             (?:'[^']*')
307             |
308             (?:[^\s]*)
309             ) # $2 => FIELD
310             )?
311            
312             \s*
313            
314             # SEP attribute
315             (?:
316             [Ss][Ee][Pp](?:[Aa][Rr][Aa][Tt][Oo][Rr])?
317             \s*=\s*
318             (
319             (?:"[^"]*")
320             |
321             (?:'[^']*')
322             |
323             (?:[^\s]*)
324             ) # $3 => SEP
325             )?
326            
327             \s*
328            
329             # ESCAPE attribute
330             (?:
331             [Ee][Ss][Cc][Aa][Pp][Ee]
332             \s*=\s*
333             (
334             (?:"[^"]*")
335             |
336             (?:'[^']*')
337             |
338             (?:[^\s]*)
339             ) # $4 => ESCAPE
340             )?
341            
342             \s*
343            
344             # FIELD attribute
345             (?:
346             [Ff][Ii][Ee][Ll][Dd]
347             \s*=\s*
348             (
349             (?:"[^"]*")
350             |
351             (?:'[^']*')
352             |
353             (?:[^\s]*)
354             ) # $5 => FIELD
355             )?
356            
357             \s*
358            
359             # SEP attribute
360             (?:
361             [Ss][Ee][Pp](?:[Aa][Rr][Aa][Tt][Oo][Rr])?
362             \s*=\s*
363             (
364             (?:"[^"]*")
365             |
366             (?:'[^']*')
367             |
368             (?:[^\s]*)
369             ) # $6 => SEP
370             )?
371            
372             \s*
373            
374             # ESCAPE attribute
375             (?:
376             [Ee][Ss][Cc][Aa][Pp][Ee]
377             \s*=\s*
378             (
379             (?:"[^"]*")
380             |
381             (?:'[^']*')
382             |
383             (?:[^\s]*)
384             ) # $7 => ESCAPE
385             )?
386            
387             \s*
388            
389             # NAME attribute
390             (?:
391             (?:
392             [Nn][Aa][Mm][Ee]
393             \s*=\s*
394             )?
395             (
396             (?:"[^"]*")
397             |
398             (?:'[^']*')
399             |
400             (?:[^\s]*)
401             ) # $8 => NAME
402             )?
403            
404             \s*
405            
406             # ESCAPE attribute
407             (?:
408             [Ee][Ss][Cc][Aa][Pp][Ee]
409             \s*=\s*
410             (
411             (?:"[^"]*")
412             |
413             (?:'[^']*')
414             |
415             (?:[^\s]*)
416             ) # $9 => ESCAPE
417             )?
418            
419             \s*
420            
421             # FIELD attribute
422             (?:
423             [Ff][Ii][Ee][Ll][Dd]
424             \s*=\s*
425             (
426             (?:"[^"]*")
427             |
428             (?:'[^']*')
429             |
430             (?:[^\s]*)
431             ) # $10 => FIELD
432             )?
433            
434             \s*
435            
436             # SEP attribute
437             (?:
438             [Ss][Ee][Pp](?:[Aa][Rr][Aa][Tt][Oo][Rr])?
439             \s*=\s*
440             (
441             (?:"[^"]*")
442             |
443             (?:'[^']*')
444             |
445             (?:[^\s]*)
446             ) # $11 => SEP
447             )?
448            
449             \s*
450            
451             # ESCAPE attribute
452             (?:
453             [Ee][Ss][Cc][Aa][Pp][Ee]
454             \s*=\s*
455             (
456             (?:"[^"]*")
457             |
458             (?:'[^']*')
459             |
460             (?:[^\s]*)
461             ) # $12 => ESCAPE
462             )?
463            
464             \s*
465            
466             # FIELD attribute
467             (?:
468             [Ff][Ii][Ee][Ll][Dd]
469             \s*=\s*
470             (
471             (?:"[^"]*")
472             |
473             (?:'[^']*')
474             |
475             (?:[^\s]*)
476             ) # $13 => FIELD
477             )?
478            
479             \s*
480            
481             # SEP attribute
482             (?:
483             [Ss][Ee][Pp](?:[Aa][Rr][Aa][Tt][Oo][Rr])?
484             \s*=\s*
485             (
486             (?:"[^"]*")
487             |
488             (?:'[^']*')
489             |
490             (?:[^\s]*)
491             ) # $14 => SEP
492             )?
493            
494             \s*
495            
496             # ESCAPE attribute
497             (?:
498             [Ee][Ss][Cc][Aa][Pp][Ee]
499             \s*=\s*
500             (
501             (?:"[^"]*")
502             |
503             (?:'[^']*')
504             |
505             (?:[^\s]*)
506             ) # $15 => ESCAPE
507             )?
508            
509             \s*
510              
511             (?:
512             (?:--)
513             |
514             (?:\/)
515             )?>
516             (.*) # $16 => $post - text that comes after the tag
517              
518             $/sxo) {
519 0           my $name = $8;
520 0 0 0       if (defined $name and length $name) {
521 0 0         my $escape = defined $1 ? $1 : defined $4 ? $4 : defined $7 ? $7 : defined $9 ? $9 : defined $12 ? $12 : defined $15 ? $15 : '';
    0          
    0          
    0          
    0          
    0          
522 0 0         my $sep = defined $3 ? $3 : defined $6 ? $6 : defined $11 ? $11 : defined $14 ? $14 : '';
    0          
    0          
    0          
523 0 0         my $field = defined $2 ? $2 : defined $5 ? $5 : defined $10 ? $10 : defined $13 ? $13 : '';
    0          
    0          
    0          
524 0 0         my $post = defined $16 ? $16 : '';
525 0 0         $field = '__value__' unless length($field);
526 0 0         $sep = substr($sep,1,length($sep)-2) if ($sep =~ /^['"]/);
527 0 0         $escape = "ESCAPE=$escape" if $escape;
528 0 0         my $join = length $sep ? "$sep" : "";
529 0           $chunk = "$join";
530 0           $chunks[$count] = $chunk.$post;
531             }
532             }
533             }
534 0           $$text_ref = join('',@chunks);
535 0     0 0   };
536 0           return $filter;
537             }
538              
539             #
540             # allow
541             # note this only works for TMPL_VAR's
542             #
543             sub tmpl_constant {
544             my $filter = sub {
545 0     0     my $text_ref = shift;
546 0           my $match = qr/<(?:\!--\s*)?[Tt][Mm][Pp][Ll]_[Cc][Oo][Nn][Ss][Tt][Aa][Nn][Tt]\s*[Nn][Aa][Mm][Ee]\s*=(.*?)\s*[Vv][Aa][Ll][Uu][Ee]\s*=(.*?)\s*(?:--)?>/;
547 0           my @taglist = $$text_ref =~ m/$match/g;
548 0 0         return unless (@taglist > 0);
549 0           my $strip = qr/^(?:'(.*)')|(?:"(.*)")$/;
550 0           my %set_params;
551 0           while (@taglist) {
552 0           my ($t,$v) = (shift @taglist,shift @taglist);
553 0           $t =~ m/$strip/;
554 0 0         $t = defined $1 ? $1 : defined $2 ? $2 : $t;
    0          
555 0           $v =~ m/$strip/;
556 0 0         $v = defined $1 ? $1 : defined $2 ? $2 : $v;
    0          
557 0           $set_params{$t} = $v;
558             }
559 0           $$text_ref =~ s/$match//g;
560 0           my $split = qr/(?=<(?:\!--\s*)?[Tt][Mm][Pp][Ll]_[Vv][Aa][Rr]\s+)/;
561 0           my @chunks = split ($split, $$text_ref);
562 0 0         return unless (@chunks > 0);
563 0           my @output;
564 0           my $chunker = qr/^(?=
565             <(?:!--\s*)?
566             [Tt][Mm][Pp][Ll]_[Vv][Aa][Rr]\s+(?:[Nn][Aa][Mm][Ee]\s*=\s*)?
567             (?:
568             "([^">]*)"
569             |
570             '([^'>]*)'
571             |
572             ([^\s=>]*)
573             )
574             \s*(?:[^>])?(?:--)?>
575             (.*)
576             )/sx;
577 0           foreach my $chunk (@chunks) {
578 0 0         if ($chunk =~ $chunker) {
579 0 0         my $name = defined $1 ? $1 : defined $2 ? $2 : defined $3 ? $3 : undef;
    0          
    0          
580 0 0 0       if (defined $name and exists $set_params{$name}) {
581 0           $chunk = $set_params{$name};
582 0 0         $chunk .= $4 if $4;
583             }
584             }
585 0           push @output, $chunk;
586             }
587 0           $$text_ref = join '',@output;
588 0     0 0   };
589 0           return $filter;
590             }
591              
592             #
593             # turns the '\r' line feed to a '\n', for the Mac OS
594             #
595             sub mac_os {
596             my $filter = sub {
597 0     0     my $text_ref = shift;
598 0           my $match = qr/\r/s;
599 0           $$text_ref =~ s/$match/\n/g;
600 0     0 0   };
601 0           return $filter;
602             }
603              
604             1;
605             __END__