File Coverage

lib/Mojolicious/Plugin/Sprite.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Sprite;
2              
3 1     1   742 use warnings;
  1         1  
  1         34  
4 1     1   4 use strict;
  1         2  
  1         27  
5 1     1   13 use Mojo::Base 'Mojolicious::Plugin';
  1         1  
  1         6  
6 1     1   539 use XML::LibXML;
  0            
  0            
7              
8             use constant DEFAULT_CONFIG_FILE => 'sprite.xml';
9             use constant DEFAULT_CSS_URL => '/css/sprite.css';
10              
11             our $VERSION = '0.01';
12              
13             our $parser = XML::LibXML->new();
14              
15             sub register {
16             my ( $self, $app, $conf ) = @_;
17             $conf ||= {};
18              
19             my $config = $conf->{config} || DEFAULT_CONFIG_FILE;
20             my $css_url = $conf->{css_url} || DEFAULT_CSS_URL;
21              
22             my $sprite_images = ref($config) eq 'HASH'
23             ? $config
24             : $self->_parse_xml_config($app, $config)
25             ;
26              
27             unless (scalar %$sprite_images) {
28             $app->log->warn( __PACKAGE__ .": Plugin not activated.");
29             }
30              
31             $app->hook(after_dispatch => sub {
32             my ($c) = @_;
33             my $res = $c->res;
34              
35             # Only successful response
36             return if $res->code !~ m/^2/;
37              
38             # Only html response
39             return unless $res->headers->content_type;
40             return if $res->headers->content_type !~ /html/;
41             return if $res->body !~ /^\s*
42              
43             # Skip if "?no_sprites=1" and mode is 'develompent'
44             return if $app->mode eq 'development' && $c->param('no_sprites');
45              
46             my $dom = $parser->parse_html_string($res->body);
47              
48             # Replace "img" tags
49             foreach my $img ( $dom->findnodes('//img') ) {
50             # Skip if "src" is empty
51             my $src = $img->getAttribute('src') or last;
52              
53             # Remove first "/"
54             $src =~ s/^\///;
55              
56             # Skip if not found
57             my $selector = $sprite_images->{$src} or last;
58              
59             # Change node name
60             $img->setNodeName('span');
61              
62             # Add single space
63             $img->appendText(' ');
64              
65             # Add 'class' and 'id' attribute
66             my @class = grep { $_ } ($img->getAttribute('class'), 'spr');
67             if ( substr($selector, 0, 1) eq '.' ) {
68             push @class, substr($selector, 1);
69             }
70             else {
71             $img->setAttribute( 'id', substr($selector, 1) );
72             }
73             $img->setAttribute( 'class', join(' ', @class) );
74              
75             # Remove unneeded attributes
76             $img->removeAttribute($_) for qw(src width height alt);
77             }
78              
79             # Add 'sprite.css'
80             foreach my $head ( $dom->findnodes('/html/head') ) {
81             my $style = $head->addNewChild(undef, 'link');
82             $style->setAttribute('rel', 'stylesheet');
83             $style->setAttribute('href', $css_url);
84             }
85              
86             $res->body( $dom->toStringHTML() );
87             });
88             }
89              
90             sub _parse_xml_config {
91             my ($self, $app, $config) = @_;
92              
93             unless (-e $config) {
94             $app->log->warn( __PACKAGE__ .": Config file '$config' does not exists.");
95             return {};
96             }
97              
98             my $parser = XML::LibXML->new();
99             my $dom = $parser->parse_file($config);
100              
101             my %sprite_images = map {
102             $_->getAttribute('image') => $_->getAttribute('selector')
103             } $dom->findnodes('/root/sprite/image');
104              
105             return \%sprite_images;
106             }
107              
108             1;
109             __END__