CSS-Compressor
view release on metacpan or search on metacpan
Compressor.pm view on Meta::CPAN
package CSS::Compressor;
use strict;
use warnings;
use Exporter qw( import );
our @EXPORT_OK = qw( css_compress );
our $VERSION = '0.05';
our $MARKER;
# take package name, replace double colons with underscore and use that as
# marker for search and replace operations
BEGIN {
$MARKER = uc __PACKAGE__;
$MARKER =~ tr!:!_!s;
}
# build optimized regular expression variables ( foo -> [Ff][Oo][Oo] )
my (
$RE_BACKGROUND_POSITION,
$RE_TRANSFORM_ORIGIN_MOZ,
$RE_TRANSFORM_ORIGIN_MS,
$RE_TRANSFORM_ORIGIN_O,
$RE_TRANSFORM_ORIGIN_WEBKIT,
$RE_TRANSFORM_ORIGIN,
$RE_BORDER,
$RE_BORDER_TOP,
$RE_BORDER_RIGHT,
$RE_BORDER_BOTTOM,
$RE_BORDER_LEFT,
$RE_OUTLINE,
$RE_BACKGROUND,
$RE_ALPHA_FILTER,
) = map +(
join '' => map m![a-zA-Z]!
? '['.ucfirst($_).lc($_).']'
: '\\'.$_,
split m//
) => qw[
background-position
moz-transform-origin
ms-transform-origin
o-transform-origin
webkit-transform-origin
transform-origin
border
border-top
border-right
border-bottom
border-right
outline
background
progid:DXImageTransform.Microsoft.Alpha(Opacity=
];
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# compress
#
# IN: 1 uncompressed CSS
# OUT: 1 compressed CSS
sub css_compress {
my ( $css ) = @_;
my @comments,
my @tokens;
# collect all comment blocks...
$css =~ s! /\* (.*?) \*/
! '/*___'.$MARKER.'_PRESERVE_CANDIDATE_COMMENT_'.
( -1 + push @comments => $1 ).'___*/'
!sogex;
# preserve urls to prevent breaking inline SVG for example
$css =~ s! ( url \( ( (?: [^()]++ | \( (?2) \) )*+ ) \) ) !
'___'.$MARKER.'_PRESERVED_TOKEN_'.(-1+push @tokens => $1).'___'
!gxe;
# preserve strings so their content doesn't get accidentally minified
$css =~ s! " ( [^"\\]*(?:\\.[^"\\]*)* ) " !
$_ = $1,
# maybe the string contains a comment-like substring?
# one, maybe more? put'em back then
s/___${MARKER}_PRESERVE_CANDIDATE_COMMENT_([0-9]+)___/$comments[$1]/go,
# minify alpha opacity in filter strings
s/$RE_ALPHA_FILTER/alpha(opacity=/go,
'"___'.$MARKER.'_PRESERVED_TOKEN_'.(-1+push @tokens => $_).'___"'
!sgxe;
$css =~ s! ' ( [^'\\]*(?:\\.[^'\\]*)* ) ' !
$_ = $1,
s/___${MARKER}_PRESERVE_CANDIDATE_COMMENT_([0-9]+)___/$comments[$1]/go,
s/$RE_ALPHA_FILTER/alpha(opacity=/go,
'\'___'.$MARKER.'_PRESERVED_TOKEN_'.(-1+push @tokens => $_).'___\''
!sgxe;
# strings are safe, now wrestle the comments
# ! in the first position of the comment means preserve
# so push to the preserved tokens while stripping the !
0 == index $_->[1] => '!'
and -1 == index $_->[1] => '! @noflip'
and
$css =~ s!___${MARKER}_PRESERVE_CANDIDATE_COMMENT_$_->[0]___!
'___'.$MARKER.'_PRESERVED_TOKEN_'.(-1+push @tokens => $_->[1]).'___'!e
# keep empty comments after child selectors (IE7 hack)
# e.g. html >/**/ body
or 0 == length $_->[1]
( run in 1.565 second using v1.01-cache-2.11-cpan-5735350b133 )