From f17fd1e081231c38a3dcdf6390f008141abd96c9 Mon Sep 17 00:00:00 2001 From: Dobrica Pavlinusic Date: Sat, 7 Apr 2012 16:39:04 +0200 Subject: [PATCH] Bug 1918 - Minify JavaScript and CSS This provides new Koha::Template::Plugin::Combine which is intended to replace direct include of JavaScript and CSS files. This plugin preserves order of included files when combining them. Code need cleanup and documentation, but attaching it to bug will provide easier way to try it out. --- Koha/Template/Plugin/Combine.pm | 141 +++++++++++++++++++ .../intranet-tmpl/prog/en/css/staff-global.css | 3 +- .../prog/en/includes/doc-head-close.inc | 46 ++++--- koha-tmpl/opac-tmpl/prog/en/css/opac.css | 3 - .../opac-tmpl/prog/en/includes/doc-head-close.inc | 143 +++++++++++++------- koha-tmpl/opac-tmpl/prog/en/js/script.js | 1 - 6 files changed, 259 insertions(+), 78 deletions(-) create mode 100644 Koha/Template/Plugin/Combine.pm diff --git a/Koha/Template/Plugin/Combine.pm b/Koha/Template/Plugin/Combine.pm new file mode 100644 index 0000000..c84bc7f --- /dev/null +++ b/Koha/Template/Plugin/Combine.pm @@ -0,0 +1,141 @@ +package Koha::Template::Plugin::Combine; +use Template::Plugin; +use base qw( Template::Plugin ); +use warnings; +use strict; + +# PLACK_DEBUG=1 always re-create files but report correct hit/miss ration + +use File::Slurp; +use Digest::MD5; +use Data::Dump qw(dump); + +use CSS::Minifier::XS; +use JavaScript::Minifier::XS; + +sub new { + my ($class, $context, @params) = @_; +#warn "## context ",dump( $context ); + bless { + _CONTEXT => $context, + js => [], # same as extension! + css => [], + inline => 1, + minify => 1, + }, $class; +} + +sub _html_params { + my $params = shift; + return join(' ', map { $_ . '="' . $params->{$_} . '"' } keys %$params ); +} + +sub javascript { + my ( $self, $params ) = @_; + warn "## combine.javascript ", dump( $params ); + $params->{type} ||= 'text/javascript'; + push @{ $self->{js} }, $params; + my $html = _html_params( $params ); + if ( $self->{inline} ) { + return "\n"; + } elsif ( my $cdata = $params->{cdata} ) { + return qq|\n|; + } else { + return "\n"; + } +} + +sub css { + my ( $self, $params ) = @_; + warn "## combine.css ",dump( $params ); + $params->{rel} ||= 'stylesheet'; + $params->{type} ||= 'text/css'; + die "no href in ",dump($params) unless exists $params->{href}; + my $html = _html_params( $params ); + + if ( exists $params->{media} ) { # FIXME not combined! + push @{ $self->{css_media} }, ""; + return ""; + } + + push @{ $self->{css} }, $params; + if ( $self->{inline}) { + return "\n"; + } else { + return "\n"; + } +} + +sub combined_files { + my ( $self, $what, $attr, $params ) = @_; +warn "## combined_files $what $attr ",dump($params); + my $key = join(' ', map { $_->{$attr} || $_->{cdata} } @{ $self->{$what} } ); + $key = Digest::MD5::md5_hex( $key ); # shorten filenames + warn "# $what $attr key = $key\n"; + my $htdocs = C4::Context->config('intrahtdocs'); + $htdocs =~ s{/intranet-tmpl/?$}{}; # FIXME DocumentRoot even for OPAC + + my ( $path, $url ); + if ( my $prefix = $params->{prefix} ) { + $path = "$htdocs/$prefix/combined/$key.$what"; + $url = "$prefix/combined/$key.$what"; + } else { + $path = "$htdocs/intranet-tmpl/combined/$key.$what"; + $url = "/intranet-tmpl/combined/$key.$what"; + } + $path =~ s{//+}{/}; # plack is picky about paths + if ( -e $path ) { + $Koha::Persistant::stats->{combine_files}->[0]++; # hit + return $url if ! $ENV{PLACK_DEBUG}; # FIXME no caching with debug + } + $Koha::Persistant::stats->{combine_files}->[1]++ if ! $ENV{PLACK_DEBUG}; # miss + + my $mix; + foreach my $include ( @{ $self->{$what} } ) { + if ( my $path = $include->{$attr} ) { + warn "combine_files $what $attr - $path"; + my $chunk = read_file("$htdocs/$path"); + die "$htdocs/$path found \@import" if $what eq 'css' && $chunk =~ m'@import'; + $mix .= "\n/* BEGIN $path */\n$chunk\n/* END $path */\n"; + } elsif ( my $cdata = $include->{cdata} ) { + warn "combine_files CDATA $cdata"; + $mix .= "\n/* BEGIN CDATA */\n$cdata\n/* END CDATA */\n"; + } + } + my $combined_size = length($mix); + if ( $self->{minify} ) { + if ( $what eq 'js' ) { + $mix = JavaScript::Minifier::XS::minify( $mix ); + } elsif ( $what eq 'css' ) { + $mix = CSS::Minifier::XS::minify( $mix ); + } else { + die "can't minify $what"; + } + my $size = length($mix); + warn sprintf "minify %s %d -> %d %.2f%%\n", $what, $combined_size, $size, $size * 100 / $combined_size; + } + write_file $path, $mix; + warn "## $path ", -s $path, "/$combined_size bytes\n"; + return $url; +} + +sub html { + my ( $self, $params ) = @_; + + if ( $self->{inline} ) { + warn "## css ",dump( $self->{css} ); + warn "## javascript ",dump( $self->{js} ); + my $mix; + my $js = $self->combined_files( 'js' => 'src', $params ); + my $css = $self->combined_files( 'css' => 'href', $params ); + return qq{ + + + + }; + } else { + return ""; + } +} + +1; diff --git a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css index ef01648..3519d20 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css +++ b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css @@ -1,5 +1,4 @@ -@import url("../lib/yui/reset-fonts-grids.css"); -@import url("../lib/yui/skin.css"); +/* import moved to koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-close.inc */ a, a:link, a:visited, a:active { color : #0000CC; diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-close.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-close.inc index bc551ac..b15aea4 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-close.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-close.inc @@ -1,42 +1,45 @@ +[% USE combine = Combine %] - - +[% combine.css( href => themelang _ "/lib/jquery/plugins/ui.tabs.css" ) %] +[% combine.css( media => "print" href => themelang _ "/css/print.css" ) %] [% IF ( intranetstylesheet ) %] - + [% combine.css( href => intranetstylesheet ) %] [% ELSE %] - + [% combine.css( href => yuipath _ "/reset-fonts-grids.css" ) %] + [% combine.css( href => yuipath _ "/skin.css" ) %] + [% combine.css( href => themelang _ "/css/staff-global.css" ) %] [% END %] [% IF ( IntranetUserCSS ) %][% END %] - - - - - +[% combine.javascript( src => themelang _ "/lib/jquery/jquery.js" ) %] +[% combine.javascript( src => themelang _ "/lib/jquery/plugins/ui.tabs.js" ) %] +[% combine.javascript( src => themelang _ "/lib/jquery/plugins/jquery.hotkeys.min.js" ) %] +[% combine.javascript( src => themelang _ "/lib/jquery/plugins/jquery.cookie.min.js" ) %] +[% combine.javascript( src => themelang _ "/lib/jquery/plugins/jquery.highlight-3.js" ) %] [% IF ( login ) %] - + [% combine.css( href => themelang _ "/css/login.css" ) %] [% END %] [% IF ( wizard ) %] - + [% combine.css( href => themelang _ "/css/wizard.css" ) %] [% END %] [% IF ( intranetcolorstylesheet ) %] - + [% combine.css( href => themelang _ "/css/" _ intranetcolorstylesheet ) %] [% END %] - - +[% combine.javascript( src => yuipath _ "/utilities/utilities.js" ) %] +[% combine.javascript( src => yuipath _ "/datasource/datasource-min.js" ) %] [% IF ( CircAutocompl ) %] - + [% combine.javascript( src => yuipath _ "/autocomplete/autocomplete-min.js" ) %] [% END %] - - - +[% combine.javascript( src => yuipath _ "/button/button-min.js" ) %] +[% combine.javascript( src => yuipath _ "/container/container_core-min.js" ) %] +[% combine.javascript( src => yuipath _ "/menu/menu-min.js" ) %] - +[% combine.javascript( src => themelang _ "/js/staff-global.js" ) %] [% IF ( intranetuserjs ) %] [% IF ( virtualshelves || intranetbookbag ) %] - + [% combine.javascript( src => themelang _ "/js/basket.js" ) %] [% END %] [% IF LocalCoverImages %] - +[% combine.javascript( src => themelang _ "/js/localcovers.js" ) %] [% END %] +[% combine.html( prefix => themelang ) %] diff --git a/koha-tmpl/opac-tmpl/prog/en/css/opac.css b/koha-tmpl/opac-tmpl/prog/en/css/opac.css index c10e08d..c48a59c 100644 --- a/koha-tmpl/opac-tmpl/prog/en/css/opac.css +++ b/koha-tmpl/opac-tmpl/prog/en/css/opac.css @@ -1,6 +1,3 @@ -@import url("../lib/yui/reset-fonts-grids.css"); -@import url("../lib/yui/skin.css"); - a { font-weight : bold; } diff --git a/koha-tmpl/opac-tmpl/prog/en/includes/doc-head-close.inc b/koha-tmpl/opac-tmpl/prog/en/includes/doc-head-close.inc index 3907f34..d5ea90e 100644 --- a/koha-tmpl/opac-tmpl/prog/en/includes/doc-head-close.inc +++ b/koha-tmpl/opac-tmpl/prog/en/includes/doc-head-close.inc @@ -2,50 +2,62 @@ -[% IF ( opacstylesheet ) %][% ELSE %] -[% IF ( opaclayoutstylesheet ) %][% ELSE %][% END %][% END %] +[% USE combine = Combine %] +[% IF ( opacstylesheet ) %] + [% combine.css( href => opacstylesheet ) %] +[% ELSE %] + [% IF ( opaclayoutstylesheet ) %] + [% combine.css( href => themelang _ "/css/" _ opaclayoutstylesheet ) %] + [% ELSE %] + [% combine.css( href => themelang _ "/css/opac.css" ) %] + [% END %] +[% END %] [% IF ( opaccolorstylesheet ) %] - + [% combine.css( href => themelang _ "/css/" _ opaccolorstylesheet ) %] [% END %] [% IF ( opacexternalsheet ) %] - + [% combine.css( href => opacexternalsheet ) %] [% END %] [% IF ( opac_css_override ) %] - + [% combine.css( href => therelang _ "/css/" _ opac_css_override ) %] [% END %] - +[% combine.css( href => themelang _ "/lib/jquery/plugins/ui.tabs.css" ) %] [% IF ( OPACUserCSS ) %][% END %] - - - - - - - +[% combine.css( href => yuipath _ "/reset-fonts-grids.css" ) %] +[% combine.css( href => yuipath _ "/skin.css" ) %] +[% combine.javascript( src => yuipath _ "/utilities/utilities.js" ) %] +[% combine.javascript( src => yuipath _ "/container/container-min.js" ) %] +[% combine.javascript( src => yuipath _ "/menu/menu-min.js" ) %] +[% combine.javascript( src => themelang _ "/lib/jquery/jquery.js" ) %] +[% combine.javascript( src => themelang _ "/lib/jquery/plugins/ui.tabs.js" ) %] +[% combine.javascript( src => themelang _ "/lib/jquery/plugins/jquery.hoverIntent.minified.js" ) %] +[% combine.javascript( src => themelang _ "/js/script.js" ) %] + [% IF ( OPACAmazonCoverImages ) %] - - +[% combine.javascript( cdata => "var NO_AMAZON_IMAGE = _(\"No cover image available\");" ) %] +[% combine.javascript( src => themelang _ "/js/amazonimages.js" ) %] [% END %] [% IF ( SyndeticsCoverImages ) %] - - [% END %] +[% combine.javascript( cdata => "var NO_AMAZON_IMAGE = _(\"No cover image available\");" ) %] +[% combine.javascript( src => themelang _ "/js/amazonimages.js" ) %] +[% END %] -[% IF ( opacbookbag ) %] +[% IF ( opacbookbag ) %] + [% combine.javascript( src => themelang _ "/js/basket.js" ) %] +[% ELSIF ( virtualshelves ) %] + [% combine.javascript( src => themelang _ "/js/basket.js" ) %] +[% ELSE %] + [% combine.javascript( cdata => "var readCookie;" ) %] +[% END %] +[% combine.javascript( cdata => "var cdata_test;" ) %] -[% IF ( opacuserlogin ) %][% IF ( TagsEnabled ) %][% END %][% ELSE %][% END %] + +[% IF ( opacbookbag ) %] +[% compose.javascript( src => themelang _ "/js/basket.js" ) %] +[% ELSIF ( virtualshelves ) %] +[% compose.javascript( src => themelang _ "/js/basket.js" ) %] +[% ELSE %] + +[% END %] + +[% IF ( opacuserlogin ) %] + [% IF ( TagsEnabled ) %] + [% combine.javascript( src => themelang _ "/js/tags.js" ) %] + [% END %] +[% END %] + [% IF ( GoogleJackets ) %] - +[% combine.javascript( src => themelang _ "/js/google-jackets.js" ) %] [% END %] [% IF OpenLibraryCovers %] - +[% combine.javascript( src => themelang _ "/js/openlibrary.js" ) %] +[% combine.javascript( src => themelang _ "/js/localcovers.js" ) %] [% END %] -[% IF ( BakerTaylorEnabled ) %] +[% IF ( BakerTaylorEnabled ) %] +[% combine.javascript( src => themelang _ "/js/bakertaylorimages.js" ) %] [% END %] + +[% END %] + [% IF ( GoogleIndicTransliteration ) %] - - + + [% combine.javascript( src => themelang _ "/js/googleindictransliteration.js" ) %] [% END %] - +[% combine.html( prefix => themelang ) %] diff --git a/koha-tmpl/opac-tmpl/prog/en/js/script.js b/koha-tmpl/opac-tmpl/prog/en/js/script.js index a3cfab8..67ea771 100644 --- a/koha-tmpl/opac-tmpl/prog/en/js/script.js +++ b/koha-tmpl/opac-tmpl/prog/en/js/script.js @@ -1,4 +1,3 @@ -function _(s) { return s } // dummy function for gettext function confirmDelete(message) { return (confirm(message) ? true : false); } -- 1.7.2.5