Robson » Code Viewer

<?
 
/*
   CSS Compressor v0.6
   http://iceyboard.no-ip.org/projects/css_compressor
   Copyright (C) 2008 Robson
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
 
// functions appear in the order they are first called
 
compress_css();
 
function compress_css()
{
   // make these variables available locally
   global $file_selector;
   global $file_props;
   // first, get the css that was sent, referenced or uploaded
   $cssfile = get_sent_css();
 
   // check if the user wants to see statistics
   if ($_POST['opt_output_stats'])
   {
       // save the size of the code
       $start_size = strlen($cssfile);
       // save the current time
       $start = explode(' ', microtime());
   }
   
   // check if no css was found
   if (!$cssfile)
       // just die
       exit ("/* You didn't upload anything or the stylesheet is empty - Ro" . "bson */");
   
   // temporarily change semicolons in web links
   $cssfile = str_replace('://', '[!semi-colon!]//', $cssfile);
   
   // remove html and css comments    
   kill_comments($cssfile);
   
   // trim whitespace from the start and end
   $cssfile = trim($cssfile);
   
   // turn all rgb values into hex
   if ($_POST['opt_rgb2hex'])
       rgb2hex($cssfile);
   
   // shorten colours
   if ($_POST['opt_colours2hex'])
       long_colours_to_short_hex($cssfile);
   if ($_POST['opt_hex2colours'])    
       long_hex_to_short_colours($cssfile);
   
   // remove any extra measurements that aren't needed
   if ($_POST['opt_remove_zeros'])
       remove_zero_measurements($cssfile);
       
   // seperate into selectors and properties
   sort_css($cssfile);
   
   // change font weight text into numbers
   if ($_POST['opt_text_weights_to_numbers'])
       font_weight_text_to_numbers($cssfile);        
   
   // check if any selectors are used twice and combine the properties
   if ($_POST['opt_combine_identical_selectors'])
       combine_identical_selectors();
   
   // remove any properties which are declared twice in one rule
   if ($_POST['opt_remove_overwritten_properties'])
       remove_overwritten_properties();
       
   // check if properties should be combined
   if ($_POST['opt_combine_props_list'])
   {
       // for each rule in the file
       for ($n = 0; $n < count($file_props); $n++)
           // attempt to combine the different parts
           combine_props_list($file_props[$n]);    
   }
   
   // for each rule in the file
   for ($n = 0; $n < count($file_props); $n++)
       // run all the individual functions to reduce their size
       array_walk($file_props[$n], 'reduce_prop');
       
   // remove all the properties that were blanked out earlier
   remove_empty_rules();
   
   // check if any rules are the same as other ones and remove the first ones
   if ($_POST['opt_combine_identical_rules'])
       combine_identical_rules();
   
   // one final run through to remove all unnecessary parts of the arrays
   remove_empty_rules();
   
   $output = create_output();
   
   // turn back colons
   $output = str_replace('[!semi-colon!]//', '://', $output);
 
   $output = stripslashes($output);
   
   // check if the user wants to view stats
   if ($_POST['opt_output_stats'])
   {
       echo '<h1>Statistics</h1><ul>';
       echo '<li>Original size: ' . round($start_size / 1024, 2) . ' kB (' . number_format($start_size) . ' B)</li>';
       echo '<li>Final size: ' . round(strlen(strip_tags($output)) / 1024, 2) . ' kB (' . number_format(strlen(strip_tags($output))) . ' B)</li>';
       echo '<li>Saved: ' . round(($start_size - strlen(strip_tags($output))) / 1024, 2) . ' kB (' . number_format(($start_size - strlen(strip_tags($output)))) . ' B)</li>';
       echo '<li>Reduction: ' . round(100 - ((strlen(strip_tags($output)) / $start_size) * 100), 2) . '%</li>';
       echo '</ul>';
       
       $finish = explode(' ', microtime());        
       
       // work out the differences between the times
       $seconds = $finish[1] - $start[1];
       $miliseconds = $finish[0] - $start[0];        
       
       $duration = round($seconds + $miliseconds, 5);
       
       echo '<ul>';
       echo '<li>Duration: ' . $duration . ' seconds</li>' ;
       echo '</ul>';
       
       echo '<ul>';
       echo '<li>Rules: ' . count($file_selector) . '</li>';
 
       for ($n = 0; $n < count($file_selector); $n++)
           $selectors += count($file_selector[$n]);
       
       for ($n = 0; $n < count($file_props); $n++)
           $props += count($file_props[$n]);
 
       echo '<li>Selectors: ' . $selectors . '</li>';
       echo '<li>Properties: ' . $props . '</li>';
       
       
       echo '</ul>';
       echo '<h1>CSS</h1>';
   }
   
   echo $output;
}
 
// retrieves the css sent by the user
// can handle uploaded stylesheets, url references and directly uploaded css
function get_sent_css()
{
   // check if they uploaded a file
   if ($_FILES['upload_file']['name'])
       // grab the uploaded file from it's temporary position
       // the temporary file is deleted at the end of the scripts execution
       // this implodes all the lines of the file into one simple variable
       return implode('', file($_FILES['upload_file']['tmp_name']));
   // the eregi check is for security, it ensures people don't request files on the local server
   else if($_POST['upload_link'] != 'http://' && eregi('http:\/\/*', $_POST['upload_link']))
   {
       // grab a remote file for compressing
       // the at symbol stops php from producing errors, because i've specified one
       $cssfile = @file($_POST['upload_link']) or die("/* Error: Sorry, that URL couldn't be found. */");
       // implode the code
       return implode('', $cssfile);    
   }
   // check if they posted css
   else if($_POST['upload_style'])
       // store the css into the css file variable
       return $_POST['upload_style'];
   // the user wants to combine multiple uploaded files    
   else if ($_FILES['upload_file_1']['name'])
   {
       // add the first file to the current css code
       $file = implode('', file($_FILES['upload_file_1']['tmp_name']));
       // check if they added another file
       if ($_FILES['upload_file_2']['name'])
           // if so, add that one to the css code string
           $file .= implode('', file($_FILES['upload_file_2']['tmp_name']));
       // check if they added another file            
       if ($_FILES['upload_file_3']['name'])
           // if so, add that one to the css code string
           $file .= implode('', file($_FILES['upload_file_3']['tmp_name']));
       // check if they added another file            
       if ($_FILES['upload_file_4']['name'])
           // if so, add that one to the css code string        
           $file .= implode('', file($_FILES['upload_file_4']['tmp_name']));
       // check if they added another file            
       if ($_FILES['upload_file_5']['name'])
           // if so, add that one to the css code string
           $file .= implode('', file($_FILES['upload_file_5']['tmp_name']));
       // check if they added another file            
       if ($_FILES['upload_file_6']['name'])
           // if so, add that one to the css code string
           $file .= implode('', file($_FILES['upload_file_6']['tmp_name']));
       // check if they added another file            
       if ($_FILES['upload_file_7']['name'])
           // if so, add that one to the css code string
           $file .= implode('', file($_FILES['upload_file_7']['tmp_name']));
       // check if they added another file            
       if ($_FILES['upload_file_8']['name'])
           // if so, add that one to the css code string
           $file .= implode('', file($_FILES['upload_file_8']['tmp_name']));
       // check if they added another file            
       if ($_FILES['upload_file_9']['name'])
           // if so, add that one to the css code string
           $file .= implode('', file($_FILES['upload_file_9']['tmp_name']));
       // check if they added another file            
       if ($_FILES['upload_file_10']['name'])
           // if so, add that one to the css code string
           $file .= implode('', file($_FILES['upload_file_10']['tmp_name']));
   
       return $file;
   }
   else
       return NULL;
}
 
// this removes html and css comments from the file
function kill_comments(&$css)
{
   // kill html comments
   $css = str_replace('<!--', '', $css);
   $css = str_replace('-->', '', $css);
   // kill css comments
   $css = preg_replace('/\/\*(.*?)\*\//si', '', $css);
}
 
// converts any rgb values to hex values
// i.e. rgb(255,170,0) -> #ffaa00
function rgb2hex(&$string)
{
   // loop only while there are rgb values in the string
   while (strpos($string, 'rgb'))
   {
       // find the location of the first rgb value
       $where = strpos($string, 'rgb');
       // add everything before to the new string
       $text .= substr($string, 0, $where);
       // remove the before part from the original string
       $string = substr($string, $where, strlen($string));
       // find the end of the rgb value
       $where = strpos($string, ')');
 
       // get the rgb value, like 'rgb(255, 170, 0)'
       $rgb = substr($string, 0, $where+1);
       
       // remove spaces, like 'rgb(255,170,0)'
       $rgb = eregi_replace(' +', '', $rgb);
       // remove the parts that aren't values, like '255,170,0'
       $rgb = substr($rgb, 4, -1);
       
       // explode the values into an array, like 255|170|0
       $rgb = explode(',', $rgb);
 
       // set colour to nothing so it doesn't use the previous value
       $colour = '';
       // loop through each rgb value, for red, green and blue
       for ($n = 0; $n < 3; $n++)
           // ff or 0f - always return two characters
           $colour .= strlen(dechex($rgb[$n])) == 1 ? '0' . dechex($rgb[$n]) : dechex($rgb[$n]) ;
 
       // 'ffaa00' - add the six-character hex value
       $text .= '#' . $colour;
       // remove the rgb property from the string
       $string = substr($string, $where+1, strlen($string));
   }
   // add the remaining parts of the file back to the original string
   // return the new string with rgb values converted to hex values
   $string = $text . $string;
}
 
// turn long colour names into short hex codes
// for example: fuscia -> #ff00ff (which is then compressed later to #f0f)
function long_colours_to_short_hex(&$string)
{
   // first, change colour names into hex values
   // hex values are shortened later, so this is done now
   $colours = array(array('000000', 'black'), array('ff00ff', 'fuchsia'), array('ffff00', 'yellow'));
   // loop through each colour
   for ($n = 0; $n < count($colours); $n++)
       // replace all instances of this colour with the hex code
       $string = str_replace(":" . $colours[$n][1], ':#' . $colours[$n][0], $string);
   
   // convert instances of 'white' to #fff
   $string = eregi_replace("white([^-])", "#ffffff\\1", $string);
}
   
// this converts hex colour codes to shorter text equivilants
// only the standard sixteen colours codes are used here
function long_hex_to_short_colours(&$string)
{    
   // the colours that are shorter than the hex representation of them
   $colours = array(
               array('808080', 'gray'), array('008000', 'green'), array('800000', 'maroon'),
               array('00080', 'navy'), array('808000', 'olive'), array('800080', 'purple'),
               array('ff0000', 'red'), array('c0c0c0', 'silver'), array('008080', 'teal')
                   );
                   
   // loop through each colour
   for ($n = 0; $n < count($colours); $n++)
       // replace hex with colour name
       // ie #808080 -> gray
       $string = str_replace('#' . $colours[$n][0], $colours[$n][1], $string);
}
 
// zero is always zero, so measurements don't matter
function remove_zero_measurements(&$string)
{
   // change 0 ems, 0 pixels and 0 percentages to 0
   // this wont change values like 10px, since those do need measurements
   $string = trim(eregi_replace('([^0-9])0(px|em|\%)', '\\10', ' ' . $string));
   $string = trim(eregi_replace('([^0-9])0\.([0-9]+)em', '\\1.\\2em', ' ' . $string));
}
 
// this seperates the css file into it's rules,
// which it then sends to another function to sort into each part
function sort_css($cssfile)
{
   // the first thing to do is seperate everything out in the file
   // so loop round each rule in the file
   while ($cssfile)
   {
       // check if there is some more code
       if (substr_count($cssfile, '}'))
       {
           // the next rule is everything up to the squiggly bracket
           $rule = substr($cssfile, 0, strpos($cssfile, '}')+1);
           // seperate out everything in this rule and add it to different global arrays
           parse_rules($rule);
           // remove that rule from the css file
           $cssfile = substr($cssfile, strlen($rule), strlen($cssfile));
       }    
       // no more rules?
       else
           // kill the css file variable to terminate this loop
           unset($cssfile);
   }
}
 
// turns font-weight text into numbers
// for example: font-weight:normal -> font-weight:400
function font_weight_text_to_numbers(&$string)
{
   // make these variables available to this function
   global $file_selector;
   global $file_props;
   
   for ($a = 0; $a < count($file_props); $a++)
   {
       for ($b = 0; $b < count($file_props[$a]); $b++)
       {
           if ($file_props[$a][$b] == 'font-weight:bold')
               $file_props[$a][$b] = 'font-weight:700';
           if ($file_props[$a][$b] == 'font-weight:normal')
               $file_props[$a][$b] = 'font-weight:400';
               
           $file_props[$a][$b] = str_replace('font:normal', 'font:400', $file_props[$a][$b]);