Csstidy fixes
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 13 Oct 2009 16:44:36 +0000 (10:44 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Wed, 14 Oct 2009 09:37:18 +0000 (03:37 -0600)
Most important, allow multiple declarations of a property within a
single CSS declaration.  Allows us to take advantage of CSS hacks
without splitting CSS files into 8 different versions based on browser
type.
While here, start cleaning these files up (i.e. disable
logging, since it is not used anywhere and is just wasting memory).
This can be cleaned up more - there are definitely things going on in
here that are eating up memory, for example.

framework/Text_Filter_Csstidy/lib/Horde/Text/Filter/Csstidy/class.csstidy.php
framework/Text_Filter_Csstidy/lib/Horde/Text/Filter/Csstidy/class.csstidy_optimise.php
framework/Text_Filter_Csstidy/lib/Horde/Text/Filter/Csstidy/class.csstidy_print.php

index 3b17355..dd76729 100644 (file)
 
 /**
  * Various CSS data needed for correct optimisations etc.
- *
- * @version 1.3
  */
 require('data.inc.php');
 
 /**
  * Contains a class for printing CSS code
- *
- * @version 1.0
  */
 require('class.csstidy_print.php');
 
 /**
  * Contains a class for optimising CSS code
- *
- * @version 1.0
  */
 require('class.csstidy_optimise.php');
 
@@ -56,946 +50,929 @@ require('class.csstidy_optimise.php');
  * An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php
  * @package csstidy
  * @author Florian Schmitz (floele at gmail dot com) 2005-2006
- * @version 1.3
  */
 class csstidy {
 
-/**
- * Saves the parsed CSS
- * @var array
- * @access public
- */
-var $css = array();
-
-/**
- * Saves the parsed CSS (raw)
- * @var array
- * @access private
- */
-var $tokens = array();
-
-/**
- * Printer class
- * @see csstidy_print
- * @var object
- * @access public
- */
-var $print;
-
-/**
- * Optimiser class
- * @see csstidy_optimise
- * @var object
- * @access private
- */
-var $optimise;
-
-/**
- * Saves the CSS charset (@charset)
- * @var string
- * @access private
- */
-var $charset = '';
-
-/**
- * Saves all @import URLs
- * @var array
- * @access private
- */
-var $import = array();
-
-/**
- * Saves the namespace
- * @var string
- * @access private
- */
-var $namespace = '';
-
-/**
- * Contains the version of csstidy
- * @var string
- * @access private
- */
-var $version = '1.3';
-
-/**
- * Stores the settings
- * @var array
- * @access private
- */
-var $settings = array();
-
-/**
- * Saves the parser-status.
- *
- * Possible values:
- * - is = in selector
- * - ip = in property
- * - iv = in value
- * - instr = in string (started at " or ' or ( )
- * - ic = in comment (ignore everything)
- * - at = in @-block
- *
- * @var string
- * @access private
- */
-var $status = 'is';
-
-
-/**
- * Saves the current at rule (@media)
- * @var string
- * @access private
- */
-var $at = '';
-
-/**
- * Saves the current selector
- * @var string
- * @access private
- */
-var $selector = '';
-
-/**
- * Saves the current property
- * @var string
- * @access private
- */
-var $property = '';
-
-/**
- * Saves the position of , in selectors
- * @var array
- * @access private
- */
-var $sel_separate = array();
-
-/**
- * Saves the current value
- * @var string
- * @access private
- */
-var $value = '';
-
-/**
- * Saves the current sub-value
- *
- * Example for a subvalue:
- * background:url(foo.png) red no-repeat;
- * "url(foo.png)", "red", and  "no-repeat" are subvalues,
- * seperated by whitespace
- * @var string
- * @access private
- */
-var $sub_value = '';
+    /**
+     * Saves the parsed CSS
+     * @var array
+     * @access public
+     */
+    var $css = array();
+
+    /**
+     * Saves the parsed CSS (raw)
+     * @var array
+     * @access private
+     */
+    var $tokens = array();
+
+    /**
+     * Printer class
+     * @see csstidy_print
+     * @var object
+     * @access public
+     */
+    var $print;
+
+    /**
+     * Optimiser class
+     * @see csstidy_optimise
+     * @var object
+     * @access private
+     */
+    var $optimise;
+
+    /**
+     * Saves the CSS charset (@charset)
+     * @var string
+     * @access private
+     */
+    var $charset = '';
+
+    /**
+     * Saves all @import URLs
+     * @var array
+     * @access private
+     */
+    var $import = array();
+
+    /**
+     * Saves the namespace
+     * @var string
+     * @access private
+     */
+    var $namespace = '';
+
+    /**
+     * Stores the settings
+     * @var array
+     * @access private
+     */
+    var $settings = array();
+
+    /**
+     * Saves the parser-status.
+     *
+     * Possible values:
+     * - is = in selector
+     * - ip = in property
+     * - iv = in value
+     * - instr = in string (started at " or ' or ( )
+     * - ic = in comment (ignore everything)
+     * - at = in @-block
+     *
+     * @var string
+     * @access private
+     */
+    var $status = 'is';
+
+
+    /**
+     * Saves the current at rule (@media)
+     * @var string
+     * @access private
+     */
+    var $at = '';
+
+    /**
+     * Saves the current selector
+     * @var string
+     * @access private
+     */
+    var $selector = '';
+
+    /**
+     * Saves the current property
+     * @var string
+     * @access private
+     */
+    var $property = '';
+
+    /**
+     * Saves the position of , in selectors
+     * @var array
+     * @access private
+     */
+    var $sel_separate = array();
+
+    /**
+     * Saves the current value
+     * @var string
+     * @access private
+     */
+    var $value = '';
+
+    /**
+     * Saves the current sub-value
+     *
+     * Example for a subvalue:
+     * background:url(foo.png) red no-repeat;
+     * "url(foo.png)", "red", and  "no-repeat" are subvalues,
+     * seperated by whitespace
+     * @var string
+     * @access private
+     */
+    var $sub_value = '';
+
+    /**
+     * Array which saves all subvalues for a property.
+     * @var array
+     * @see sub_value
+     * @access private
+     */
+    var $sub_value_arr = array();
+
+    /**
+     * Saves the char which opened the last string
+     * @var string
+     * @access private
+     */
+    var $str_char = '';
+    var $cur_string = '';
+
+    /**
+     * Status from which the parser switched to ic or instr
+     * @var string
+     * @access private
+     */
+    var $from = '';
+
+    /**
+     * Variable needed to manage string-in-strings, for example url("foo.png")
+     * @var string
+     * @access private
+     */
+    var $str_in_str = false;
+
+    /**
+     * =true if in invalid at-rule
+     * @var bool
+     * @access private
+     */
+    var $invalid_at = false;
+
+    /**
+     * =true if something has been added to the current selector
+     * @var bool
+     * @access private
+     */
+    var $added = false;
+
+    /**
+     * Array which saves the message log
+     * @var array
+     * @access private
+     */
+    var $log = array();
+
+    /**
+     * Saves the line number
+     * @var integer
+     * @access private
+     */
+    var $line = 1;
+
+    /**
+     * Loads standard template and sets default settings
+     * @access private
+     */
+    function csstidy()
+    {
+        $this->settings['remove_bslash'] = true;
+        $this->settings['compress_colors'] = true;
+        $this->settings['compress_font-weight'] = true;
+        $this->settings['lowercase_s'] = false;
+        $this->settings['optimise_shorthands'] = 1;
+        $this->settings['remove_last_;'] = false;
+        $this->settings['case_properties'] = 1;
+        $this->settings['sort_properties'] = false;
+        $this->settings['sort_selectors'] = false;
+        $this->settings['merge_selectors'] = 2;
+        $this->settings['discard_invalid_properties'] = false;
+        $this->settings['css_level'] = 'CSS2.1';
+        $this->settings['preserve_css'] = false;
+        $this->settings['timestamp'] = false;
+        $this->settings['log'] = false;
+
+        $this->load_template('default');
+        $this->print = new csstidy_print($this);
+        $this->optimise = new csstidy_optimise($this);
+    }
 
-/**
- * Array which saves all subvalues for a property.
- * @var array
- * @see sub_value
- * @access private
- */
-var $sub_value_arr = array();
+    /**
+     * Get the value of a setting.
+     * @param string $setting
+     * @access public
+     * @return mixed
+     */
+    function get_cfg($setting)
+    {
+        if(isset($this->settings[$setting]))
+        {
+            return $this->settings[$setting];
+        }
+        return false;
+    }
 
-/**
- * Saves the char which opened the last string
- * @var string
- * @access private
- */
-var $str_char = '';
-var $cur_string = '';
+    /**
+     * Set the value of a setting.
+     * @param string $setting
+     * @param mixed $value
+     * @access public
+     * @return bool
+     */
+    function set_cfg($setting,$value)
+    {
+        if(isset($this->settings[$setting]) && $value !== '')
+        {
+            $this->settings[$setting] = $value;
+            return true;
+        }
+        return false;
+    }
 
-/**
- * Status from which the parser switched to ic or instr
- * @var string
- * @access private
- */
-var $from = '';
+    /**
+     * Adds a token to $this->tokens
+     * @param mixed $type
+     * @param string $data
+     * @param bool $do add a token even if preserve_css is off
+     * @access private
+     */
+    function _add_token($type, $data, $do = false) {
+        if($this->get_cfg('preserve_css') || $do) {
+            $this->tokens[] = array($type, ($type == COMMENT) ? $data : trim($data));
+        }
+    }
 
-/**
- * Variable needed to manage string-in-strings, for example url("foo.png")
- * @var string
- * @access private
- */
-var $str_in_str = false;
+    /**
+     * Add a message to the message log
+     * @param string $message
+     * @param string $type
+     * @param integer $line
+     * @access private
+     */
+    function log($message,$type,$line = -1)
+    {
+        if (!$this->settings['log']) {
+            return;
+        }
 
-/**
- * =true if in invalid at-rule
- * @var bool
- * @access private
- */
-var $invalid_at = false;
+        if($line === -1)
+        {
+            $line = $this->line;
+        }
+        $line = intval($line);
+        $add = array('m' => $message, 't' => $type);
+        if(!isset($this->log[$line]) || !in_array($add,$this->log[$line]))
+        {
+            $this->log[$line][] = $add;
+        }
+    }
 
-/**
- * =true if something has been added to the current selector
- * @var bool
- * @access private
- */
-var $added = false;
+    /**
+     * Parse unicode notations and find a replacement character
+     * @param string $string
+     * @param integer $i
+     * @access private
+     * @return string
+     */
+    function _unicode(&$string, &$i)
+    {
+        ++$i;
+        $add = '';
+        $tokens =& $GLOBALS['csstidy']['tokens'];
+        $replaced = false;
 
-/**
- * Array which saves the message log
- * @var array
- * @access private
- */
-var $log = array();
+        while($i < strlen($string) && (ctype_xdigit($string{$i}) || ctype_space($string{$i})) && strlen($add) < 6)
+        {
+            $add .= $string{$i};
 
-/**
- * Saves the line number
- * @var integer
- * @access private
- */
-var $line = 1;
+            if(ctype_space($string{$i})) {
+                break;
+            }
+            $i++;
+        }
 
-/**
- * Loads standard template and sets default settings
- * @access private
- * @version 1.3
- */
-function csstidy()
-{
-       $this->settings['remove_bslash'] = true;
-       $this->settings['compress_colors'] = true;
-       $this->settings['compress_font-weight'] = true;
-       $this->settings['lowercase_s'] = false;
-       $this->settings['optimise_shorthands'] = 1;
-       $this->settings['remove_last_;'] = false;
-       $this->settings['case_properties'] = 1;
-       $this->settings['sort_properties'] = false;
-       $this->settings['sort_selectors'] = false;
-       $this->settings['merge_selectors'] = 2;
-       $this->settings['discard_invalid_properties'] = false;
-       $this->settings['css_level'] = 'CSS2.1';
-    $this->settings['preserve_css'] = false;
-    $this->settings['timestamp'] = false;
-
-       $this->load_template('default');
-    $this->print = new csstidy_print($this);
-    $this->optimise = new csstidy_optimise($this);
-}
+        if(hexdec($add) > 47 && hexdec($add) < 58 || hexdec($add) > 64 && hexdec($add) < 91 || hexdec($add) > 96 && hexdec($add) < 123)
+        {
+            $this->log('Replaced unicode notation: Changed \\'. $add .' to ' . chr(hexdec($add)),'Information');
+            $add = chr(hexdec($add));
+            $replaced = true;
+        }
+        else {
+            $add = trim('\\'.$add);
+        }
 
-/**
- * Get the value of a setting.
- * @param string $setting
- * @access public
- * @return mixed
- * @version 1.0
- */
-function get_cfg($setting)
-{
-       if(isset($this->settings[$setting]))
-       {
-               return $this->settings[$setting];
-       }
-       return false;
-}
+        if(@ctype_xdigit($string{$i+1}) && ctype_space($string{$i})
+            && !$replaced || !ctype_space($string{$i})) {
+                $i--;
+            }
 
-/**
- * Set the value of a setting.
- * @param string $setting
- * @param mixed $value
- * @access public
- * @return bool
- * @version 1.0
- */
-function set_cfg($setting,$value)
-{
-       if(isset($this->settings[$setting]) && $value !== '')
-       {
-               $this->settings[$setting] = $value;
-               return true;
-       }
-       return false;
-}
+        if($add != '\\' || !$this->get_cfg('remove_bslash') || strpos($tokens, $string{$i+1}) !== false) {
+            return $add;
+        }
 
-/**
- * Adds a token to $this->tokens
- * @param mixed $type
- * @param string $data
- * @param bool $do add a token even if preserve_css is off
- * @access private
- * @version 1.0
- */
-function _add_token($type, $data, $do = false) {
-    if($this->get_cfg('preserve_css') || $do) {
-        $this->tokens[] = array($type, ($type == COMMENT) ? $data : trim($data));
+        if($add == '\\') {
+            $this->log('Removed unnecessary backslash','Information');
+        }
+        return '';
     }
-}
 
-/**
- * Add a message to the message log
- * @param string $message
- * @param string $type
- * @param integer $line
- * @access private
- * @version 1.0
- */
-function log($message,$type,$line = -1)
-{
-       if($line === -1)
-       {
-               $line = $this->line;
-       }
-       $line = intval($line);
-       $add = array('m' => $message, 't' => $type);
-       if(!isset($this->log[$line]) || !in_array($add,$this->log[$line]))
-       {
-               $this->log[$line][] = $add;
-       }
-}
+    /**
+     * Loads a new template
+     * @param string $content either filename (if $from_file == true), content of a template file, "high_compression", "highest_compression", "low_compression", or "default"
+     * @param bool $from_file uses $content as filename if true
+     * @access public
+     * @see http://csstidy.sourceforge.net/templates.php
+     */
+    function load_template($content, $from_file=true)
+    {
+        $predefined_templates =& $GLOBALS['csstidy']['predefined_templates'];
+        if($content == 'high_compression' || $content == 'default' || $content == 'highest_compression' || $content == 'low_compression')
+        {
+            $this->template = $predefined_templates[$content];
+            return;
+        }
 
-/**
- * Parse unicode notations and find a replacement character
- * @param string $string
- * @param integer $i
- * @access private
- * @return string
- * @version 1.2
- */
-function _unicode(&$string, &$i)
-{
-       ++$i;
-       $add = '';
-       $tokens =& $GLOBALS['csstidy']['tokens'];
-       $replaced = false;
-
-       while($i < strlen($string) && (ctype_xdigit($string{$i}) || ctype_space($string{$i})) && strlen($add) < 6)
-       {
-               $add .= $string{$i};
-
-               if(ctype_space($string{$i})) {
-                       break;
-               }
-               $i++;
-       }
-
-       if(hexdec($add) > 47 && hexdec($add) < 58 || hexdec($add) > 64 && hexdec($add) < 91 || hexdec($add) > 96 && hexdec($add) < 123)
-       {
-               $this->log('Replaced unicode notation: Changed \\'. $add .' to ' . chr(hexdec($add)),'Information');
-               $add = chr(hexdec($add));
-               $replaced = true;
-       }
-       else {
-               $add = trim('\\'.$add);
-       }
-
-       if(@ctype_xdigit($string{$i+1}) && ctype_space($string{$i})
-       && !$replaced || !ctype_space($string{$i})) {
-               $i--;
-       }
-
-       if($add != '\\' || !$this->get_cfg('remove_bslash') || strpos($tokens, $string{$i+1}) !== false) {
-               return $add;
-       }
-
-       if($add == '\\') {
-               $this->log('Removed unnecessary backslash','Information');
-       }
-       return '';
-}
+        if($from_file)
+        {
+            $content = strip_tags(file_get_contents($content),'<span>');
+        }
+        $content = str_replace("\r\n","\n",$content); // Unify newlines (because the output also only uses \n)
+        $template = explode('|',$content);
 
-/**
- * Loads a new template
- * @param string $content either filename (if $from_file == true), content of a template file, "high_compression", "highest_compression", "low_compression", or "default"
- * @param bool $from_file uses $content as filename if true
- * @access public
- * @version 1.1
- * @see http://csstidy.sourceforge.net/templates.php
- */
-function load_template($content, $from_file=true)
-{
-       $predefined_templates =& $GLOBALS['csstidy']['predefined_templates'];
-       if($content == 'high_compression' || $content == 'default' || $content == 'highest_compression' || $content == 'low_compression')
-       {
-               $this->template = $predefined_templates[$content];
-               return;
-       }
-
-       if($from_file)
-       {
-               $content = strip_tags(file_get_contents($content),'<span>');
-       }
-       $content = str_replace("\r\n","\n",$content); // Unify newlines (because the output also only uses \n)
-       $template = explode('|',$content);
-
-       for ($i = 0; $i < count($template); $i++ )
-       {
-               $this->template[$i] = $template[$i];
-       }
-}
+        for ($i = 0; $i < count($template); $i++ )
+        {
+            $this->template[$i] = $template[$i];
+        }
+    }
 
-/**
- * Starts parsing from URL
- * @param string $url
- * @access public
- * @version 1.0
- */
-function parse_from_url($url)
-{
-       return $this->parse(@file_get_contents($url));
-}
+    /**
+     * Starts parsing from URL
+     * @param string $url
+     * @access public
+     */
+    function parse_from_url($url)
+    {
+        return $this->parse(@file_get_contents($url));
+    }
 
-/**
- * Checks if there is a token at the current position
- * @param string $string
- * @param integer $i
- * @access public
- * @version 1.11
- */
-function is_token(&$string, $i)
-{
-       $tokens =& $GLOBALS['csstidy']['tokens'];
-       return (strpos($tokens, $string{$i}) !== false && !csstidy::escaped($string,$i));
-}
+    /**
+     * Checks if there is a token at the current position
+     * @param string $string
+     * @param integer $i
+     * @access public
+     */
+    function is_token(&$string, $i)
+    {
+        $tokens =& $GLOBALS['csstidy']['tokens'];
+        return (strpos($tokens, $string{$i}) !== false && !$this->escaped($string,$i));
+    }
 
 
-/**
- * Parses CSS in $string. The code is saved as array in $this->css
- * @param string $string the CSS code
- * @access public
- * @return bool
- * @version 1.1
- */
-function parse($string) {
-    // PHP bug? Settings need to be refreshed in PHP4
-    $this->print = new csstidy_print($this);
-    $this->optimise = new csstidy_optimise($this);
+    /**
+     * Parses CSS in $string. The code is saved as array in $this->css
+     * @param string $string the CSS code
+     * @access public
+     * @return bool
+     */
+    function parse($string) {
+        // PHP bug? Settings need to be refreshed in PHP4
+        $this->print = new csstidy_print($this);
+        $this->optimise = new csstidy_optimise($this);
 
-    $all_properties =& $GLOBALS['csstidy']['all_properties'];
-    $at_rules =& $GLOBALS['csstidy']['at_rules'];
+        $all_properties =& $GLOBALS['csstidy']['all_properties'];
+        $at_rules =& $GLOBALS['csstidy']['at_rules'];
 
-    $this->css = array();
-    $this->print->input_css = $string;
-    $string = str_replace("\r\n","\n",$string) . ' ';
-    $cur_comment = '';
+        $this->css = array();
+        $this->print->input_css = $string;
+        $string = str_replace("\r\n","\n",$string) . ' ';
+        $cur_comment = '';
+        $s_id = 0;
 
-    for ($i = 0, $size = strlen($string); $i < $size; $i++ )
-    {
-        if($string{$i} == "\n" || $string{$i} == "\r")
+        for ($i = 0, $size = strlen($string); $i < $size; $i++ )
         {
-            ++$this->line;
-        }
+            if($string{$i} == "\n" || $string{$i} == "\r")
+            {
+                ++$this->line;
+            }
 
-        switch($this->status)
-        {
-            /* Case in at-block */
-            case 'at':
-            if(csstidy::is_token($string,$i))
+            switch($this->status)
             {
-                if($string{$i} == '/' && @$string{$i+1} == '*')
-                {
-                    $this->status = 'ic'; ++$i;
-                    $this->from = 'at';
-                }
-                elseif($string{$i} == '{')
-                {
-                    $this->status = 'is';
-                    $this->_add_token(AT_START, $this->at);
-                }
-                elseif($string{$i} == ',')
-                {
-                    $this->at = trim($this->at).',';
-                }
-                elseif($string{$i} == '\\')
+                /* Case in at-block */
+            case 'at':
+                if($this->is_token($string,$i))
                 {
-                    $this->at .= $this->_unicode($string,$i);
+                    if($string{$i} == '/' && @$string{$i+1} == '*')
+                    {
+                        $this->status = 'ic'; ++$i;
+                        $this->from = 'at';
+                    }
+                    elseif($string{$i} == '{')
+                    {
+                        $this->status = 'is';
+                        $this->_add_token(AT_START, $this->at);
+                    }
+                    elseif($string{$i} == ',')
+                    {
+                        $this->at = trim($this->at).',';
+                    }
+                    elseif($string{$i} == '\\')
+                    {
+                        $this->at .= $this->_unicode($string,$i);
+                    }
                 }
-            }
-            else
-            {
-                $lastpos = strlen($this->at)-1;
-                if(!( (ctype_space($this->at{$lastpos}) || csstidy::is_token($this->at,$lastpos) && $this->at{$lastpos} == ',') && ctype_space($string{$i})))
+                else
                 {
-                    $this->at .= $string{$i};
+                    $lastpos = strlen($this->at)-1;
+                    if(!( (ctype_space($this->at{$lastpos}) || $this->is_token($this->at,$lastpos) && $this->at{$lastpos} == ',') && ctype_space($string{$i})))
+                    {
+                        $this->at .= $string{$i};
+                    }
                 }
-            }
-            break;
+                break;
 
-            /* Case in-selector */
+                /* Case in-selector */
             case 'is':
-            if(csstidy::is_token($string,$i))
-            {
-                if($string{$i} == '/' && @$string{$i+1} == '*' && trim($this->selector) == '')
-                {
-                    $this->status = 'ic'; ++$i;
-                    $this->from = 'is';
-                }
-                elseif($string{$i} == '@' && trim($this->selector) == '')
+                if($this->is_token($string,$i))
                 {
-                    // Check for at-rule
-                    $this->invalid_at = true;
-                    foreach($at_rules as $name => $type)
+                    if($string{$i} == '/' && @$string{$i+1} == '*' && trim($this->selector) == '')
                     {
-                        if(!strcasecmp(substr($string,$i+1,strlen($name)),$name))
+                        $this->status = 'ic'; ++$i;
+                        $this->from = 'is';
+                    }
+                    elseif($string{$i} == '@' && trim($this->selector) == '')
+                    {
+                        // Check for at-rule
+                        $this->invalid_at = true;
+                        foreach($at_rules as $name => $type)
                         {
-                            ($type == 'at') ? $this->at = '@'.$name : $this->selector = '@'.$name;
-                            $this->status = $type;
-                            $i += strlen($name);
-                            $this->invalid_at = false;
+                            if(!strcasecmp(substr($string,$i+1,strlen($name)),$name))
+                            {
+                                ($type == 'at') ? $this->at = '@'.$name : $this->selector = '@'.$name;
+                                $this->status = $type;
+                                $i += strlen($name);
+                                $this->invalid_at = false;
+                            }
                         }
-                    }
 
-                    if($this->invalid_at)
-                    {
-                        $this->selector = '@';
-                        $invalid_at_name = '';
-                        for($j = $i+1; $j < $size; ++$j)
+                        if($this->invalid_at)
                         {
-                            if(!ctype_alpha($string{$j}))
+                            $this->selector = '@';
+                            $invalid_at_name = '';
+                            for($j = $i+1; $j < $size; ++$j)
                             {
-                                break;
+                                if(!ctype_alpha($string{$j}))
+                                {
+                                    break;
+                                }
+                                $invalid_at_name .= $string{$j};
                             }
-                            $invalid_at_name .= $string{$j};
+                            $this->log('Invalid @-rule: '.$invalid_at_name.' (removed)','Warning');
                         }
-                        $this->log('Invalid @-rule: '.$invalid_at_name.' (removed)','Warning');
+                    }
+                    elseif(($string{$i} == '"' || $string{$i} == "'"))
+                    {
+                        $this->cur_string = $string{$i};
+                        $this->status = 'instr';
+                        $this->str_char = $string{$i};
+                        $this->from = 'is';
+                    }
+                    elseif($this->invalid_at && $string{$i} == ';')
+                    {
+                        $this->invalid_at = false;
+                        $this->status = 'is';
+                    }
+                    elseif($string{$i} == '{')
+                    {
+                        $this->status = 'ip';
+                        $this->_add_token(SEL_START, $this->selector);
+                        $this->added = false;
+                    }
+                    elseif($string{$i} == '}')
+                    {
+                        $this->_add_token(AT_END, $this->at);
+                        $this->at = '';
+                        $this->selector = '';
+                        $this->sel_separate = array();
+                        ++$s_id;
+                    }
+                    elseif($string{$i} == ',')
+                    {
+                        $this->selector = trim($this->selector).',';
+                        $this->sel_separate[] = strlen($this->selector);
+                    }
+                    elseif($string{$i} == '\\')
+                    {
+                        $this->selector .= $this->_unicode($string,$i);
+                    }
+                    // remove unnecessary universal selector,  FS#147
+                    else if(!($string{$i} == '*' && @in_array($string{$i+1}, array('.', '#', '[', ':')))) {
+                        $this->selector .= $string{$i};
                     }
                 }
-                elseif(($string{$i} == '"' || $string{$i} == "'"))
-                {
-                    $this->cur_string = $string{$i};
-                    $this->status = 'instr';
-                    $this->str_char = $string{$i};
-                    $this->from = 'is';
-                }
-                elseif($this->invalid_at && $string{$i} == ';')
-                {
-                    $this->invalid_at = false;
-                    $this->status = 'is';
-                }
-                elseif($string{$i} == '{')
-                {
-                    $this->status = 'ip';
-                    $this->_add_token(SEL_START, $this->selector);
-                    $this->added = false;
-                }
-                elseif($string{$i} == '}')
-                {
-                    $this->_add_token(AT_END, $this->at);
-                    $this->at = '';
-                    $this->selector = '';
-                    $this->sel_separate = array();
-                }
-                elseif($string{$i} == ',')
-                {
-                    $this->selector = trim($this->selector).',';
-                    $this->sel_separate[] = strlen($this->selector);
-                }
-                elseif($string{$i} == '\\')
-                {
-                    $this->selector .= $this->_unicode($string,$i);
-                }
-                // remove unnecessary universal selector,  FS#147
-                else if(!($string{$i} == '*' && @in_array($string{$i+1}, array('.', '#', '[', ':')))) {
-                    $this->selector .= $string{$i};
-                }
-            }
-            else
-            {
-                $lastpos = strlen($this->selector)-1;
-                if($lastpos == -1 || !( (ctype_space($this->selector{$lastpos}) || csstidy::is_token($this->selector,$lastpos) && $this->selector{$lastpos} == ',') && ctype_space($string{$i})))
+                else
                 {
-                    $this->selector .= $string{$i};
+                    $lastpos = strlen($this->selector)-1;
+                    if($lastpos == -1 || !( (ctype_space($this->selector{$lastpos}) || $this->is_token($this->selector,$lastpos) && $this->selector{$lastpos} == ',') && ctype_space($string{$i})))
+                    {
+                        $this->selector .= $string{$i};
+                    }
                 }
-            }
-            break;
+                break;
 
-            /* Case in-property */
+                /* Case in-property */
             case 'ip':
-            if(csstidy::is_token($string,$i))
-            {
-                if(($string{$i} == ':' || $string{$i} == '=') && $this->property != '')
+                if($this->is_token($string,$i))
                 {
-                    $this->status = 'iv';
-                    if(!$this->get_cfg('discard_invalid_properties') || csstidy::property_is_valid($this->property)) {
-                        $this->_add_token(PROPERTY, $this->property);
+                    if(($string{$i} == ':' || $string{$i} == '=') && $this->property != '')
+                    {
+                        $this->status = 'iv';
+                        if(!$this->get_cfg('discard_invalid_properties') || $this->property_is_valid($this->property)) {
+                            $this->_add_token(PROPERTY, $this->property);
+                        }
+                    }
+                    elseif($string{$i} == '/' && @$string{$i+1} == '*' && $this->property == '')
+                    {
+                        $this->status = 'ic'; ++$i;
+                        $this->from = 'ip';
+                    }
+                    elseif($string{$i} == '}')
+                    {
+                        $this->explode_selectors();
+                        $this->status = 'is';
+                        $this->invalid_at = false;
+                        $this->_add_token(SEL_END, $this->selector);
+                        $this->selector = '';
+                        $this->property = '';
+                    }
+                    elseif($string{$i} == ';')
+                    {
+                        $this->property = '';
+                    }
+                    elseif($string{$i} == '\\')
+                    {
+                        $this->property .= $this->_unicode($string,$i);
                     }
                 }
-                elseif($string{$i} == '/' && @$string{$i+1} == '*' && $this->property == '')
-                {
-                    $this->status = 'ic'; ++$i;
-                    $this->from = 'ip';
-                }
-                elseif($string{$i} == '}')
-                {
-                    $this->explode_selectors();
-                    $this->status = 'is';
-                    $this->invalid_at = false;
-                    $this->_add_token(SEL_END, $this->selector);
-                    $this->selector = '';
-                    $this->property = '';
-                }
-                elseif($string{$i} == ';')
-                {
-                    $this->property = '';
-                }
-                elseif($string{$i} == '\\')
+                elseif(!ctype_space($string{$i}))
                 {
-                    $this->property .= $this->_unicode($string,$i);
+                    $this->property .= $string{$i};
                 }
-            }
-            elseif(!ctype_space($string{$i}))
-            {
-                $this->property .= $string{$i};
-            }
-            break;
+                break;
 
-            /* Case in-value */
+                /* Case in-value */
             case 'iv':
-            $pn = (($string{$i} == "\n" || $string{$i} == "\r") && $this->property_is_next($string,$i+1) || $i == strlen($string)-1);
-            if(csstidy::is_token($string,$i) || $pn)
-            {
-                if($string{$i} == '/' && @$string{$i+1} == '*')
-                {
-                    $this->status = 'ic'; ++$i;
-                    $this->from = 'iv';
-                }
-                elseif(($string{$i} == '"' || $string{$i} == "'" || $string{$i} == '('))
-                {
-                    $this->cur_string = $string{$i};
-                    $this->str_char = ($string{$i} == '(') ? ')' : $string{$i};
-                    $this->status = 'instr';
-                    $this->from = 'iv';
-                }
-                elseif($string{$i} == ',')
-                {
-                    $this->sub_value = trim($this->sub_value).',';
-                }
-                elseif($string{$i} == '\\')
+                $pn = (($string{$i} == "\n" || $string{$i} == "\r") && $this->property_is_next($string,$i+1) || $i == strlen($string)-1);
+                if($this->is_token($string,$i) || $pn)
                 {
-                    $this->sub_value .= $this->_unicode($string,$i);
-                }
-                elseif($string{$i} == ';' || $pn)
-                {
-                    if($this->selector{0} == '@' && isset($at_rules[substr($this->selector,1)]) && $at_rules[substr($this->selector,1)] == 'iv')
+                    if($string{$i} == '/' && @$string{$i+1} == '*')
                     {
-                        $this->sub_value_arr[] = trim($this->sub_value);
+                        $this->status = 'ic'; ++$i;
+                        $this->from = 'iv';
+                    }
+                    elseif(($string{$i} == '"' || $string{$i} == "'" || $string{$i} == '('))
+                    {
+                        $this->cur_string = $string{$i};
+                        $this->str_char = ($string{$i} == '(') ? ')' : $string{$i};
+                        $this->status = 'instr';
+                        $this->from = 'iv';
+                    }
+                    elseif($string{$i} == ',')
+                    {
+                        $this->sub_value = trim($this->sub_value).',';
+                    }
+                    elseif($string{$i} == '\\')
+                    {
+                        $this->sub_value .= $this->_unicode($string,$i);
+                    }
+                    elseif($string{$i} == ';' || $pn)
+                    {
+                        if($this->selector{0} == '@' && isset($at_rules[substr($this->selector,1)]) && $at_rules[substr($this->selector,1)] == 'iv')
+                        {
+                            $this->sub_value_arr[] = trim($this->sub_value);
 
-                        $this->status = 'is';
+                            $this->status = 'is';
 
-                        switch($this->selector)
-                        {
+                            switch($this->selector)
+                            {
                             case '@charset': $this->charset = $this->sub_value_arr[0]; break;
                             case '@namespace': $this->namespace = implode(' ',$this->sub_value_arr); break;
                             case '@import': $this->import[] = implode(' ',$this->sub_value_arr); break;
-                        }
+                            }
 
-                        $this->sub_value_arr = array();
-                        $this->sub_value = '';
-                        $this->selector = '';
-                        $this->sel_separate = array();
+                            $this->sub_value_arr = array();
+                            $this->sub_value = '';
+                            $this->selector = '';
+                            $this->sel_separate = array();
+                        }
+                        else
+                        {
+                            $this->status = 'ip';
+                        }
                     }
-                    else
+                    elseif($string{$i} != '}')
                     {
-                        $this->status = 'ip';
+                        $this->sub_value .= $string{$i};
                     }
-                }
-                elseif($string{$i} != '}')
-                {
-                    $this->sub_value .= $string{$i};
-                }
-                if(($string{$i} == '}' || $string{$i} == ';' || $pn) && !empty($this->selector))
-                {
-                    if($this->at == '')
+                    if(($string{$i} == '}' || $string{$i} == ';' || $pn) && !empty($this->selector))
                     {
-                        $this->at = DEFAULT_AT;
-                    }
+                        if($this->at == '')
+                        {
+                            $this->at = DEFAULT_AT;
+                        }
 
-                    // case settings
-                    if($this->get_cfg('lowercase_s'))
-                    {
-                        $this->selector = strtolower($this->selector);
-                    }
-                    $this->property = strtolower($this->property);
+                        // case settings
+                        if($this->get_cfg('lowercase_s'))
+                        {
+                            $this->selector = strtolower($this->selector);
+                        }
+                        $this->property = strtolower($this->property);
 
-                    $this->optimise->subvalue();
-                    if($this->sub_value != '') {
-                        $this->sub_value_arr[] = $this->sub_value;
-                        $this->sub_value = '';
-                    }
+                        $this->optimise->subvalue();
+                        if($this->sub_value != '') {
+                            $this->sub_value_arr[] = $this->sub_value;
+                            $this->sub_value = '';
+                        }
 
-                    $this->value = implode(' ',$this->sub_value_arr);
+                        $this->value = implode(' ',$this->sub_value_arr);
 
-                    $this->selector = trim($this->selector);
+                        $this->selector = trim($this->selector);
 
-                    $this->optimise->value();
+                        $this->optimise->value();
 
-                    $valid = csstidy::property_is_valid($this->property);
-                    if((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid))
-                    {
-                        $this->css_add_property($this->at,$this->selector,$this->property,$this->value);
-                        $this->_add_token(VALUE, $this->value);
-                        $this->optimise->shorthands();
-                    }
-                    if(!$valid)
-                    {
-                        if($this->get_cfg('discard_invalid_properties'))
+                        $valid = $this->property_is_valid($this->property);
+                        if((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid))
                         {
-                            $this->log('Removed invalid property: '.$this->property,'Warning');
+                            $this->css_add_property($this->at,$this->selector,$this->property,$this->value, $s_id);
+                            $this->_add_token(VALUE, $this->value);
+                            $this->optimise->shorthands();
                         }
-                        else
+                        if(!$valid)
                         {
-                            $this->log('Invalid property in '.strtoupper($this->get_cfg('css_level')).': '.$this->property,'Warning');
+                            if($this->get_cfg('discard_invalid_properties'))
+                            {
+                                $this->log('Removed invalid property: '.$this->property,'Warning');
+                            }
+                            else
+                            {
+                                $this->log('Invalid property in '.strtoupper($this->get_cfg('css_level')).': '.$this->property,'Warning');
+                            }
                         }
-                    }
 
-                    $this->property = '';
-                    $this->sub_value_arr = array();
-                    $this->value = '';
+                        $this->property = '';
+                        $this->sub_value_arr = array();
+                        $this->value = '';
+                    }
+                    if($string{$i} == '}')
+                    {
+                        $this->explode_selectors();
+                        $this->_add_token(SEL_END, $this->selector);
+                        $this->status = 'is';
+                        $this->invalid_at = false;
+                        $this->selector = '';
+                    }
                 }
-                if($string{$i} == '}')
+                elseif(!$pn)
                 {
-                    $this->explode_selectors();
-                    $this->_add_token(SEL_END, $this->selector);
-                    $this->status = 'is';
-                    $this->invalid_at = false;
-                    $this->selector = '';
-                }
-            }
-            elseif(!$pn)
-            {
-                $this->sub_value .= $string{$i};
+                    $this->sub_value .= $string{$i};
 
-                if(ctype_space($string{$i}))
-                {
-                    $this->optimise->subvalue();
-                    if($this->sub_value != '') {
-                        $this->sub_value_arr[] = $this->sub_value;
-                        $this->sub_value = '';
+                    if(ctype_space($string{$i}))
+                    {
+                        $this->optimise->subvalue();
+                        if($this->sub_value != '') {
+                            $this->sub_value_arr[] = $this->sub_value;
+                            $this->sub_value = '';
+                        }
                     }
                 }
-            }
-            break;
+                break;
 
-            /* Case in string */
+                /* Case in string */
             case 'instr':
-            if($this->str_char == ')' && ($string{$i} == '"' || $string{$i} == '\'') && !$this->str_in_str && !csstidy::escaped($string,$i))
-            {
-                $this->str_in_str = true;
-            }
-            elseif($this->str_char == ')' && ($string{$i} == '"' || $string{$i} == '\'') && $this->str_in_str && !csstidy::escaped($string,$i))
-            {
-                $this->str_in_str = false;
-            }
-            $temp_add = $string{$i};           // ...and no not-escaped backslash at the previous position
-            if( ($string{$i} == "\n" || $string{$i} == "\r") && !($string{$i-1} == '\\' && !csstidy::escaped($string,$i-1)) )
-            {
-                $temp_add = "\\A ";
-                $this->log('Fixed incorrect newline in string','Warning');
-            }
-            if (!($this->str_char == ')' && in_array($string{$i}, $GLOBALS['csstidy']['whitespace']) && !$this->str_in_str)) {
-                $this->cur_string .= $temp_add;
-            }
-            if($string{$i} == $this->str_char && !csstidy::escaped($string,$i) && !$this->str_in_str)
-            {
-                $this->status = $this->from;
-                if (!preg_match('|[' . implode('', $GLOBALS['csstidy']['whitespace']) . ']|uis', $this->cur_string) && $this->property != 'content') {
-                    if ($this->str_char == '"' || $this->str_char == '\'') {
-                           if ($this->status != 'is') {
-                                               $this->cur_string = substr($this->cur_string, 1, -1);
-                           }
-                                       } else if (strlen($this->cur_string) > 3 && ($this->cur_string[1] == '"' || $this->cur_string[1] == '\'')) /* () */ {
-                                               $this->cur_string = $this->cur_string[0] . substr($this->cur_string, 2, -2) . substr($this->cur_string, -1);
-                                       }
+                if($this->str_char == ')' && ($string{$i} == '"' || $string{$i} == '\'') && !$this->str_in_str && !$this->escaped($string,$i))
+                {
+                    $this->str_in_str = true;
                 }
-                if($this->from == 'iv')
+                elseif($this->str_char == ')' && ($string{$i} == '"' || $string{$i} == '\'') && $this->str_in_str && !$this->escaped($string,$i))
                 {
-                    $this->sub_value .= $this->cur_string;
+                    $this->str_in_str = false;
                 }
-                elseif($this->from == 'is')
+                $temp_add = $string{$i};           // ...and no not-escaped backslash at the previous position
+                if( ($string{$i} == "\n" || $string{$i} == "\r") && !($string{$i-1} == '\\' && !$this->escaped($string,$i-1)) )
                 {
-                    $this->selector .= $this->cur_string;
+                    $temp_add = "\\A ";
+                    $this->log('Fixed incorrect newline in string','Warning');
                 }
-            }
-            break;
+                if (!($this->str_char == ')' && in_array($string{$i}, $GLOBALS['csstidy']['whitespace']) && !$this->str_in_str)) {
+                    $this->cur_string .= $temp_add;
+                }
+                if($string{$i} == $this->str_char && !$this->escaped($string,$i) && !$this->str_in_str)
+                {
+                    $this->status = $this->from;
+                    if (!preg_match('|[' . implode('', $GLOBALS['csstidy']['whitespace']) . ']|uis', $this->cur_string) && $this->property != 'content') {
+                        if ($this->str_char == '"' || $this->str_char == '\'') {
+                            if ($this->status != 'is') {
+                                $this->cur_string = substr($this->cur_string, 1, -1);
+                            }
+                        } else if (strlen($this->cur_string) > 3 && ($this->cur_string[1] == '"' || $this->cur_string[1] == '\'')) /* () */ {
+                            $this->cur_string = $this->cur_string[0] . substr($this->cur_string, 2, -2) . substr($this->cur_string, -1);
+                        }
+                    }
+                    if($this->from == 'iv')
+                    {
+                        $this->sub_value .= $this->cur_string;
+                    }
+                    elseif($this->from == 'is')
+                    {
+                        $this->selector .= $this->cur_string;
+                    }
+                }
+                break;
 
-            /* Case in-comment */
+                /* Case in-comment */
             case 'ic':
-            if($string{$i} == '*' && $string{$i+1} == '/')
-            {
-                $this->status = $this->from;
-                $i++;
-                $this->_add_token(COMMENT, $cur_comment);
-                $cur_comment = '';
-            }
-            else
-            {
-                $cur_comment .= $string{$i};
+                if($string{$i} == '*' && $string{$i+1} == '/')
+                {
+                    $this->status = $this->from;
+                    $i++;
+                    $this->_add_token(COMMENT, $cur_comment);
+                    $cur_comment = '';
+                }
+                else
+                {
+                    $cur_comment .= $string{$i};
+                }
+                break;
             }
-            break;
         }
-    }
 
-    $this->optimise->postparse();
+        $this->optimise->postparse();
 
-    $this->print->_reset();
+        $this->print->_reset();
 
-    return !(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace));
-}
+        return !(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace));
+    }
 
-/**
- * Explodes selectors
- * @access private
- * @version 1.0
- */
-function explode_selectors()
-{
-    // Explode multiple selectors
-    if($this->get_cfg('merge_selectors') == 1)
+    /**
+     * Explodes selectors
+     * @access private
+     */
+    function explode_selectors()
     {
-        $new_sels = array();
-        $lastpos = 0;
-        $this->sel_separate[] = strlen($this->selector);
-        foreach($this->sel_separate as $num => $pos)
+        // Explode multiple selectors
+        if($this->get_cfg('merge_selectors') == 1)
         {
-            if($num == count($this->sel_separate)-1) {
-                $pos += 1;
-            }
+            $new_sels = array();
+            $lastpos = 0;
+            $this->sel_separate[] = strlen($this->selector);
+            foreach($this->sel_separate as $num => $pos)
+            {
+                if($num == count($this->sel_separate)-1) {
+                    $pos += 1;
+                }
 
-            $new_sels[] = substr($this->selector,$lastpos,$pos-$lastpos-1);
-            $lastpos = $pos;
-        }
+                $new_sels[] = substr($this->selector,$lastpos,$pos-$lastpos-1);
+                $lastpos = $pos;
+            }
 
-        if(count($new_sels) > 1)
-        {
-            foreach($new_sels as $selector)
+            if(count($new_sels) > 1)
             {
-                $this->merge_css_blocks($this->at,$selector,$this->css[$this->at][$this->selector]);
+                foreach($new_sels as $selector)
+                {
+                    $this->merge_css_blocks($this->at,$selector,$this->css[$this->at][$this->selector]);
+                }
+                unset($this->css[$this->at][$this->selector]);
             }
-            unset($this->css[$this->at][$this->selector]);
         }
+        $this->sel_separate = array();
     }
-    $this->sel_separate = array();
-}
-
-/**
- * Checks if a character is escaped (and returns true if it is)
- * @param string $string
- * @param integer $pos
- * @access public
- * @return bool
- * @version 1.02
- */
-function escaped(&$string,$pos)
-{
-       return !(@($string{$pos-1} != '\\') || csstidy::escaped($string,$pos-1));
-}
 
-/**
- * Adds a property with value to the existing CSS code
- * @param string $media
- * @param string $selector
- * @param string $property
- * @param string $new_val
- * @access private
- * @version 1.2
- */
-function css_add_property($media,$selector,$property,$new_val)
-{
-    if($this->get_cfg('preserve_css') || trim($new_val) == '') {
-        return;
+    /**
+     * Checks if a character is escaped (and returns true if it is)
+     * @param string $string
+     * @param integer $pos
+     * @access public
+     * @return bool
+     */
+    function escaped(&$string,$pos)
+    {
+        return !(@($string{$pos-1} != '\\') || $this->escaped($string,$pos-1));
     }
 
-    $this->added = true;
-    if(isset($this->css[$media][$selector][$property]))
+    /**
+     * Adds a property with value to the existing CSS code
+     * @param string $media
+     * @param string $selector
+     * @param string $property
+     * @param string $new_val
+     * @param string $selector_id
+     * @access private
+     */
+    function css_add_property($media,$selector,$property,$new_val,$selector_id = null)
     {
-        if((csstidy::is_important($this->css[$media][$selector][$property]) && csstidy::is_important($new_val)) || !csstidy::is_important($this->css[$media][$selector][$property]))
-        {
-            unset($this->css[$media][$selector][$property]);
-            $this->css[$media][$selector][$property] = trim($new_val);
+        $new_val = trim($new_val);
+
+        if (!$this->get_cfg('preserve_css') && ($new_val != '')) {
+            $this->added = true;
+
+            if (isset($this->css[$media][$selector][$property])) {
+                $ptr = &$this->css[$media][$selector][$property];
+                if ($this->is_important(end($ptr['p']))) {
+                    return;
+                } elseif (!is_null($selector_id) &&
+                          ($ptr['id'] == $selector_id)) {
+                    $ptr['p'][] = $new_val;
+                    return;
+                }
+            }
+
+            $this->css[$media][$selector][$property] = array(
+                'id' => (is_null($selector_id) ? -1 : $selector_id),
+                'p' => array($new_val)
+            );
         }
     }
-    else
+
+    /**
+     * Adds CSS to an existing media/selector
+     * @param string $media
+     * @param string $selector
+     * @param array $css_add
+     * @access private
+     */
+    function merge_css_blocks($media, $selector, $css_add)
     {
-        $this->css[$media][$selector][$property] = trim($new_val);
+        foreach ($css_add as $property => $value) {
+            $this->css_add_property($media, $selector, $property, $value);
+        }
     }
-}
-
-/**
- * Adds CSS to an existing media/selector
- * @param string $media
- * @param string $selector
- * @param array $css_add
- * @access private
- * @version 1.1
- */
-function merge_css_blocks($media,$selector,$css_add)
-{
-       foreach($css_add as $property => $value)
-       {
-               $this->css_add_property($media,$selector,$property,$value,false);
-       }
-}
 
-/**
- * Checks if $value is !important.
- * @param string $value
- * @return bool
- * @access public
- * @version 1.0
- */
-function is_important(&$value)
-{
-       return (!strcasecmp(substr(str_replace($GLOBALS['csstidy']['whitespace'],'',$value),-10,10),'!important'));
-}
+    /**
+     * Checks if $value is !important.
+     * @param string $value
+     * @return bool
+     * @access public
+     */
+    function is_important($value)
+    {
+        return (!strcasecmp(substr(str_replace($GLOBALS['csstidy']['whitespace'],'',$value),-10,10),'!important'));
+    }
 
-/**
- * Returns a value without !important
- * @param string $value
- * @return string
- * @access public
- * @version 1.0
- */
-function gvw_important($value)
-{
-       if(csstidy::is_important($value))
-       {
-               $value = trim($value);
-               $value = substr($value,0,-9);
-               $value = trim($value);
-               $value = substr($value,0,-1);
-               $value = trim($value);
-               return $value;
-       }
-       return $value;
-}
+    /**
+     * Returns a value without !important
+     * @param string $value
+     * @return string
+     * @access public
+     */
+    function gvw_important($value)
+    {
+        if($this->is_important($value))
+        {
+            $value = trim($value);
+            $value = substr($value,0,-9);
+            $value = trim($value);
+            $value = substr($value,0,-1);
+            $value = trim($value);
+        }
+        return $value;
+    }
 
-/**
- * Checks if the next word in a string from pos is a CSS property
- * @param string $istring
- * @param integer $pos
- * @return bool
- * @access private
- * @version 1.2
- */
-function property_is_next($istring, $pos)
-{
-       $all_properties =& $GLOBALS['csstidy']['all_properties'];
-       $istring = substr($istring,$pos,strlen($istring)-$pos);
-       $pos = strpos($istring,':');
-       if($pos === false)
-       {
-               return false;
-       }
-       $istring = strtolower(trim(substr($istring,0,$pos)));
-       if(isset($all_properties[$istring]))
-       {
-               $this->log('Added semicolon to the end of declaration','Warning');
-               return true;
-       }
-       return false;
-}
+    /**
+     * Checks if the next word in a string from pos is a CSS property
+     * @param string $istring
+     * @param integer $pos
+     * @return bool
+     * @access private
+     */
+    function property_is_next($istring, $pos)
+    {
+        $all_properties =& $GLOBALS['csstidy']['all_properties'];
+        $istring = substr($istring,$pos,strlen($istring)-$pos);
+        $pos = strpos($istring,':');
+        if($pos === false)
+        {
+            return false;
+        }
+        $istring = strtolower(trim(substr($istring,0,$pos)));
+        if(isset($all_properties[$istring]))
+        {
+            $this->log('Added semicolon to the end of declaration','Warning');
+            return true;
+        }
+        return false;
+    }
 
-/**
- * Checks if a property is valid
- * @param string $property
- * @return bool;
- * @access public
- * @version 1.0
- */
-function property_is_valid($property) {
-    $all_properties =& $GLOBALS['csstidy']['all_properties'];
-    return (isset($all_properties[$property]) && strpos($all_properties[$property],strtoupper($this->get_cfg('css_level'))) !== false );
-}
+    /**
+     * Checks if a property is valid
+     * @param string $property
+     * @return bool;
+     * @access public
+     */
+    function property_is_valid($property) {
+        $all_properties =& $GLOBALS['csstidy']['all_properties'];
+        return (isset($all_properties[$property]) && strpos($all_properties[$property],strtoupper($this->get_cfg('css_level'))) !== false );
+    }
 
 }
-?>
index 7dc355e..9be0415 100644 (file)
@@ -33,7 +33,6 @@
  *
  * @package csstidy
  * @author Florian Schmitz (floele at gmail dot com) 2005-2006
- * @version 1.0
  */
 
 class csstidy_optimise
@@ -42,12 +41,10 @@ class csstidy_optimise
      * Constructor
      * @param array $css contains the class csstidy
      * @access private
-     * @version 1.0
      */
     function csstidy_optimise(&$css)
     {
         $this->parser    =& $css;
-        $this->css       =& $css->css;
         $this->sub_value =& $css->sub_value;
         $this->at        =& $css->at;
         $this->selector  =& $css->selector;
@@ -58,7 +55,6 @@ class csstidy_optimise
     /**
      * Optimises $css after parsing
      * @access public
-     * @version 1.0
      */
     function postparse()
     {
@@ -66,29 +62,25 @@ class csstidy_optimise
             return;
         }
 
-        if ($this->parser->get_cfg('merge_selectors') == 2)
-        {
-            foreach ($this->css as $medium => $value)
-            {
-                $this->merge_selectors($this->css[$medium]);
+        $css = &$this->parser->css;
+
+        if ($this->parser->get_cfg('merge_selectors') == 2) {
+            foreach (array_keys($css) as $medium) {
+                $this->merge_selectors($css[$medium]);
             }
         }
 
-        if ($this->parser->get_cfg('optimise_shorthands') > 0)
-        {
-            foreach ($this->css as $medium => $value)
-            {
-                foreach ($value as $selector => $value1)
-                {
-                    $this->css[$medium][$selector] = csstidy_optimise::merge_4value_shorthands($this->css[$medium][$selector]);
-
-                    if ($this->parser->get_cfg('optimise_shorthands') < 2) {
-                        continue;
-                    }
+        if ($this->parser->get_cfg('optimise_shorthands') > 0) {
+            foreach ($css as $medium => $value) {
+                foreach ($value as $selector => $value1) {
+                    $ptr = &$css[$medium][$selector];
+                    $ptr = $this->merge_4value_shorthands($ptr);
 
-                    $this->css[$medium][$selector] = csstidy_optimise::merge_bg($this->css[$medium][$selector]);
-                    if (empty($this->css[$medium][$selector])) {
-                        unset($this->css[$medium][$selector]);
+                    if ($this->parser->get_cfg('optimise_shorthands') >= 2) {
+                        $ptr = $this->merge_bg($ptr);
+                        if (empty($ptr)) {
+                            unset($ptr);
+                        }
                     }
                 }
             }
@@ -98,7 +90,6 @@ class csstidy_optimise
     /**
      * Optimises values
      * @access public
-     * @version 1.0
      */
     function value()
     {
@@ -107,7 +98,7 @@ class csstidy_optimise
         // optimise shorthand properties
         if(isset($shorthands[$this->property]))
         {
-            $temp = csstidy_optimise::shorthand($this->value); // FIXME - move
+            $temp = $this->shorthand($this->value); // FIXME - move
             if($temp != $this->value)
             {
                 $this->parser->log('Optimised shorthand notation ('.$this->property.'): Changed "'.$this->value.'" to "'.$temp.'"','Information');
@@ -125,27 +116,26 @@ class csstidy_optimise
     /**
      * Optimises shorthands
      * @access public
-     * @version 1.0
      */
     function shorthands()
     {
-        $shorthands =& $GLOBALS['csstidy']['shorthands'];
-
-        if(!$this->parser->get_cfg('optimise_shorthands') || $this->parser->get_cfg('preserve_css')) {
+        if (!$this->parser->get_cfg('optimise_shorthands') ||
+            $this->parser->get_cfg('preserve_css')) {
             return;
         }
 
-        if($this->property == 'background' && $this->parser->get_cfg('optimise_shorthands') > 1)
-        {
-            unset($this->css[$this->at][$this->selector]['background']);
-            $this->parser->merge_css_blocks($this->at,$this->selector,csstidy_optimise::dissolve_short_bg($this->value));
+        $css = &$this->parser->css;
+        $shorthands =& $GLOBALS['csstidy']['shorthands'];
+
+        if ($this->property == 'background' &&
+            $this->parser->get_cfg('optimise_shorthands') > 1) {
+            unset($css[$this->at][$this->selector]['background']);
+            $this->parser->merge_css_blocks($this->at,$this->selector,$this->dissolve_short_bg($this->value));
         }
-        if(isset($shorthands[$this->property]))
-        {
-            $this->parser->merge_css_blocks($this->at,$this->selector,csstidy_optimise::dissolve_4value_shorthands($this->property,$this->value));
-            if(is_array($shorthands[$this->property]))
-            {
-                unset($this->css[$this->at][$this->selector][$this->property]);
+        if(isset($shorthands[$this->property])) {
+            $this->parser->merge_css_blocks($this->at,$this->selector,$this->dissolve_4value_shorthands($this->property,$this->value));
+            if(is_array($shorthands[$this->property])) {
+                unset($css[$this->at][$this->selector][$this->property]);
             }
         }
     }
@@ -153,7 +143,6 @@ class csstidy_optimise
     /**
      * Optimises a sub-value
      * @access public
-     * @version 1.0
      */
     function subvalue()
     {
@@ -166,11 +155,11 @@ class csstidy_optimise
         }
 
         $important = '';
-        if(csstidy::is_important($this->sub_value))
+        if($this->parser->is_important($this->sub_value))
         {
             $important = '!important';
         }
-        $this->sub_value = csstidy::gvw_important($this->sub_value);
+        $this->sub_value = $this->parser->gvw_important($this->sub_value);
 
         // Compress font-weight
         if($this->property == 'font-weight' && $this->parser->get_cfg('compress_font-weight'))
@@ -218,14 +207,13 @@ class csstidy_optimise
      * @param string $value
      * @access public
      * @return string
-     * @version 1.0
      */
     function shorthand($value)
     {
         $important = '';
-        if(csstidy::is_important($value))
+        if($this->parser->is_important($value))
         {
-            $values = csstidy::gvw_important($value);
+            $values = $this->parser->gvw_important($value);
             $important = '!important';
         }
         else $values = $value;
@@ -275,13 +263,12 @@ class csstidy_optimise
      * @param string $string
      * @return string
      * @access public
-     * @version 1.1
      */
     function compress_important(&$string)
     {
-        if(csstidy::is_important($string))
+        if($this->parser->is_important($string))
         {
-            $string = csstidy::gvw_important($string) . '!important';
+            $string = $this->parser->gvw_important($string) . '!important';
         }
         return $string;
     }
@@ -290,7 +277,6 @@ class csstidy_optimise
      * Color compression function. Converts all rgb() values to #-values and uses the short-form if possible. Also replaces 4 color names by #-values.
      * @param string $color
      * @return string
-     * @version 1.1
      */
     function cut_color($color)
     {
@@ -365,7 +351,6 @@ class csstidy_optimise
      * Compresses numbers (ie. 1.0 becomes 1 or 1.100 becomes 1.1 )
      * @param string $subvalue
      * @return string
-     * @version 1.2
      */
     function compress_numbers($subvalue)
     {
@@ -443,7 +428,6 @@ class csstidy_optimise
      * @param array $array
      * @return array
      * @access public
-     * @version 1.2
      */
     function merge_selectors(&$array)
     {
@@ -492,7 +476,6 @@ class csstidy_optimise
      * @param string $property
      * @param string $value
      * @return array
-     * @version 1.0
      * @see merge_4value_shorthands()
      */
     function dissolve_4value_shorthands($property,$value)
@@ -505,9 +488,9 @@ class csstidy_optimise
         }
 
         $important = '';
-        if(csstidy::is_important($value))
+        if($this->parser->is_important($value))
         {
-            $value = csstidy::gvw_important($value);
+            $value = $this->parser->gvw_important($value);
             $important = '!important';
         }
         $values = explode(' ',$value);
@@ -551,7 +534,6 @@ class csstidy_optimise
      * @param string $sep seperator
      * @param string $string
      * @return array
-     * @version 1.0
      */
     function explode_ws($sep,$string)
     {
@@ -565,11 +547,11 @@ class csstidy_optimise
             switch($status)
             {
                 case 'st':
-                if($string{$i} == $sep && !csstidy::escaped($string,$i))
+                if($string{$i} == $sep && !$this->parser->escaped($string,$i))
                 {
                     ++$num;
                 }
-                elseif($string{$i} == '"' || $string{$i} == '\'' || $string{$i} == '(' && !csstidy::escaped($string,$i))
+                elseif($string{$i} == '"' || $string{$i} == '\'' || $string{$i} == '(' && !$this->parser->escaped($string,$i))
                 {
                     $status = 'str';
                     $to = ($string{$i} == '(') ? ')' : $string{$i};
@@ -582,7 +564,7 @@ class csstidy_optimise
                 break;
 
                 case 'str':
-                if($string{$i} == $to && !csstidy::escaped($string,$i))
+                if($string{$i} == $to && !$this->parser->escaped($string,$i))
                 {
                     $status = 'st';
                 }
@@ -605,39 +587,37 @@ class csstidy_optimise
      * Merges Shorthand properties again, the opposite of dissolve_4value_shorthands()
      * @param array $array
      * @return array
-     * @version 1.2
      * @see dissolve_4value_shorthands()
      */
     function merge_4value_shorthands($array)
     {
         $return = $array;
-        $shorthands =$GLOBALS['csstidy']['shorthands'];
+        $shorthands = &$GLOBALS['csstidy']['shorthands'];
 
-        foreach($shorthands as $key => $value)
-        {
-            if(isset($array[$value[0]]) && isset($array[$value[1]])
-            && isset($array[$value[2]]) && isset($array[$value[3]]) && $value !== 0)
-            {
-                $return[$key] = '';
+        foreach ($shorthands as $key => $value) {
+            if ((count($value) == 4) &&
+                isset($array[$value[0]]) &&
+                isset($array[$value[1]]) &&
+                isset($array[$value[2]]) &&
+                isset($array[$value[3]])) {
 
-                $important = '';
-                for($i = 0; $i < 4; $i++)
-                {
-                    $val = $array[$value[$i]];
-                    if(csstidy::is_important($val))
-                    {
+                $important = $newval = '';
+
+                for ($i = 0; $i < 4; ++$i) {
+                    $val = end($array[$value[$i]]['p']);
+                    if ($this->parser->is_important($val)) {
                         $important = '!important';
-                        $return[$key] .= csstidy::gvw_important($val).' ';
-                    }
-                    else
-                    {
-                        $return[$key] .= $val.' ';
+                        $newval .= $this->parser->gvw_important($val).' ';
+                    } else {
+                        $newval .= $val.' ';
                     }
                     unset($return[$value[$i]]);
                 }
-                $return[$key] = csstidy_optimise::shorthand(trim($return[$key].$important));
+
+                $return[$key]['p'] = array($this->shorthand(trim($newval . $important)));
             }
         }
+
         return $return;
     }
 
@@ -645,7 +625,6 @@ class csstidy_optimise
      * Dissolve background property
      * @param string $str_value
      * @return array
-     * @version 1.0
      * @see merge_bg()
      * @todo full CSS 3 compliance
      */
@@ -660,19 +639,19 @@ class csstidy_optimise
         $important = '';
         $return = array('background-image' => NULL,'background-size' => NULL,'background-repeat' => NULL,'background-position' => NULL,'background-attachment'=>NULL,'background-clip' => NULL,'background-origin' => NULL,'background-color' => NULL);
 
-        if(csstidy::is_important($str_value))
+        if($this->parser->is_important($str_value))
         {
             $important = ' !important';
-            $str_value = csstidy::gvw_important($str_value);
+            $str_value = $this->parser->gvw_important($str_value);
         }
 
-        $str_value = csstidy_optimise::explode_ws(',',$str_value);
+        $str_value = $this->explode_ws(',',$str_value);
         for($i = 0; $i < count($str_value); $i++)
         {
             $have['clip'] = FALSE; $have['pos'] = FALSE;
             $have['color'] = FALSE; $have['bg'] = FALSE;
 
-            $str_value[$i] = csstidy_optimise::explode_ws(' ',trim($str_value[$i]));
+            $str_value[$i] = $this->explode_ws(' ',trim($str_value[$i]));
 
             for($j = 0; $j < count($str_value[$i]); $j++)
             {
@@ -731,31 +710,28 @@ class csstidy_optimise
      * Merges all background properties
      * @param array $input_css
      * @return array
-     * @version 1.0
      * @see dissolve_short_bg()
      * @todo full CSS 3 compliance
      */
     function merge_bg($input_css)
     {
-        $background_prop_default =& $GLOBALS['csstidy']['background_prop_default'];
+        $bpd = &$GLOBALS['csstidy']['background_prop_default'];
+
         // Max number of background images. CSS3 not yet fully implemented
-        $number_of_values = @max(count(csstidy_optimise::explode_ws(',',$input_css['background-image'])),count(csstidy_optimise::explode_ws(',',$input_css['background-color'])),1);
+        $num_vals = @max(count($this->explode_ws(',', end($input_css['background-image']['p']))), count($this->explode_ws(',', end($input_css['background-color']['p']))), 1);
+
         // Array with background images to check if BG image exists
-        $bg_img_array = @csstidy_optimise::explode_ws(',',csstidy::gvw_important($input_css['background-image']));
-        $new_bg_value = '';
-        $important = '';
+        $bg_img_array = @$this->explode_ws(',',$this->parser->gvw_important(end($input_css['background-image']['p'])));
+        $important = $new_bg_value = '';
 
-        for($i = 0; $i < $number_of_values; $i++)
-        {
-            foreach($background_prop_default as $bg_property => $default_value)
-            {
+        for ($i = 0; $i < $num_vals; ++$i) {
+            foreach ($bpd as $bg_property => $default_value) {
                 // Skip if property does not exist
-                if(!isset($input_css[$bg_property]))
-                {
+                if (!isset($input_css[$bg_property])) {
                     continue;
                 }
 
-                $cur_value = $input_css[$bg_property];
+                $cur_value = end($input_css[$bg_property]['p']);
 
                 // Skip some properties if there is no background image
                 if((!isset($bg_img_array[$i]) || $bg_img_array[$i] === 'none')
@@ -766,10 +742,10 @@ class csstidy_optimise
                 }
 
                 // Remove !important
-                if(csstidy::is_important($cur_value))
+                if($this->parser->is_important($cur_value))
                 {
                     $important = ' !important';
-                    $cur_value = csstidy::gvw_important($cur_value);
+                    $cur_value = $this->parser->gvw_important($cur_value);
                 }
 
                 // Do not add default values
@@ -778,7 +754,7 @@ class csstidy_optimise
                     continue;
                 }
 
-                $temp = csstidy_optimise::explode_ws(',',$cur_value);
+                $temp = $this->explode_ws(',',$cur_value);
 
                 if(isset($temp[$i]))
                 {
@@ -794,17 +770,20 @@ class csstidy_optimise
             }
 
             $new_bg_value = trim($new_bg_value);
-            if($i != $number_of_values-1) $new_bg_value .= ',';
+            if ($i != ($num_vals - 1)) {
+                $new_bg_value .= ',';
+            }
         }
 
         // Delete all background-properties
-        foreach($background_prop_default as $bg_property => $default_value)
-        {
+        foreach ($bpd as $bg_property => $default_value) {
             unset($input_css[$bg_property]);
         }
 
         // Add new background property
-        if($new_bg_value !== '') $input_css['background'] = $new_bg_value.$important;
+        if ($new_bg_value !== '') {
+            $input_css['background'] = array('p' => array($new_bg_value . $important));
+        }
 
         return $input_css;
     }
index ec96203..bbf7e6e 100644 (file)
@@ -68,7 +68,6 @@ class csstidy_print
     function csstidy_print(&$css)
     {
         $this->parser    =& $css;
-        $this->css       =& $css->css;
         $this->template  =& $css->template;
         $this->tokens    =& $css->tokens;
         $this->charset   =& $css->charset;
@@ -245,22 +244,28 @@ class csstidy_print
     {
         $this->tokens = array();
 
-        foreach ($this->css as $medium => $val)
-        {
-            if ($this->parser->get_cfg('sort_selectors')) ksort($val);
+        reset($this->parser->css);
+        while (list($medium, $val) = each($this->parser->css)) {
+            if ($this->parser->get_cfg('sort_selectors')) {
+                ksort($val);
+            }
+
             if ($medium != DEFAULT_AT) {
                 $this->parser->_add_token(AT_START, $medium, true);
             }
 
-            foreach ($val as $selector => $vali)
-            {
-                if ($this->parser->get_cfg('sort_properties')) ksort($vali);
+            foreach ($val as $selector => $vali) {
+                if ($this->parser->get_cfg('sort_properties')) {
+                    ksort($vali);
+                }
+
                 $this->parser->_add_token(SEL_START, $selector, true);
 
-                foreach ($vali as $property => $valj)
-                {
-                    $this->parser->_add_token(PROPERTY, $property, true);
-                    $this->parser->_add_token(VALUE, $valj, true);
+                foreach ($vali as $property => $valj) {
+                    foreach ($valj['p'] as $valk) {
+                        $this->parser->_add_token(PROPERTY, $property, true);
+                        $this->parser->_add_token(VALUE, $valk, true);
+                    }
                 }
 
                 $this->parser->_add_token(SEL_END, $selector, true);
@@ -346,4 +351,3 @@ class csstidy_print
         }
     }
 }
-?>
\ No newline at end of file