<?php
namespace {
/**
 * CssMin - A (simple) css minifier with benefits
 * 
 * --
 * Copyright (c) 2011 Joe Scylla <joe.scylla@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * --
 * 
 * @package     CssMin
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
/**
 * Abstract definition of a CSS token class.
 * 
 * Every token has to extend this class.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssToken
    {
    /**
     * Returns the token as string.
     * 
     * @return string
     */
    abstract public function __toString();
    }

/**
 * Abstract definition of a for a ruleset start token.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssRulesetStartToken extends aCssToken
    {
    
    }

/**
 * Abstract definition of a for ruleset end token.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssRulesetEndToken extends aCssToken
    {
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "}";
        }
    }

/**
 * Abstract definition of a parser plugin.
 * 
 * Every parser plugin have to extend this class. A parser plugin contains the logic to parse one or aspects of a 
 * stylesheet.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssParserPlugin
    {
    /**
     * Plugin configuration.
     * 
     * @var array
     */
    protected $configuration = array();
    /**
     * The CssParser of the plugin.
     * 
     * @var CssParser
     */
    protected $parser = null;
    /**
     * Plugin buffer.
     * 
     * @var string
     */
    protected $buffer = "";
    /**
     * Constructor.
     * 
     * @param CssParser $parser The CssParser object of this plugin.
     * @param array $configuration Plugin configuration [optional]
     * @return void
     */
    public function __construct(CssParser $parser, array $configuration = null)
        {
        $this->configuration    = $configuration;
        $this->parser           = $parser;
        }
    /**
     * Returns the array of chars triggering the parser plugin.
     * 
     * @return array
     */
    abstract public function getTriggerChars();
    /**
     * Returns the array of states triggering the parser plugin or FALSE if every state will trigger the parser plugin.
     * 
     * @return array
     */
    abstract public function getTriggerStates();
    /**
     * Parser routine of the plugin.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    abstract public function parse($index, $char, $previousChar, $state);
    }

/**
 * Abstract definition of a minifier plugin class. 
 * 
 * Minifier plugin process the parsed tokens one by one to apply changes to the token. Every minifier plugin has to 
 * extend this class.
 *
 * @package     CssMin/Minifier/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssMinifierPlugin
    {
    /**
     * Plugin configuration.
     * 
     * @var array
     */
    protected $configuration = array();
    /**
     * The CssMinifier of the plugin.
     * 
     * @var CssMinifier
     */
    protected $minifier = null;
    /**
     * Constructor.
     * 
     * @param CssMinifier $minifier The CssMinifier object of this plugin.
     * @param array $configuration Plugin configuration [optional]
     * @return void
     */
    public function __construct(CssMinifier $minifier, array $configuration = array())
        {
        $this->configuration    = $configuration;
        $this->minifier         = $minifier;
        }
    /**
     * Apply the plugin to the token.
     * 
     * @param aCssToken $token Token to process
     * @return boolean Return TRUE to break the processing of this token; FALSE to continue
     */
    abstract public function apply(aCssToken &$token);
    /**
     * --
     * 
     * @return array
     */
    abstract public function getTriggerTokens();
    }

/**
 * Abstract definition of a minifier filter class. 
 * 
 * Minifier filters allows a pre-processing of the parsed token to add, edit or delete tokens. Every minifier filter
 * has to extend this class.
 * 
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssMinifierFilter
    {
    /**
     * Filter configuration.
     * 
     * @var array
     */
    protected $configuration = array();
    /**
     * The CssMinifier of the filter.
     * 
     * @var CssMinifier
     */
    protected $minifier = null;
    /**
     * Constructor.
     * 
     * @param CssMinifier $minifier The CssMinifier object of this plugin.
     * @param array $configuration Filter configuration [optional]
     * @return void
     */
    public function __construct(CssMinifier $minifier, array $configuration = array())
        {
        $this->configuration    = $configuration;
        $this->minifier         = $minifier;
        }
    /**
     * Filter the tokens.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
     */
    abstract public function apply(array &$tokens);
    }

/**
 * Abstract formatter definition.
 * 
 * Every formatter have to extend this class.
 * 
 * @package     CssMin/Formatter
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssFormatter
    {
    /**
     * Indent string.
     * 
     * @var string
     */
    protected $indent = "    ";
    /**
     * Declaration padding.
     * 
     * @var integer
     */
    protected $padding = 0;
    /**
     * Tokens.
     * 
     * @var array
     */
    protected $tokens = array();
    /**
     * Constructor.
     * 
     * @param array $tokens Array of CssToken
     * @param string $indent Indent string [optional]
     * @param integer $padding Declaration value padding [optional]
     */
    public function __construct(array $tokens, $indent = null, $padding = null)
        {
        $this->tokens   = $tokens;
        $this->indent   = !is_null($indent) ? $indent : $this->indent;
        $this->padding  = !is_null($padding) ? $padding : $this->padding;
        }
    /**
     * Returns the array of aCssToken as formatted string.
     * 
     * @return string
     */
    abstract public function __toString();
    }

/**
 * Abstract definition of a ruleset declaration token.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssDeclarationToken extends aCssToken
    {
    /**
     * Is the declaration flagged as important?
     * 
     * @var boolean
     */
    public $IsImportant = false;
    /**
     * Is the declaration flagged as last one of the ruleset?
     * 
     * @var boolean
     */
    public $IsLast = false;
    /**
     * Property name of the declaration.
     * 
     * @var string
     */
    public $Property = "";
    /**
     * Value of the declaration.
     * 
     * @var string
     */
    public $Value = "";
    /**
     * Set the properties of the @font-face declaration. 
     * 
     * @param string $property Property of the declaration
     * @param string $value Value of the declaration
     * @param boolean $isImportant Is the !important flag is set?
     * @param boolean $IsLast Is the declaration the last one of the block?
     * @return void
     */
    public function __construct($property, $value, $isImportant = false, $isLast = false)
        {
        $this->Property     = $property;
        $this->Value        = $value;
        $this->IsImportant  = $isImportant;
        $this->IsLast       = $isLast;
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return $this->Property . ":" . $this->Value . ($this->IsImportant ? " !important" : "") . ($this->IsLast ? "" : ";");
        }
    }

/**
 * Abstract definition of a for at-rule block start token.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssAtBlockStartToken extends aCssToken
    {
    
    }

/**
 * Abstract definition of a for at-rule block end token.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
abstract class aCssAtBlockEndToken extends aCssToken
    {
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "}";
        }
    }

/**
 * {@link aCssFromatter Formatter} returning the CSS source in {@link http://goo.gl/etzLs Whitesmiths indent style}.
 * 
 * @package     CssMin/Formatter
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssWhitesmithsFormatter extends aCssFormatter
    {
    /**
     * Implements {@link aCssFormatter::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        $r              = array();
        $level          = 0;
        for ($i = 0, $l = count($this->tokens); $i < $l; $i++)
            {
            $token      = $this->tokens[$i];
            $class      = get_class($token);
            $indent     = str_repeat($this->indent, $level);
            if ($class === "CssCommentToken")
                {
                $lines = array_map("trim", explode("\n", $token->Comment));
                for ($ii = 0, $ll = count($lines); $ii < $ll; $ii++)
                    {
                    $r[] = $indent . (substr($lines[$ii], 0, 1) == "*" ? " " : "") . $lines[$ii];
                    }
                }
            elseif ($class === "CssAtCharsetToken")
                {
                $r[] = $indent . "@charset " . $token->Charset . ";";
                }
            elseif ($class === "CssAtFontFaceStartToken")
                {
                $r[] = $indent . "@font-face";
                $r[] = $this->indent . $indent . "{";
                $level++;
                }
            elseif ($class === "CssAtImportToken")
                {
                $r[] = $indent . "@import " . $token->Import . " " . implode(", ", $token->MediaTypes) . ";";
                }
            elseif ($class === "CssAtKeyframesStartToken")
                {
                $r[] = $indent . "@keyframes \"" . $token->Name . "\"";
                $r[] = $this->indent . $indent . "{";
                $level++;
                }
            elseif ($class === "CssAtMediaStartToken")
                {
                $r[] = $indent . "@media " . implode(", ", $token->MediaTypes);
                $r[] = $this->indent . $indent . "{";
                $level++;
                }
            elseif ($class === "CssAtPageStartToken")
                {
                $r[] = $indent . "@page";
                $r[] = $this->indent . $indent . "{";
                $level++;
                }
            elseif ($class === "CssAtVariablesStartToken")
                {
                $r[] = $indent . "@variables " . implode(", ", $token->MediaTypes);
                $r[] = $this->indent . $indent . "{";
                $level++;
                }
            elseif ($class === "CssRulesetStartToken" || $class === "CssAtKeyframesRulesetStartToken")
                {
                $r[] = $indent . implode(", ", $token->Selectors);
                $r[] = $this->indent . $indent . "{";
                $level++;
                }
            elseif ($class == "CssAtFontFaceDeclarationToken"
                || $class === "CssAtKeyframesRulesetDeclarationToken"
                || $class === "CssAtPageDeclarationToken"
                || $class == "CssAtVariablesDeclarationToken"
                || $class === "CssRulesetDeclarationToken"
                )
                {
                $declaration = $indent . $token->Property . ": ";
                if ($this->padding)
                    {
                    $declaration = str_pad($declaration, $this->padding, " ", STR_PAD_RIGHT);
                    }
                $r[] = $declaration . $token->Value . ($token->IsImportant ? " !important" : "") . ";";
                }
            elseif ($class === "CssAtFontFaceEndToken"
                || $class === "CssAtMediaEndToken"
                || $class === "CssAtKeyframesEndToken"
                || $class === "CssAtKeyframesRulesetEndToken"
                || $class === "CssAtPageEndToken"
                || $class === "CssAtVariablesEndToken"
                || $class === "CssRulesetEndToken"
                )
                {
                $r[] = $indent . "}";
                $level--;
                }
            }
        return implode("\n", $r);
        }
    }

/**
 * This {@link aCssMinifierPlugin} will process var-statement and sets the declaration value to the variable value. 
 * 
 * This plugin only apply the variable values. The variable values itself will get parsed by the
 * {@link CssVariablesMinifierFilter}.
 * 
 * Example:
 * <code>
 * @variables
 *      {
 *      defaultColor: black;
 *      }
 * color: var(defaultColor);
 * </code>
 * 
 * Will get converted to:
 * <code>
 * color:black;
 * </code>
 *
 * @package     CssMin/Minifier/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssVariablesMinifierPlugin extends aCssMinifierPlugin
    {
    /**
     * Regular expression matching a value.
     * 
     * @var string
     */
    private $reMatch = "/var\((.+)\)/iSU";
    /**
     * Parsed variables.
     * 
     * @var array
     */
    private $variables = null;
    /**
     * Returns the variables.
     * 
     * @return array
     */
    public function getVariables()
        {
        return $this->variables;
        }
    /**
     * Implements {@link aCssMinifierPlugin::minify()}.
     * 
     * @param aCssToken $token Token to process
     * @return boolean Return TRUE to break the processing of this token; FALSE to continue
     */
    public function apply(aCssToken &$token)
        {
        if (stripos($token->Value, "var") !== false && preg_match_all($this->reMatch, $token->Value, $m))
            {
            $mediaTypes = $token->MediaTypes;
            if (!in_array("all", $mediaTypes))
                {
                $mediaTypes[] = "all";
                }
            for ($i = 0, $l = count($m[0]); $i < $l; $i++)
                {
                $variable   = trim($m[1][$i]);
                foreach ($mediaTypes as $mediaType)
                    {
                    if (isset($this->variables[$mediaType], $this->variables[$mediaType][$variable]))
                        {
                        // Variable value found => set the declaration value to the variable value and return
                        $token->Value = str_replace($m[0][$i], $this->variables[$mediaType][$variable], $token->Value);
                        continue 2;
                        }
                    }
                // If no value was found trigger an error and replace the token with a CssNullToken
                CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": No value found for variable <code>" . $variable . "</code> in media types <code>" . implode(", ", $mediaTypes) . "</code>", (string) $token));
                $token = new CssNullToken();
                return true;
                }
            }
        return false;
        }
    /**
     * Implements {@link aMinifierPlugin::getTriggerTokens()}
     * 
     * @return array
     */
    public function getTriggerTokens()
        {
        return array
            (
            "CssAtFontFaceDeclarationToken",
            "CssAtPageDeclarationToken",
            "CssRulesetDeclarationToken"
            );
        }
    /**
     * Sets the variables.
     * 
     * @param array $variables Variables to set
     * @return void
     */
    public function setVariables(array $variables)
        {
        $this->variables = $variables;
        }
    }

/**
 * This {@link aCssMinifierFilter minifier filter} will parse the variable declarations out of @variables at-rule 
 * blocks. The variables will get store in the {@link CssVariablesMinifierPlugin} that will apply the variables to 
 * declaration.
 * 
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssVariablesMinifierFilter extends aCssMinifierFilter
    {
    /**
     * Implements {@link aCssMinifierFilter::filter()}.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
     */
    public function apply(array &$tokens)
        {
        $variables          = array();
        $defaultMediaTypes  = array("all");
        $mediaTypes         = array();
        $remove             = array();
        for($i = 0, $l = count($tokens); $i < $l; $i++)
            {
            // @variables at-rule block found
            if (get_class($tokens[$i]) === "CssAtVariablesStartToken")
                {
                $remove[] = $i;
                $mediaTypes = (count($tokens[$i]->MediaTypes) == 0 ? $defaultMediaTypes : $tokens[$i]->MediaTypes);
                foreach ($mediaTypes as $mediaType)
                    {
                    if (!isset($variables[$mediaType]))
                        {
                        $variables[$mediaType] = array();
                        }
                    }
                // Read the variable declaration tokens
                for($i = $i; $i < $l; $i++)
                    {
                    // Found a variable declaration => read the variable values
                    if (get_class($tokens[$i]) === "CssAtVariablesDeclarationToken")
                        {
                        foreach ($mediaTypes as $mediaType)
                            {
                            $variables[$mediaType][$tokens[$i]->Property] = $tokens[$i]->Value;
                            }
                        $remove[] = $i;
                        }
                    // Found the variables end token => break;
                    elseif (get_class($tokens[$i]) === "CssAtVariablesEndToken")
                        {
                        $remove[] = $i;
                        break;
                        }
                    }
                }
            }
        // Variables in @variables at-rule blocks
        foreach($variables as $mediaType => $null)
            {
            foreach($variables[$mediaType] as $variable => $value)
                {
                // If a var() statement in a variable value found...
                if (stripos($value, "var") !== false && preg_match_all("/var\((.+)\)/iSU", $value, $m))
                    {
                    // ... then replace the var() statement with the variable values.
                    for ($i = 0, $l = count($m[0]); $i < $l; $i++)
                        {
                        $variables[$mediaType][$variable] = str_replace($m[0][$i], (isset($variables[$mediaType][$m[1][$i]]) ? $variables[$mediaType][$m[1][$i]] : ""), $variables[$mediaType][$variable]);
                        }
                    }
                }
            }
        // Remove the complete @variables at-rule block
        foreach ($remove as $i)
            {
            $tokens[$i] = null;
            }
        if (!($plugin = $this->minifier->getPlugin("CssVariablesMinifierPlugin")))
            {
            CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin <code>CssVariablesMinifierPlugin</code> was not found but is required for <code>" . __CLASS__ . "</code>"));
            }
        else
            {
            $plugin->setVariables($variables);
            }
        return count($remove);
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for preserve parsing url() values.
 * 
 * This plugin return no {@link aCssToken CssToken} but ensures that url() values will get parsed properly.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssUrlParserPlugin extends aCssParserPlugin
    {
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("(", ")");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return false;
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        // Start of string
        if ($char === "(" && strtolower(substr($this->parser->getSource(), $index - 3, 4)) === "url(" && $state !== "T_URL")
            {
            $this->parser->pushState("T_URL");
            $this->parser->setExclusive(__CLASS__);
            }
        // Escaped LF in url => remove escape backslash and LF
        elseif ($char === "\n" && $previousChar === "\\" && $state === "T_URL")
            {
            $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2));
            }
        // Parse error: Unescaped LF in string literal
        elseif ($char === "\n" && $previousChar !== "\\" && $state === "T_URL")
            {
            $line = $this->parser->getBuffer();
            $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -1) . ")"); // Replace the LF with the url string delimiter
            $this->parser->popState();
            $this->parser->unsetExclusive();
            CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated string literal", $line . "_"));
            }
        // End of string
        elseif ($char === ")" && $state === "T_URL")
            {
            $this->parser->popState();
            $this->parser->unsetExclusive();
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for preserve parsing string values.
 * 
 * This plugin return no {@link aCssToken CssToken} but ensures that string values will get parsed properly.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssStringParserPlugin extends aCssParserPlugin
    {
    /**
     * Current string delimiter char.
     * 
     * @var string
     */
    private $delimiterChar = null;
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("\"", "'", "\n");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return false;
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        // Start of string
        if (($char === "\"" || $char === "'") && $state !== "T_STRING")
            {
            $this->delimiterChar = $char;
            $this->parser->pushState("T_STRING");
            $this->parser->setExclusive(__CLASS__);
            }
        // Escaped LF in string => remove escape backslash and LF
        elseif ($char === "\n" && $previousChar === "\\" && $state === "T_STRING")
            {
            $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2));
            }
        // Parse error: Unescaped LF in string literal
        elseif ($char === "\n" && $previousChar !== "\\" && $state === "T_STRING")
            {
            $line = $this->parser->getBuffer();
            $this->parser->popState();
            $this->parser->unsetExclusive();
            $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -1) . $this->delimiterChar); // Replace the LF with the current string char
            CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated string literal", $line . "_"));
            $this->delimiterChar = null;
            }
        // End of string
        elseif ($char === $this->delimiterChar && $state === "T_STRING")
            {
            // If the Previous char is a escape char count the amount of the previous escape chars. If the amount of 
            // escape chars is uneven do not end the string
            if ($previousChar == "\\")
                {
                $source = $this->parser->getSource();
                $c      = 1;
                $i      = $index - 2;
                while (substr($source, $i, 1) === "\\")
                    {
                    $c++; $i--;
                    }
                if ($c % 2)
                    {
                    return false;
                    }
                }
            $this->parser->popState();
            $this->parser->unsetExclusive();
            $this->delimiterChar = null;
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * This {@link aCssMinifierFilter minifier filter} sorts the ruleset declarations of a ruleset by name.
 * 
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Rowan Beentje <http://assanka.net>
 * @copyright   Rowan Beentje <http://assanka.net>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssSortRulesetPropertiesMinifierFilter extends aCssMinifierFilter
    {
    /**
     * Implements {@link aCssMinifierFilter::filter()}.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value larger than 0 will rebuild the array
     */
    public function apply(array &$tokens)
        {
        $r = 0;
        for ($i = 0, $l = count($tokens); $i < $l; $i++)
            {
            // Only look for ruleset start rules
            if (get_class($tokens[$i]) !== "CssRulesetStartToken") { continue; }
            // Look for the corresponding ruleset end
            $endIndex = false;
            for ($ii = $i + 1; $ii < $l; $ii++)
                {
                if (get_class($tokens[$ii]) !== "CssRulesetEndToken") { continue; }
                $endIndex = $ii;
                break;
                }
            if (!$endIndex) { break; }
            $startIndex = $i;
            $i = $endIndex;
            // Skip if there's only one token in this ruleset
            if ($endIndex - $startIndex <= 2) { continue; }
            // Ensure that everything between the start and end is a declaration token, for safety
            for ($ii = $startIndex + 1; $ii < $endIndex; $ii++)
                {
                if (get_class($tokens[$ii]) !== "CssRulesetDeclarationToken") { continue(2); }
                }
            $declarations = array_slice($tokens, $startIndex + 1, $endIndex - $startIndex - 1);
            // Check whether a sort is required
            $sortRequired = $lastPropertyName = false;
            foreach ($declarations as $declaration) 
                {
                if ($lastPropertyName)
                    {
                    if (strcmp($lastPropertyName, $declaration->Property) > 0)
                        {
                        $sortRequired = true;
                        break;
                        }
                    }
                $lastPropertyName = $declaration->Property;
                }
            if (!$sortRequired) { continue; }
            // Arrange the declarations alphabetically by name
            usort($declarations, array(__CLASS__, "userDefinedSort1"));
            // Update "IsLast" property
            for ($ii = 0, $ll = count($declarations) - 1; $ii <= $ll; $ii++)
                {
                if ($ii == $ll)
                    {
                    $declarations[$ii]->IsLast = true;
                    }
                else
                    {
                    $declarations[$ii]->IsLast = false;
                    }
                }
            // Splice back into the array.
            array_splice($tokens, $startIndex + 1, $endIndex - $startIndex - 1, $declarations);
            $r += $endIndex - $startIndex - 1;
            }
        return $r;
        }
    /**
     * User defined sort function.
     * 
     * @return integer
     */
    public static function userDefinedSort1($a, $b)
        {
        return strcmp($a->Property, $b->Property);
        }
    }

/**
 * This {@link aCssToken CSS token} represents the start of a ruleset.
 * 
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssRulesetStartToken extends aCssRulesetStartToken
    {
    /**
     * Array of selectors.
     * 
     * @var array
     */
    public $Selectors = array();
    /**
     * Set the properties of a ruleset token.
     * 
     * @param array $selectors Selectors of the ruleset 
     * @return void
     */
    public function __construct(array $selectors = array())
        {
        $this->Selectors = $selectors;
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return implode(",", $this->Selectors) . "{";
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for parsing ruleset block with including declarations.
 * 
 * Found rulesets will add a {@link CssRulesetStartToken} and {@link CssRulesetEndToken} to the 
 * parser; including declarations as {@link CssRulesetDeclarationToken}.
 *
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssRulesetParserPlugin extends aCssParserPlugin
    {
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array(",", "{", "}", ":", ";");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return array("T_DOCUMENT", "T_AT_MEDIA", "T_RULESET::SELECTORS", "T_RULESET", "T_RULESET_DECLARATION");
        }
    /**
     * Selectors.
     * 
     * @var array
     */
    private $selectors = array();
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        // Start of Ruleset and selectors
        if ($char === "," && ($state === "T_DOCUMENT" || $state === "T_AT_MEDIA" || $state === "T_RULESET::SELECTORS"))
            {
            if ($state !== "T_RULESET::SELECTORS")
                {
                $this->parser->pushState("T_RULESET::SELECTORS");
                }
            $this->selectors[] = $this->parser->getAndClearBuffer(",{");
            }
        // End of selectors and start of declarations
        elseif ($char === "{" && ($state === "T_DOCUMENT" || $state === "T_AT_MEDIA" || $state === "T_RULESET::SELECTORS"))
            {
            if ($this->parser->getBuffer() !== "")
                {
                $this->selectors[] = $this->parser->getAndClearBuffer(",{");
                if ($state == "T_RULESET::SELECTORS")
                    {
                    $this->parser->popState();
                    }
                $this->parser->pushState("T_RULESET");
                $this->parser->appendToken(new CssRulesetStartToken($this->selectors));
                $this->selectors = array();
                }
            }
        // Start of declaration
        elseif ($char === ":" && $state === "T_RULESET")
            {
            $this->parser->pushState("T_RULESET_DECLARATION");
            $this->buffer = $this->parser->getAndClearBuffer(":;", true);
            }
        // Unterminated ruleset declaration
        elseif ($char === ":" && $state === "T_RULESET_DECLARATION")
            {
            // Ignore Internet Explorer filter declarations
            if ($this->buffer === "filter")
                {
                return false;
                }
            CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
            }
        // End of declaration
        elseif (($char === ";" || $char === "}") && $state === "T_RULESET_DECLARATION")
            {
            $value = $this->parser->getAndClearBuffer(";}");
            if (strtolower(substr($value, -10, 10)) === "!important")
                {
                $value = trim(substr($value, 0, -10));
                $isImportant = true;
                }
            else
                {
                $isImportant = false;
                }
            $this->parser->popState();
            $this->parser->appendToken(new CssRulesetDeclarationToken($this->buffer, $value, $this->parser->getMediaTypes(), $isImportant));
            // Declaration ends with a right curly brace; so we have to end the ruleset
            if ($char === "}")
                {
                $this->parser->appendToken(new CssRulesetEndToken());
                $this->parser->popState();
                }
            $this->buffer = "";
            }
        // End of ruleset
        elseif ($char === "}" && $state === "T_RULESET")
            {
            $this->parser->popState();
            $this->parser->clearBuffer();
            $this->parser->appendToken(new CssRulesetEndToken());
            $this->buffer = "";
            $this->selectors = array();
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 *  This {@link aCssToken CSS token} represents the end of a ruleset.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssRulesetEndToken extends aCssRulesetEndToken
    {
    
    }

/**
 * This {@link aCssToken CSS token} represents a ruleset declaration.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssRulesetDeclarationToken extends aCssDeclarationToken
    {
    /**
     * Media types of the declaration.
     * 
     * @var array
     */
    public $MediaTypes = array("all");
    /**
     * Set the properties of a ddocument- or at-rule @media level declaration. 
     * 
     * @param string $property Property of the declaration
     * @param string $value Value of the declaration
     * @param mixed $mediaTypes Media types of the declaration
     * @param boolean $isImportant Is the !important flag is set
     * @param boolean $isLast Is the declaration the last one of the ruleset
     * @return void
     */
    public function __construct($property, $value, $mediaTypes = null, $isImportant = false, $isLast = false)
        {
        parent::__construct($property, $value, $isImportant, $isLast);
        $this->MediaTypes   = $mediaTypes ? $mediaTypes : array("all");
        }
    }

/**
 * This {@link aCssMinifierFilter minifier filter} sets the IsLast property of any last declaration in a ruleset, 
 * @font-face at-rule or @page at-rule block. If the property IsLast is TRUE the decrations will get stringified 
 * without tailing semicolon.
 * 
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssRemoveLastDelarationSemiColonMinifierFilter extends aCssMinifierFilter
    {
    /**
     * Implements {@link aCssMinifierFilter::filter()}.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
     */
    public function apply(array &$tokens)
        {
        for ($i = 0, $l = count($tokens); $i < $l; $i++)
            {
            $current    = get_class($tokens[$i]);
            $next       = isset($tokens[$i+1]) ? get_class($tokens[$i+1]) : false;
            if (($current === "CssRulesetDeclarationToken" && $next === "CssRulesetEndToken") ||
                ($current === "CssAtFontFaceDeclarationToken" && $next === "CssAtFontFaceEndToken") || 
                ($current === "CssAtPageDeclarationToken" && $next === "CssAtPageEndToken"))
                {
                $tokens[$i]->IsLast = true;
                }
            }
        return 0;
        }
    }

/**
 * This {@link aCssMinifierFilter minifier filter} will remove any empty rulesets (including @keyframes at-rule block 
 * rulesets).
 *
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssRemoveEmptyRulesetsMinifierFilter extends aCssMinifierFilter
    {
    /**
     * Implements {@link aCssMinifierFilter::filter()}.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
     */
    public function apply(array &$tokens)
        {
        $r = 0;
        for ($i = 0, $l = count($tokens); $i < $l; $i++)
            {
            $current    = get_class($tokens[$i]);
            $next       = isset($tokens[$i + 1]) ? get_class($tokens[$i + 1]) : false;
            if (($current === "CssRulesetStartToken" && $next === "CssRulesetEndToken") ||
                ($current === "CssAtKeyframesRulesetStartToken" && $next === "CssAtKeyframesRulesetEndToken" && !array_intersect(array("from", "0%", "to", "100%"), array_map("strtolower", $tokens[$i]->Selectors)))
                )
                {
                $tokens[$i]     = null;
                $tokens[$i + 1] = null;
                $i++;
                $r = $r + 2;
                }
            }
        return $r;
        }
    }

/**
 * This {@link aCssMinifierFilter minifier filter} will remove any empty @font-face, @keyframes, @media and @page 
 * at-rule blocks.
 * 
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssRemoveEmptyAtBlocksMinifierFilter extends aCssMinifierFilter
    {
    /**
     * Implements {@link aCssMinifierFilter::filter()}.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
     */
    public function apply(array &$tokens)
        {
        $r = 0;
        for ($i = 0, $l = count($tokens); $i < $l; $i++)
            {
            $current    = get_class($tokens[$i]);
            $next       = isset($tokens[$i + 1]) ? get_class($tokens[$i + 1]) : false;
            if (($current === "CssAtFontFaceStartToken" && $next === "CssAtFontFaceEndToken") ||
                ($current === "CssAtKeyframesStartToken" && $next === "CssAtKeyframesEndToken") ||
                ($current === "CssAtPageStartToken" && $next === "CssAtPageEndToken") ||
                ($current === "CssAtMediaStartToken" && $next === "CssAtMediaEndToken"))
                {
                $tokens[$i]     = null;
                $tokens[$i + 1] = null;
                $i++;
                $r = $r + 2;
                }
            }
        return $r;
        }
    }

/**
 * This {@link aCssMinifierFilter minifier filter} will remove any comments from the array of parsed tokens.
 * 
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssRemoveCommentsMinifierFilter extends aCssMinifierFilter
    {
    /**
     * Implements {@link aCssMinifierFilter::filter()}.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
     */
    public function apply(array &$tokens)
        {
        $r = 0;
        for ($i = 0, $l = count($tokens); $i < $l; $i++)
            {
            if (get_class($tokens[$i]) === "CssCommentToken")
                {
                $tokens[$i] = null;
                $r++;
                }
            }
        return $r;
        }
    }

/**
 * CSS Parser.
 * 
 * @package     CssMin/Parser
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssParser
    {
    /**
     * Parse buffer.
     * 
     * @var string
     */
    private $buffer = "";
    /**
     * {@link aCssParserPlugin Plugins}.
     * 
     * @var array
     */
    private $plugins = array();
    /**
     * Source to parse.
     * 
     * @var string
     */
    private $source = "";
    /**
     * Current state.
     * 
     * @var integer
     */
    private $state = "T_DOCUMENT";
    /**
     * Exclusive state.
     * 
     * @var string
     */
    private $stateExclusive = false;
    /**
     * Media types state.
     * 
     * @var mixed
     */
    private $stateMediaTypes = false;
    /**
     * State stack.
     * 
     * @var array
     */
    private $states = array("T_DOCUMENT");
    /**
     * Parsed tokens.
     * 
     * @var array
     */
    private $tokens = array();
    /**
     * Constructer.
     * 
     *  Create instances of the used {@link aCssParserPlugin plugins}.
     * 
     * @param string $source CSS source [optional]
     * @param array $plugins Plugin configuration [optional]
     * @return void
     */
    public function __construct($source = null, array $plugins = null)
        {
        $plugins = array_merge(array
            (
            "Comment"       => true,
            "String"        => true,
            "Url"           => true,
            "Expression"    => true,
            "Ruleset"       => true,
            "AtCharset"     => true,
            "AtFontFace"    => true,
            "AtImport"      => true,
            "AtKeyframes"   => true,
            "AtMedia"       => true,
            "AtPage"        => true,
            "AtVariables"   => true
            ), is_array($plugins) ? $plugins : array());
        // Create plugin instances
        foreach ($plugins as $name => $config)
            {
            if ($config !== false)
                {
                $class  = "Css" . $name . "ParserPlugin";
                $config = is_array($config) ? $config : array();
                if (class_exists($class))
                    {
                    $this->plugins[] = new $class($this, $config);
                    }
                else
                    {
                    CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin <code>" . $name . "</code> with the class name <code>" . $class . "</code> was not found"));
                    }
                }
            }
        if (!is_null($source))
            {
            $this->parse($source);
            }
        }
    /**
     * Append a token to the array of tokens.
     * 
     * @param aCssToken $token Token to append
     * @return void
     */
    public function appendToken(aCssToken $token)
        {
        $this->tokens[] = $token;
        }
    /**
     * Clears the current buffer.
     * 
     * @return void
     */
    public function clearBuffer()
        {
        $this->buffer = "";
        }
    /**
     * Returns and clear the current buffer.
     * 
     * @param string $trim Chars to use to trim the returned buffer
     * @param boolean $tolower if TRUE the returned buffer will get converted to lower case
     * @return string
     */
    public function getAndClearBuffer($trim = "", $tolower = false)
        {
        $r = $this->getBuffer($trim, $tolower);
        $this->buffer = "";
        return $r;
        }
    /**
     * Returns the current buffer.
     * 
     * @param string $trim Chars to use to trim the returned buffer
     * @param boolean $tolower if TRUE the returned buffer will get converted to lower case
     * @return string
     */
    public function getBuffer($trim = "", $tolower = false)
        {
        $r = $this->buffer;
        if ($trim)
            {
            $r = trim($r, " \t\n\r\0\x0B" . $trim);
            }
        if ($tolower)
            {
            $r = strtolower($r);
            }
        return $r;
        }
    /**
     * Returns the current media types state.
     * 
     * @return array
     */ 
    public function getMediaTypes()
        {
        return $this->stateMediaTypes;
        }
    /**
     * Returns the CSS source.
     * 
     * @return string
     */
    public function getSource()
        {
        return $this->source;
        }
    /**
     * Returns the current state.
     * 
     * @return integer The current state
     */
    public function getState()
        {
        return $this->state;
        }
    /**
     * Returns a plugin by class name.
     * 
     * @param string $name Class name of the plugin 
     * @return aCssParserPlugin
     */
    public function getPlugin($class)
        {
        static $index = null;
        if (is_null($index))
            {
            $index = array();
            for ($i = 0, $l = count($this->plugins); $i < $l; $i++)
                {
                $index[get_class($this->plugins[$i])] = $i;
                }
            }
        return isset($index[$class]) ? $this->plugins[$index[$class]] : false;
        }
    /**
     * Returns the parsed tokens.
     * 
     * @return array
     */
    public function getTokens()
        {
        return $this->tokens;
        }
    /**
     * Returns if the current state equals the passed state.
     * 
     * @param integer $state State to compare with the current state
     * @return boolean TRUE is the state equals to the passed state; FALSE if not
     */
    public function isState($state)
        {
        return ($this->state == $state);
        }
    /**
     * Parse the CSS source and return a array with parsed tokens.
     * 
     * @param string $source CSS source
     * @return array Array with tokens
     */
    public function parse($source)
        {
        // Reset
        $this->source = "";
        $this->tokens = array();
        // Create a global and plugin lookup table for trigger chars; set array of plugins as local variable and create 
        // several helper variables for plugin handling
        $globalTriggerChars     = "";
        $plugins                = $this->plugins;
        $pluginCount            = count($plugins);
        $pluginIndex            = array();
        $pluginTriggerStates    = array();
        $pluginTriggerChars     = array();
        for ($i = 0, $l = count($plugins); $i < $l; $i++)
            {
            $tPluginClassName               = get_class($plugins[$i]);
            $pluginTriggerChars[$i]         = implode("", $plugins[$i]->getTriggerChars());
            $tPluginTriggerStates           = $plugins[$i]->getTriggerStates();
            $pluginTriggerStates[$i]        = $tPluginTriggerStates === false ? false : "|" . implode("|", $tPluginTriggerStates) . "|";
            $pluginIndex[$tPluginClassName] = $i;
            for ($ii = 0, $ll = strlen($pluginTriggerChars[$i]); $ii < $ll; $ii++)
                {
                $c = substr($pluginTriggerChars[$i], $ii, 1);
                if (strpos($globalTriggerChars, $c) === false)
                    {
                    $globalTriggerChars .= $c;
                    }
                }
            }
        // Normalise line endings
        $source         = str_replace("\r\n", "\n", $source);   // Windows to Unix line endings
        $source         = str_replace("\r", "\n", $source);     // Mac to Unix line endings
        $this->source   = $source;
        // Variables
        $buffer         = &$this->buffer;
        $exclusive      = &$this->stateExclusive;
        $state          = &$this->state;
        $c = $p         = null;
        // --
        for ($i = 0, $l = strlen($source); $i < $l; $i++)
            {
            // Set the current Char
            $c = $source[$i]; // Is faster than: $c = substr($source, $i, 1);
            // Normalize and filter double whitespace characters
            if ($exclusive === false)
                {
                if ($c === "\n" || $c === "\t")
                    {
                    $c = " ";
                    }
                if ($c === " " && $p === " ")
                    {
                    continue;
                    }
                }
            $buffer .= $c;
            // Extended processing only if the current char is a global trigger char
            if (strpos($globalTriggerChars, $c) !== false)
                {
                // Exclusive state is set; process with the exclusive plugin 
                if ($exclusive)
                    {
                    $tPluginIndex = $pluginIndex[$exclusive];
                    if (strpos($pluginTriggerChars[$tPluginIndex], $c) !== false && ($pluginTriggerStates[$tPluginIndex] === false || strpos($pluginTriggerStates[$tPluginIndex], $state) !== false))
                        {
                        $r = $plugins[$tPluginIndex]->parse($i, $c, $p, $state);
                        // Return value is TRUE => continue with next char
                        if ($r === true)
                            {
                            continue;
                            }
                        // Return value is numeric => set new index and continue with next char
                        elseif ($r !== false && $r != $i)
                            {
                            $i = $r;
                            continue;
                            }
                        }
                    }
                // Else iterate through the plugins
                else
                    {
                    $triggerState = "|" . $state . "|";
                    for ($ii = 0, $ll = $pluginCount; $ii < $ll; $ii++)
                        {
                        // Only process if the current char is one of the plugin trigger chars
                        if (strpos($pluginTriggerChars[$ii], $c) !== false && ($pluginTriggerStates[$ii] === false || strpos($pluginTriggerStates[$ii], $triggerState) !== false))
                            {
                            // Process with the plugin
                            $r = $plugins[$ii]->parse($i, $c, $p, $state);
                            // Return value is TRUE => break the plugin loop and and continue with next char
                            if ($r === true)
                                {
                                break;
                                }
                            // Return value is numeric => set new index, break the plugin loop and and continue with next char
                            elseif ($r !== false && $r != $i)
                                {
                                $i = $r;
                                break;
                                }
                            }
                        }
                    }
                }
            $p = $c; // Set the parent char
            }
        return $this->tokens;
        }
    /**
     * Remove the last state of the state stack and return the removed stack value.
     * 
     * @return integer Removed state value
     */
    public function popState()
        {
        $r = array_pop($this->states);
        $this->state = $this->states[count($this->states) - 1];
        return $r;
        }
    /**
     * Adds a new state onto the state stack.
     * 
     * @param integer $state State to add onto the state stack.
     * @return integer The index of the added state in the state stacks
     */
    public function pushState($state)
        {
        $r = array_push($this->states, $state);
        $this->state = $this->states[count($this->states) - 1];
        return $r;
        }
    /**
     * Sets/restores the buffer.
     * 
     * @param string $buffer Buffer to set
     * @return void
     */ 
    public function setBuffer($buffer)
        {
        $this->buffer = $buffer;
        }
    /**
     * Set the exclusive state.
     * 
     * @param string $exclusive Exclusive state
     * @return void
     */ 
    public function setExclusive($exclusive)
        {
        $this->stateExclusive = $exclusive; 
        }
    /**
     * Set the media types state.
     * 
     * @param array $mediaTypes Media types state
     * @return void
     */ 
    public function setMediaTypes(array $mediaTypes)
        {
        $this->stateMediaTypes = $mediaTypes; 
        }
    /**
     * Sets the current state in the state stack; equals to {@link CssParser::popState()} + {@link CssParser::pushState()}.
     * 
     * @param integer $state State to set
     * @return integer
     */
    public function setState($state)
        {
        $r = array_pop($this->states);
        array_push($this->states, $state);
        $this->state = $this->states[count($this->states) - 1];
        return $r;
        }
    /**
     * Removes the exclusive state.
     * 
     * @return void
     */
    public function unsetExclusive()
        {
        $this->stateExclusive = false;
        }
    /**
     * Removes the media types state.
     * 
     * @return void
     */
    public function unsetMediaTypes()
        {
        $this->stateMediaTypes = false;
        }
    }

/**
 * {@link aCssFromatter Formatter} returning the CSS source in {@link http://goo.gl/j4XdU OTBS indent style} (The One True Brace Style).
 * 
 * @package     CssMin/Formatter
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssOtbsFormatter extends aCssFormatter
    {
    /**
     * Implements {@link aCssFormatter::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        $r              = array();
        $level          = 0;
        for ($i = 0, $l = count($this->tokens); $i < $l; $i++)
            {
            $token      = $this->tokens[$i];
            $class      = get_class($token);
            $indent     = str_repeat($this->indent, $level);
            if ($class === "CssCommentToken")
                {
                $lines = array_map("trim", explode("\n", $token->Comment));
                for ($ii = 0, $ll = count($lines); $ii < $ll; $ii++)
                    {
                    $r[] = $indent . (substr($lines[$ii], 0, 1) == "*" ? " " : "") . $lines[$ii];
                    }
                }
            elseif ($class === "CssAtCharsetToken")
                {
                $r[] = $indent . "@charset " . $token->Charset . ";";
                }
            elseif ($class === "CssAtFontFaceStartToken")
                {
                $r[] = $indent . "@font-face {";
                $level++;
                }
            elseif ($class === "CssAtImportToken")
                {
                $r[] = $indent . "@import " . $token->Import . " " . implode(", ", $token->MediaTypes) . ";";
                }
            elseif ($class === "CssAtKeyframesStartToken")
                {
                $r[] = $indent . "@keyframes \"" . $token->Name . "\" {";
                $level++;
                }
            elseif ($class === "CssAtMediaStartToken")
                {
                $r[] = $indent . "@media " . implode(", ", $token->MediaTypes) . " {";
                $level++;
                }
            elseif ($class === "CssAtPageStartToken")
                {
                $r[] = $indent . "@page {";
                $level++;
                }
            elseif ($class === "CssAtVariablesStartToken")
                {
                $r[] = $indent . "@variables " . implode(", ", $token->MediaTypes) . " {";
                $level++;
                }
            elseif ($class === "CssRulesetStartToken" || $class === "CssAtKeyframesRulesetStartToken")
                {
                $r[] = $indent . implode(", ", $token->Selectors) . " {";
                $level++;
                }
            elseif ($class == "CssAtFontFaceDeclarationToken"
                || $class === "CssAtKeyframesRulesetDeclarationToken"
                || $class === "CssAtPageDeclarationToken"
                || $class == "CssAtVariablesDeclarationToken"
                || $class === "CssRulesetDeclarationToken"
                )
                {
                $declaration = $indent . $token->Property . ": ";
                if ($this->padding)
                    {
                    $declaration = str_pad($declaration, $this->padding, " ", STR_PAD_RIGHT);
                    }
                $r[] = $declaration . $token->Value . ($token->IsImportant ? " !important" : "") . ";";
                }
            elseif ($class === "CssAtFontFaceEndToken"
                || $class === "CssAtMediaEndToken"
                || $class === "CssAtKeyframesEndToken"
                || $class === "CssAtKeyframesRulesetEndToken"
                || $class === "CssAtPageEndToken"
                || $class === "CssAtVariablesEndToken"
                || $class === "CssRulesetEndToken"
                )
                {
                $level--;
                $r[] = str_repeat($indent, $level) . "}";
                }
            }
        return implode("\n", $r);
        }
    }

/**
 * This {@link aCssToken CSS token} is a utility token that extends {@link aNullToken} and returns only a empty string.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssNullToken extends aCssToken
    {
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "";
        }
    }

/**
 * CSS Minifier.
 * 
 * @package     CssMin/Minifier
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssMinifier
    {
    /**
     * {@link aCssMinifierFilter Filters}.
     *  
     * @var array
     */
    private $filters = array();
    /**
     * {@link aCssMinifierPlugin Plugins}.
     * 
     * @var array
     */
    private $plugins = array();
    /**
     * Minified source.
     * 
     * @var string
     */
    private $minified = "";
    /**
     * Constructer.
     * 
     * Creates instances of {@link aCssMinifierFilter filters} and {@link aCssMinifierPlugin plugins}.
     * 
     * @param string $source CSS source [optional]
     * @param array $filters Filter configuration [optional]
     * @param array $plugins Plugin configuration [optional]
     * @return void
     */
    public function __construct($source = null, array $filters = null, array $plugins = null)
        {
        $filters = array_merge(array
            (
            "ImportImports"                 => false,
            "RemoveComments"                => true, 
            "RemoveEmptyRulesets"           => true,
            "RemoveEmptyAtBlocks"           => true,
            "ConvertLevel3Properties"       => false,
            "ConvertLevel3AtKeyframes"      => false,
            "Variables"                     => true,
            "RemoveLastDelarationSemiColon" => true
            ), is_array($filters) ? $filters : array());
        $plugins = array_merge(array
            (
            "Variables"                     => true,
            "ConvertFontWeight"             => false,
            "ConvertHslColors"              => false,
            "ConvertRgbColors"              => false,
            "ConvertNamedColors"            => false,
            "CompressColorValues"           => false,
            "CompressUnitValues"            => false,
            "CompressExpressionValues"      => false
            ), is_array($plugins) ? $plugins : array());
        // Filters
        foreach ($filters as $name => $config)
            {
            if ($config !== false)
                {
                $class  = "Css" . $name . "MinifierFilter";
                $config = is_array($config) ? $config : array();
                if (class_exists($class))
                    {
                    $this->filters[] = new $class($this, $config);
                    }
                else
                    {
                    CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The filter <code>" . $name . "</code> with the class name <code>" . $class . "</code> was not found"));
                    }
                }
            }
        // Plugins
        foreach ($plugins as $name => $config)
            {
            if ($config !== false)
                {
                $class  = "Css" . $name . "MinifierPlugin";
                $config = is_array($config) ? $config : array();
                if (class_exists($class))
                    {
                    $this->plugins[] = new $class($this, $config);
                    }
                else
                    {
                    CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin <code>" . $name . "</code> with the class name <code>" . $class . "</code> was not found"));
                    }
                }
            }
        // --
        if (!is_null($source))
            {
            $this->minify($source);
            }
        }
    /**
     * Returns the minified Source.
     * 
     * @return string
     */
    public function getMinified()
        {
        return $this->minified;
        }
    /**
     * Returns a plugin by class name.
     * 
     * @param string $name Class name of the plugin
     * @return aCssMinifierPlugin
     */
    public function getPlugin($class)
        {
        static $index = null;
        if (is_null($index))
            {
            $index = array();
            for ($i = 0, $l = count($this->plugins); $i < $l; $i++)
                {
                $index[get_class($this->plugins[$i])] = $i;
                }
            }
        return isset($index[$class]) ? $this->plugins[$index[$class]] : false;
        }
    /**
     * Minifies the CSS source.
     * 
     * @param string $source CSS source
     * @return string
     */
    public function minify($source)
        {
        // Variables
        $r                      = "";
        $parser                 = new CssParser($source);
        $tokens                 = $parser->getTokens();
        $filters                = $this->filters;
        $filterCount            = count($this->filters);
        $plugins                = $this->plugins;
        $pluginCount            = count($plugins);
        $pluginIndex            = array();
        $pluginTriggerTokens    = array();
        $globalTriggerTokens    = array();
        for ($i = 0, $l = count($plugins); $i < $l; $i++)
            {
            $tPluginClassName               = get_class($plugins[$i]);
            $pluginTriggerTokens[$i]        = $plugins[$i]->getTriggerTokens();
            foreach ($pluginTriggerTokens[$i] as $v)
                {
                if (!in_array($v, $globalTriggerTokens))
                    {
                    $globalTriggerTokens[] = $v;
                    }
                }
            $pluginTriggerTokens[$i] = "|" . implode("|", $pluginTriggerTokens[$i]) . "|";
            $pluginIndex[$tPluginClassName] = $i;
            }
        $globalTriggerTokens = "|" . implode("|", $globalTriggerTokens) . "|";
        /*
         * Apply filters
         */
        for($i = 0; $i < $filterCount; $i++)
            {
            // Apply the filter; if the return value is larger than 0...
            if ($filters[$i]->apply($tokens) > 0)
                {
                // ...then filter null values and rebuild the token array
                $tokens = array_values(array_filter($tokens));
                }
            }
        $tokenCount = count($tokens);
        /*
         * Apply plugins
         */
        for($i = 0; $i < $tokenCount; $i++)
            {
            $triggerToken = "|" . get_class($tokens[$i]) . "|";
            if (strpos($globalTriggerTokens, $triggerToken) !== false)
                {
                for($ii = 0; $ii < $pluginCount; $ii++)
                    {
                    if (strpos($pluginTriggerTokens[$ii], $triggerToken) !== false || $pluginTriggerTokens[$ii] === false)
                        {
                        // Apply the plugin; if the return value is TRUE continue to the next token
                        if ($plugins[$ii]->apply($tokens[$i]) === true)
                            {
                            continue 2;
                            }
                        }
                    }
                }
            }
        // Stringify the tokens
        for($i = 0; $i < $tokenCount; $i++)
            {
            $r .= (string) $tokens[$i];
            }
        $this->minified = $r;
        return $r;
        }
    }

/**
 * CssMin - A (simple) css minifier with benefits
 * 
 * --
 * Copyright (c) 2011 Joe Scylla <joe.scylla@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * --
 * 
 * @package     CssMin
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssMin
    {
    /**
     * Index of classes
     * 
     * @var array
     */
    private static $classIndex = array();
    /**
     * Parse/minify errors
     * 
     * @var array
     */
    private static $errors = array();
    /**
     * Verbose output.
     * 
     * @var boolean
     */
    private static $isVerbose = false;
    /**
     * {@link http://goo.gl/JrW54 Autoload} function of CssMin.
     * 
     * @param string $class Name of the class
     * @return void
     */
    public static function autoload($class)
        {
        if (isset(self::$classIndex[$class]))
            {
            require(self::$classIndex[$class]);
            }
        }
    /**
     * Return errors
     * 
     * @return array of {CssError}.
     */
    public static function getErrors()
        {
        return self::$errors;
        }
    /**
     * Returns if there were errors.
     * 
     * @return boolean
     */
    public static function hasErrors()
        {
        return count(self::$errors) > 0;
        }
    /**
     * Initialises CssMin.
     * 
     * @return void
     */
    public static function initialise()
        {
        // Create the class index for autoloading or including
        $paths = array(dirname(__FILE__));
        while (list($i, $path) = each($paths))
            {
            $subDirectorys = glob($path . "*", GLOB_MARK | GLOB_ONLYDIR | GLOB_NOSORT);
            if (is_array($subDirectorys))
                {
                foreach ($subDirectorys as $subDirectory)
                    {
                    $paths[] = $subDirectory;
                    }
                }
            $files = glob($path . "*.php", 0);
            if (is_array($files))
                {
                foreach ($files as $file)
                    {
                    $class = substr(basename($file), 0, -4);
                    self::$classIndex[$class] = $file;
                    }
                }
            }
        krsort(self::$classIndex);
        // Only use autoloading if spl_autoload_register() is available and no __autoload() is defined (because 
        // __autoload() breaks if spl_autoload_register() is used. 
        if (function_exists("spl_autoload_register") && !is_callable("__autoload"))
            {
            spl_autoload_register(array(__CLASS__, "autoload"));
            }
        // Otherwise include all class files
        else
            {
            foreach (self::$classIndex as $class => $file)
                {
                if (!class_exists($class))
                    {
                    require_once($file);
                    }
                }
            }
        }
    /**
     * Minifies CSS source.
     * 
     * @param string $source CSS source
     * @param array $filters Filter configuration [optional]
     * @param array $plugins Plugin configuration [optional]
     * @return string Minified CSS
     */
    public static function minify($source, array $filters = null, array $plugins = null)
        {
        self::$errors = array();
        $minifier = new CssMinifier($source, $filters, $plugins);
        return $minifier->getMinified();
        }
    /**
     * Parse the CSS source.
     * 
     * @param string $source CSS source
     * @param array $plugins Plugin configuration [optional]
     * @return array Array of aCssToken
     */
    public static function parse($source, array $plugins = null)
        {
        self::$errors = array();
        $parser = new CssParser($source, $plugins);
        return $parser->getTokens();
        }
    /**
     * --
     * 
     * @param boolean $to
     * @return boolean
     */
    public static function setVerbose($to)
        {
        self::$isVerbose = (boolean) $to;
        return self::$isVerbose;
        }
    /**
     * --
     * 
     * @param CssError $error
     * @return void
     */
    public static function triggerError(CssError $error)
        {
        self::$errors[] = $error;
        if (self::$isVerbose)
            {
            trigger_error((string) $error, E_USER_WARNING);
            }
        }
    }
// Initialises CssMin
CssMin::initialise();

/**
 * This {@link aCssMinifierFilter minifier filter} import external css files defined with the @import at-rule into the 
 * current stylesheet. 
 * 
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssImportImportsMinifierFilter extends aCssMinifierFilter
    {
    /**
     * Array with already imported external stylesheets.
     * 
     * @var array
     */
    private $imported = array();
    /**
     * Implements {@link aCssMinifierFilter::filter()}.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
     */
    public function apply(array &$tokens)
        {
        if (!isset($this->configuration["BasePath"]) || !is_dir($this->configuration["BasePath"]))
            {
            CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Base path <code>" . ($this->configuration["BasePath"] ? $this->configuration["BasePath"] : "null"). "</code> is not a directory"));
            return 0;
            }
        for ($i = 0, $l = count($tokens); $i < $l; $i++)
            {
            if (get_class($tokens[$i]) === "CssAtImportToken")
                {
                $import = $this->configuration["BasePath"] . "/" . $tokens[$i]->Import;
                // Import file was not found/is not a file
                if (!is_file($import))
                    {
                    CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Import file <code>" . $import. "</code> was not found.", (string) $tokens[$i]));
                    }
                // Import file already imported; remove this @import at-rule to prevent recursions
                elseif (in_array($import, $this->imported))
                    {
                    CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Import file <code>" . $import. "</code> was already imported.", (string) $tokens[$i]));
                    $tokens[$i] = null;
                    }
                else
                    {
                    $this->imported[] = $import;
                    $parser = new CssParser(file_get_contents($import));
                    $import = $parser->getTokens();
                    // The @import at-rule has media types defined requiring special handling
                    if (count($tokens[$i]->MediaTypes) > 0 && !(count($tokens[$i]->MediaTypes) == 1 && $tokens[$i]->MediaTypes[0] == "all"))
                        {
                        $blocks = array();
                        /*
                         * Filter or set media types of @import at-rule or remove the @import at-rule if no media type is matching the parent @import at-rule
                         */
                        for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
                            {
                            if (get_class($import[$ii]) === "CssAtImportToken")
                                {
                                // @import at-rule defines no media type or only the "all" media type; set the media types to the one defined in the parent @import at-rule
                                if (count($import[$ii]->MediaTypes) == 0 || (count($import[$ii]->MediaTypes) == 1 && $import[$ii]->MediaTypes[0] == "all"))
                                    {
                                    $import[$ii]->MediaTypes = $tokens[$i]->MediaTypes;
                                    }
                                // @import at-rule defineds one or more media types; filter out media types not matching with the  parent @import at-rule
                                elseif (count($import[$ii]->MediaTypes > 0))
                                    {
                                    foreach ($import[$ii]->MediaTypes as $index => $mediaType)
                                        {
                                        if (!in_array($mediaType, $tokens[$i]->MediaTypes))
                                            {
                                            unset($import[$ii]->MediaTypes[$index]);
                                            }
                                        }
                                    $import[$ii]->MediaTypes = array_values($import[$ii]->MediaTypes);
                                    // If there are no media types left in the @import at-rule remove the @import at-rule
                                    if (count($import[$ii]->MediaTypes) == 0)
                                        {
                                        $import[$ii] = null;
                                        }
                                    }
                                }
                            }
                        /*
                         * Remove media types of @media at-rule block not defined in the @import at-rule
                         */
                        for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
                            {
                            if (get_class($import[$ii]) === "CssAtMediaStartToken")
                                {
                                foreach ($import[$ii]->MediaTypes as $index => $mediaType)
                                    {
                                    if (!in_array($mediaType, $tokens[$i]->MediaTypes))
                                        {
                                        unset($import[$ii]->MediaTypes[$index]);
                                        }
                                    $import[$ii]->MediaTypes = array_values($import[$ii]->MediaTypes);
                                    }
                                }
                            }
                        /*
                         * If no media types left of the @media at-rule block remove the complete block
                         */
                        for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
                            {
                            if (get_class($import[$ii]) === "CssAtMediaStartToken")
                                {
                                if (count($import[$ii]->MediaTypes) === 0)
                                    {
                                    for ($iii = $ii; $iii < $ll; $iii++)
                                        {
                                        if (get_class($import[$iii]) === "CssAtMediaEndToken")
                                            {
                                            break;
                                            }
                                        }
                                    if (get_class($import[$iii]) === "CssAtMediaEndToken")
                                        {
                                        array_splice($import, $ii, $iii - $ii + 1, array());
                                        $ll = count($import);
                                        }
                                    }
                                }
                            }
                        /*
                         * If the media types of the @media at-rule equals the media types defined in the @import 
                         * at-rule remove the CssAtMediaStartToken and CssAtMediaEndToken token
                         */ 
                        for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
                            {
                            if (get_class($import[$ii]) === "CssAtMediaStartToken" && count(array_diff($tokens[$i]->MediaTypes, $import[$ii]->MediaTypes)) === 0)
                                {
                                for ($iii = $ii; $iii < $ll; $iii++)
                                    {
                                    if (get_class($import[$iii]) == "CssAtMediaEndToken")
                                        {
                                        break;
                                        }
                                    }
                                if (get_class($import[$iii]) == "CssAtMediaEndToken")
                                    {
                                    unset($import[$ii]);
                                    unset($import[$iii]);
                                    $import = array_values($import);
                                    $ll = count($import);
                                    }
                                }
                            }
                        /**
                         * Extract CssAtImportToken and CssAtCharsetToken tokens
                         */
                        for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
                            {
                            $class = get_class($import[$ii]);
                            if ($class === "CssAtImportToken" || $class === "CssAtCharsetToken")
                                {
                                $blocks = array_merge($blocks, array_splice($import, $ii, 1, array()));
                                $ll = count($import);
                                }
                            }
                        /*
                         * Extract the @font-face, @media and @page at-rule block
                         */
                        for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
                            {
                            $class = get_class($import[$ii]);
                            if ($class === "CssAtFontFaceStartToken" || $class === "CssAtMediaStartToken" || $class === "CssAtPageStartToken" || $class === "CssAtVariablesStartToken")
                                {
                                for ($iii = $ii; $iii < $ll; $iii++)
                                    {
                                    $class = get_class($import[$iii]);
                                    if ($class === "CssAtFontFaceEndToken" || $class === "CssAtMediaEndToken" || $class === "CssAtPageEndToken" || $class === "CssAtVariablesEndToken")
                                        {
                                        break;
                                        }
                                    }
                                $class = get_class($import[$iii]);
                                if (isset($import[$iii]) && ($class === "CssAtFontFaceEndToken" || $class === "CssAtMediaEndToken" || $class === "CssAtPageEndToken" || $class === "CssAtVariablesEndToken"))
                                    {
                                    $blocks = array_merge($blocks, array_splice($import, $ii, $iii - $ii + 1, array()));
                                    $ll = count($import);
                                    }
                                }
                            }
                        // Create the import array with extracted tokens and the rulesets wrapped into a @media at-rule block
                        $import = array_merge($blocks, array(new CssAtMediaStartToken($tokens[$i]->MediaTypes)), $import, array(new CssAtMediaEndToken()));
                        }
                    // Insert the imported tokens
                    array_splice($tokens, $i, 1, $import);
                    // Modify parameters of the for-loop
                    $i--;
                    $l = count($tokens);
                    }
                }
            }
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for preserve parsing expression() declaration values.
 * 
 * This plugin return no {@link aCssToken CssToken} but ensures that expression() declaration values will get parsed 
 * properly.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssExpressionParserPlugin extends aCssParserPlugin
    {
    /**
     * Count of left braces.
     * 
     * @var integer
     */
    private $leftBraces = 0;
    /**
     * Count of right braces.
     * 
     * @var integer
     */
    private $rightBraces = 0;
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("(", ")", ";", "}");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return false;
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        // Start of expression
        if ($char === "(" && strtolower(substr($this->parser->getSource(), $index - 10, 11)) === "expression(" && $state !== "T_EXPRESSION")
            {
            $this->parser->pushState("T_EXPRESSION");
            $this->leftBraces++;
            }
        // Count left braces
        elseif ($char === "(" && $state === "T_EXPRESSION")
            {
            $this->leftBraces++;
            }
        // Count right braces
        elseif ($char === ")" && $state === "T_EXPRESSION")
            {
            $this->rightBraces++;
            }
        // Possible end of expression; if left and right braces are equal the expressen ends
        elseif (($char === ";" || $char === "}") && $state === "T_EXPRESSION" && $this->leftBraces === $this->rightBraces)
            {
            $this->leftBraces = $this->rightBraces = 0;
            $this->parser->popState();
            return $index - 1;
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * CSS Error.
 * 
 * @package     CssMin
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssError
    {
    /**
     * File.
     * 
     * @var string
     */
    public $File = "";
    /**
     * Line.
     * 
     * @var integer
     */
    public $Line = 0;
    /**
     * Error message.
     * 
     * @var string
     */
    public $Message = "";
    /**
     * Source.
     * 
     * @var string
     */
    public $Source = "";
    /**
     * Constructor triggering the error.
     * 
     * @param string $message Error message
     * @param string $source Corresponding line [optional]
     * @return void
     */
    public function __construct($file, $line, $message, $source = "")
        {
        $this->File     = $file;
        $this->Line     = $line;
        $this->Message  = $message;
        $this->Source   = $source;
        }
    /**
     * Returns the error as formatted string.
     * 
     * @return string
     */ 
    public function __toString()
        {
        return $this->Message . ($this->Source ? ": <br /><code>" . $this->Source . "</code>": "") . "<br />in file " . $this->File . " at line " . $this->Line;
        }
    }

/**
 * This {@link aCssMinifierPlugin} will convert a color value in rgb notation to hexadecimal notation.
 * 
 * Example:
 * <code>
 * color: rgb(200,60%,5);
 * </code>
 * 
 * Will get converted to:
 * <code>
 * color:#c89905;
 * </code>
 *
 * @package     CssMin/Minifier/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssConvertRgbColorsMinifierPlugin extends aCssMinifierPlugin
    {
    /**
     * Regular expression matching the value.
     * 
     * @var string
     */
    private $reMatch = "/rgb\s*\(\s*([0-9%]+)\s*,\s*([0-9%]+)\s*,\s*([0-9%]+)\s*\)/iS";
    /**
     * Implements {@link aCssMinifierPlugin::minify()}.
     * 
     * @param aCssToken $token Token to process
     * @return boolean Return TRUE to break the processing of this token; FALSE to continue
     */
    public function apply(aCssToken &$token)
        {
        if (stripos($token->Value, "rgb") !== false && preg_match($this->reMatch, $token->Value, $m))
            {
            for ($i = 1, $l = count($m); $i < $l; $i++)
                {
                if (strpos("%", $m[$i]) !== false)
                    {
                    $m[$i] = substr($m[$i], 0, -1);
                    $m[$i] = (int) (256 * ($m[$i] / 100));
                    }
                $m[$i] = str_pad(dechex($m[$i]),  2, "0", STR_PAD_LEFT);
                }
            $token->Value = str_replace($m[0], "#" . $m[1] . $m[2] . $m[3], $token->Value);
            }
        return false;
        }
    /**
     * Implements {@link aMinifierPlugin::getTriggerTokens()}
     * 
     * @return array
     */
    public function getTriggerTokens()
        {
        return array
            (
            "CssAtFontFaceDeclarationToken",
            "CssAtPageDeclarationToken",
            "CssRulesetDeclarationToken"
            );
        }
    }

/**
 * This {@link aCssMinifierPlugin} will convert named color values to hexadecimal notation.
 * 
 * Example:
 * <code>
 * color: black;
 * border: 1px solid indigo;
 * </code>
 * 
 * Will get converted to:
 * <code>
 * color:#000;
 * border:1px solid #4b0082;
 * </code>
 * 
 * @package     CssMin/Minifier/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssConvertNamedColorsMinifierPlugin extends aCssMinifierPlugin
    {
    
    /**
     * Regular expression matching the value.
     * 
     * @var string
     */
    private $reMatch = null;
    /**
     * Regular expression replacing the value.
     * 
     * @var string
     */
    private $reReplace = "\"\${1}\" . \$this->transformation[strtolower(\"\${2}\")] . \"\${3}\"";
    /**
     * Transformation table used by the {@link CssConvertNamedColorsMinifierPlugin::$reReplace replace regular expression}.
     * 
     * @var array
     */
    private $transformation = array
        ( 
        "aliceblue"                     => "#f0f8ff",
        "antiquewhite"                  => "#faebd7",
        "aqua"                          => "#0ff",
        "aquamarine"                    => "#7fffd4",
        "azure"                         => "#f0ffff",
        "beige"                         => "#f5f5dc",
        "black"                         => "#000",
        "blue"                          => "#00f",
        "blueviolet"                    => "#8a2be2",
        "brown"                         => "#a52a2a",
        "burlywood"                     => "#deb887",
        "cadetblue"                     => "#5f9ea0",
        "chartreuse"                    => "#7fff00",
        "chocolate"                     => "#d2691e",
        "coral"                         => "#ff7f50",
        "cornflowerblue"                => "#6495ed",
        "cornsilk"                      => "#fff8dc",
        "crimson"                       => "#dc143c",
        "darkblue"                      => "#00008b",
        "darkcyan"                      => "#008b8b",
        "darkgoldenrod"                 => "#b8860b",
        "darkgray"                      => "#a9a9a9",
        "darkgreen"                     => "#006400",
        "darkkhaki"                     => "#bdb76b",
        "darkmagenta"                   => "#8b008b",
        "darkolivegreen"                => "#556b2f",
        "darkorange"                    => "#ff8c00",
        "darkorchid"                    => "#9932cc",
        "darkred"                       => "#8b0000",
        "darksalmon"                    => "#e9967a",
        "darkseagreen"                  => "#8fbc8f",
        "darkslateblue"                 => "#483d8b",
        "darkslategray"                 => "#2f4f4f",
        "darkturquoise"                 => "#00ced1",
        "darkviolet"                    => "#9400d3",
        "deeppink"                      => "#ff1493",
        "deepskyblue"                   => "#00bfff",
        "dimgray"                       => "#696969",
        "dodgerblue"                    => "#1e90ff",
        "firebrick"                     => "#b22222",
        "floralwhite"                   => "#fffaf0",
        "forestgreen"                   => "#228b22",
        "fuchsia"                       => "#f0f",
        "gainsboro"                     => "#dcdcdc",
        "ghostwhite"                    => "#f8f8ff",
        "gold"                          => "#ffd700",
        "goldenrod"                     => "#daa520",
        "gray"                          => "#808080",
        "green"                         => "#008000",
        "greenyellow"                   => "#adff2f",
        "honeydew"                      => "#f0fff0",
        "hotpink"                       => "#ff69b4",
        "indianred"                     => "#cd5c5c",
        "indigo"                        => "#4b0082",
        "ivory"                         => "#fffff0",
        "khaki"                         => "#f0e68c",
        "lavender"                      => "#e6e6fa",
        "lavenderblush"                 => "#fff0f5",
        "lawngreen"                     => "#7cfc00",
        "lemonchiffon"                  => "#fffacd",
        "lightblue"                     => "#add8e6",
        "lightcoral"                    => "#f08080",
        "lightcyan"                     => "#e0ffff",
        "lightgoldenrodyellow"          => "#fafad2",
        "lightgreen"                    => "#90ee90",
        "lightgrey"                     => "#d3d3d3",
        "lightpink"                     => "#ffb6c1",
        "lightsalmon"                   => "#ffa07a",
        "lightseagreen"                 => "#20b2aa",
        "lightskyblue"                  => "#87cefa",
        "lightslategray"                => "#789",
        "lightsteelblue"                => "#b0c4de",
        "lightyellow"                   => "#ffffe0",
        "lime"                          => "#0f0",
        "limegreen"                     => "#32cd32",
        "linen"                         => "#faf0e6",
        "maroon"                        => "#800000",
        "mediumaquamarine"              => "#66cdaa",
        "mediumblue"                    => "#0000cd",
        "mediumorchid"                  => "#ba55d3",
        "mediumpurple"                  => "#9370db",
        "mediumseagreen"                => "#3cb371",
        "mediumslateblue"               => "#7b68ee",
        "mediumspringgreen"             => "#00fa9a",
        "mediumturquoise"               => "#48d1cc",
        "mediumvioletred"               => "#c71585",
        "midnightblue"                  => "#191970",
        "mintcream"                     => "#f5fffa",
        "mistyrose"                     => "#ffe4e1",
        "moccasin"                      => "#ffe4b5",
        "navajowhite"                   => "#ffdead",
        "navy"                          => "#000080",
        "oldlace"                       => "#fdf5e6",
        "olive"                         => "#808000",
        "olivedrab"                     => "#6b8e23",
        "orange"                        => "#ffa500",
        "orangered"                     => "#ff4500",
        "orchid"                        => "#da70d6",
        "palegoldenrod"                 => "#eee8aa",
        "palegreen"                     => "#98fb98",
        "paleturquoise"                 => "#afeeee",
        "palevioletred"                 => "#db7093",
        "papayawhip"                    => "#ffefd5",
        "peachpuff"                     => "#ffdab9",
        "peru"                          => "#cd853f",
        "pink"                          => "#ffc0cb",
        "plum"                          => "#dda0dd",
        "powderblue"                    => "#b0e0e6",
        "purple"                        => "#800080",
        "red"                           => "#f00",
        "rosybrown"                     => "#bc8f8f",
        "royalblue"                     => "#4169e1",
        "saddlebrown"                   => "#8b4513",
        "salmon"                        => "#fa8072",
        "sandybrown"                    => "#f4a460",
        "seagreen"                      => "#2e8b57",
        "seashell"                      => "#fff5ee",
        "sienna"                        => "#a0522d",
        "silver"                        => "#c0c0c0",
        "skyblue"                       => "#87ceeb",
        "slateblue"                     => "#6a5acd",
        "slategray"                     => "#708090",
        "snow"                          => "#fffafa",
        "springgreen"                   => "#00ff7f",
        "steelblue"                     => "#4682b4",
        "tan"                           => "#d2b48c",
        "teal"                          => "#008080",
        "thistle"                       => "#d8bfd8",
        "tomato"                        => "#ff6347",
        "turquoise"                     => "#40e0d0",
        "violet"                        => "#ee82ee",
        "wheat"                         => "#f5deb3",
        "white"                         => "#fff",
        "whitesmoke"                    => "#f5f5f5",
        "yellow"                        => "#ff0",
        "yellowgreen"                   => "#9acd32"
        );
    /**
     * Overwrites {@link aCssMinifierPlugin::__construct()}.
     * 
     * The constructor will create the {@link CssConvertNamedColorsMinifierPlugin::$reReplace replace regular expression}
     * based on the {@link CssConvertNamedColorsMinifierPlugin::$transformation transformation table}.
     * 
     * @param CssMinifier $minifier The CssMinifier object of this plugin.
     * @param array $configuration Plugin configuration [optional]
     * @return void
     */
    public function __construct(CssMinifier $minifier, array $configuration = array())
        {
        $this->reMatch = "/(^|\s)+(" . implode("|", array_keys($this->transformation)) . ")(\s|$)+/eiS";
        parent::__construct($minifier, $configuration);
        }
    /**
     * Implements {@link aCssMinifierPlugin::minify()}.
     * 
     * @param aCssToken $token Token to process
     * @return boolean Return TRUE to break the processing of this token; FALSE to continue
     */
    public function apply(aCssToken &$token)
        {
        $lcValue = strtolower($token->Value);
        // Declaration value equals a value in the transformation table => simple replace
        if (isset($this->transformation[$lcValue]))
            {
            $token->Value = $this->transformation[$lcValue];
            }
        // Declaration value contains a value in the transformation table => regular expression replace
        elseif (preg_match($this->reMatch, $token->Value))
            {
            $token->Value = preg_replace($this->reMatch, $this->reReplace, $token->Value);
            }
        return false;
        }
    /**
     * Implements {@link aMinifierPlugin::getTriggerTokens()}
     * 
     * @return array
     */
    public function getTriggerTokens()
        {
        return array
            (
            "CssAtFontFaceDeclarationToken",
            "CssAtPageDeclarationToken",
            "CssRulesetDeclarationToken"
            );
        }
    }

/**
 * This {@link aCssMinifierFilter minifier filter} triggers on CSS Level 3 properties and will add declaration tokens
 * with browser-specific properties.
 * 
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssConvertLevel3PropertiesMinifierFilter extends aCssMinifierFilter
    {
    /**
     * Css property transformations table. Used to convert CSS3 and proprietary properties to the browser-specific 
     * counterparts.
     * 
     * @var array
     */
    private $transformations = array
        (
        // Property                     Array(Mozilla, Webkit, Opera, Internet Explorer); NULL values are placeholders and will get ignored
        "animation"                     => array(null, "-webkit-animation", null, null),
        "animation-delay"               => array(null, "-webkit-animation-delay", null, null),
        "animation-direction"           => array(null, "-webkit-animation-direction", null, null),
        "animation-duration"            => array(null, "-webkit-animation-duration", null, null),
        "animation-fill-mode"           => array(null, "-webkit-animation-fill-mode", null, null),
        "animation-iteration-count"     => array(null, "-webkit-animation-iteration-count", null, null),
        "animation-name"                => array(null, "-webkit-animation-name", null, null),
        "animation-play-state"          => array(null, "-webkit-animation-play-state", null, null),
        "animation-timing-function"     => array(null, "-webkit-animation-timing-function", null, null),
        "appearance"                    => array("-moz-appearance", "-webkit-appearance", null, null),
        "backface-visibility"           => array(null, "-webkit-backface-visibility", null, null),
        "background-clip"               => array(null, "-webkit-background-clip", null, null),
        "background-composite"          => array(null, "-webkit-background-composite", null, null),
        "background-inline-policy"      => array("-moz-background-inline-policy", null, null, null),
        "background-origin"             => array(null, "-webkit-background-origin", null, null),
        "background-position-x"         => array(null, null, null, "-ms-background-position-x"),
        "background-position-y"         => array(null, null, null, "-ms-background-position-y"),
        "background-size"               => array(null, "-webkit-background-size", null, null),
        "behavior"                      => array(null, null, null, "-ms-behavior"),
        "binding"                       => array("-moz-binding", null, null, null),
        "border-after"                  => array(null, "-webkit-border-after", null, null),
        "border-after-color"            => array(null, "-webkit-border-after-color", null, null),
        "border-after-style"            => array(null, "-webkit-border-after-style", null, null),
        "border-after-width"            => array(null, "-webkit-border-after-width", null, null),
        "border-before"                 => array(null, "-webkit-border-before", null, null),
        "border-before-color"           => array(null, "-webkit-border-before-color", null, null),
        "border-before-style"           => array(null, "-webkit-border-before-style", null, null),
        "border-before-width"           => array(null, "-webkit-border-before-width", null, null),
        "border-border-bottom-colors"   => array("-moz-border-bottom-colors", null, null, null),
        "border-bottom-left-radius"     => array("-moz-border-radius-bottomleft", "-webkit-border-bottom-left-radius", null, null),
        "border-bottom-right-radius"    => array("-moz-border-radius-bottomright", "-webkit-border-bottom-right-radius", null, null),
        "border-end"                    => array("-moz-border-end", "-webkit-border-end", null, null),
        "border-end-color"              => array("-moz-border-end-color", "-webkit-border-end-color", null, null),
        "border-end-style"              => array("-moz-border-end-style", "-webkit-border-end-style", null, null),
        "border-end-width"              => array("-moz-border-end-width", "-webkit-border-end-width", null, null),
        "border-fit"                    => array(null, "-webkit-border-fit", null, null),
        "border-horizontal-spacing"     => array(null, "-webkit-border-horizontal-spacing", null, null),
        "border-image"                  => array("-moz-border-image", "-webkit-border-image", null, null),
        "border-left-colors"            => array("-moz-border-left-colors", null, null, null),
        "border-radius"                 => array("-moz-border-radius", "-webkit-border-radius", null, null),
        "border-border-right-colors"    => array("-moz-border-right-colors", null, null, null),
        "border-start"                  => array("-moz-border-start", "-webkit-border-start", null, null),
        "border-start-color"            => array("-moz-border-start-color", "-webkit-border-start-color", null, null),
        "border-start-style"            => array("-moz-border-start-style", "-webkit-border-start-style", null, null),
        "border-start-width"            => array("-moz-border-start-width", "-webkit-border-start-width", null, null),
        "border-top-colors"             => array("-moz-border-top-colors", null, null, null),
        "border-top-left-radius"        => array("-moz-border-radius-topleft", "-webkit-border-top-left-radius", null, null),
        "border-top-right-radius"       => array("-moz-border-radius-topright", "-webkit-border-top-right-radius", null, null),
        "border-vertical-spacing"       => array(null, "-webkit-border-vertical-spacing", null, null),
        "box-align"                     => array("-moz-box-align", "-webkit-box-align", null, null),
        "box-direction"                 => array("-moz-box-direction", "-webkit-box-direction", null, null),
        "box-flex"                      => array("-moz-box-flex", "-webkit-box-flex", null, null),
        "box-flex-group"                => array(null, "-webkit-box-flex-group", null, null),
        "box-flex-lines"                => array(null, "-webkit-box-flex-lines", null, null),
        "box-ordinal-group"             => array("-moz-box-ordinal-group", "-webkit-box-ordinal-group", null, null),
        "box-orient"                    => array("-moz-box-orient", "-webkit-box-orient", null, null),
        "box-pack"                      => array("-moz-box-pack", "-webkit-box-pack", null, null),
        "box-reflect"                   => array(null, "-webkit-box-reflect", null, null),
        "box-shadow"                    => array("-moz-box-shadow", "-webkit-box-shadow", null, null),
        "box-sizing"                    => array("-moz-box-sizing", null, null, null),
        "color-correction"              => array(null, "-webkit-color-correction", null, null),
        "column-break-after"            => array(null, "-webkit-column-break-after", null, null),
        "column-break-before"           => array(null, "-webkit-column-break-before", null, null),
        "column-break-inside"           => array(null, "-webkit-column-break-inside", null, null),
        "column-count"                  => array("-moz-column-count", "-webkit-column-count", null, null),
        "column-gap"                    => array("-moz-column-gap", "-webkit-column-gap", null, null),
        "column-rule"                   => array("-moz-column-rule", "-webkit-column-rule", null, null),
        "column-rule-color"             => array("-moz-column-rule-color", "-webkit-column-rule-color", null, null),
        "column-rule-style"             => array("-moz-column-rule-style", "-webkit-column-rule-style", null, null),
        "column-rule-width"             => array("-moz-column-rule-width", "-webkit-column-rule-width", null, null),
        "column-span"                   => array(null, "-webkit-column-span", null, null),
        "column-width"                  => array("-moz-column-width", "-webkit-column-width", null, null),
        "columns"                       => array(null, "-webkit-columns", null, null),
        "filter"                        => array(__CLASS__, "filter"),
        "float-edge"                    => array("-moz-float-edge", null, null, null),
        "font-feature-settings"         => array("-moz-font-feature-settings", null, null, null),
        "font-language-override"        => array("-moz-font-language-override", null, null, null),
        "font-size-delta"               => array(null, "-webkit-font-size-delta", null, null),
        "font-smoothing"                => array(null, "-webkit-font-smoothing", null, null),
        "force-broken-image-icon"       => array("-moz-force-broken-image-icon", null, null, null),
        "highlight"                     => array(null, "-webkit-highlight", null, null),
        "hyphenate-character"           => array(null, "-webkit-hyphenate-character", null, null),
        "hyphenate-locale"              => array(null, "-webkit-hyphenate-locale", null, null),
        "hyphens"                       => array(null, "-webkit-hyphens", null, null),
        "force-broken-image-icon"       => array("-moz-image-region", null, null, null),
        "ime-mode"                      => array(null, null, null, "-ms-ime-mode"),
        "interpolation-mode"            => array(null, null, null, "-ms-interpolation-mode"),
        "layout-flow"                   => array(null, null, null, "-ms-layout-flow"),
        "layout-grid"                   => array(null, null, null, "-ms-layout-grid"),
        "layout-grid-char"              => array(null, null, null, "-ms-layout-grid-char"),
        "layout-grid-line"              => array(null, null, null, "-ms-layout-grid-line"),
        "layout-grid-mode"              => array(null, null, null, "-ms-layout-grid-mode"),
        "layout-grid-type"              => array(null, null, null, "-ms-layout-grid-type"),
        "line-break"                    => array(null, "-webkit-line-break", null, "-ms-line-break"),
        "line-clamp"                    => array(null, "-webkit-line-clamp", null, null),
        "line-grid-mode"                => array(null, null, null, "-ms-line-grid-mode"),
        "logical-height"                => array(null, "-webkit-logical-height", null, null),
        "logical-width"                 => array(null, "-webkit-logical-width", null, null),
        "margin-after"                  => array(null, "-webkit-margin-after", null, null),
        "margin-after-collapse"         => array(null, "-webkit-margin-after-collapse", null, null),
        "margin-before"                 => array(null, "-webkit-margin-before", null, null),
        "margin-before-collapse"        => array(null, "-webkit-margin-before-collapse", null, null),
        "margin-bottom-collapse"        => array(null, "-webkit-margin-bottom-collapse", null, null),
        "margin-collapse"               => array(null, "-webkit-margin-collapse", null, null),
        "margin-end"                    => array("-moz-margin-end", "-webkit-margin-end", null, null),
        "margin-start"                  => array("-moz-margin-start", "-webkit-margin-start", null, null),
        "margin-top-collapse"           => array(null, "-webkit-margin-top-collapse", null, null),
        "marquee "                      => array(null, "-webkit-marquee", null, null),
        "marquee-direction"             => array(null, "-webkit-marquee-direction", null, null),
        "marquee-increment"             => array(null, "-webkit-marquee-increment", null, null),
        "marquee-repetition"            => array(null, "-webkit-marquee-repetition", null, null),
        "marquee-speed"                 => array(null, "-webkit-marquee-speed", null, null),
        "marquee-style"                 => array(null, "-webkit-marquee-style", null, null),
        "mask"                          => array(null, "-webkit-mask", null, null),
        "mask-attachment"               => array(null, "-webkit-mask-attachment", null, null),
        "mask-box-image"                => array(null, "-webkit-mask-box-image", null, null),
        "mask-clip"                     => array(null, "-webkit-mask-clip", null, null),
        "mask-composite"                => array(null, "-webkit-mask-composite", null, null),
        "mask-image"                    => array(null, "-webkit-mask-image", null, null),
        "mask-origin"                   => array(null, "-webkit-mask-origin", null, null),
        "mask-position"                 => array(null, "-webkit-mask-position", null, null),
        "mask-position-x"               => array(null, "-webkit-mask-position-x", null, null),
        "mask-position-y"               => array(null, "-webkit-mask-position-y", null, null),
        "mask-repeat"                   => array(null, "-webkit-mask-repeat", null, null),
        "mask-repeat-x"                 => array(null, "-webkit-mask-repeat-x", null, null),
        "mask-repeat-y"                 => array(null, "-webkit-mask-repeat-y", null, null),
        "mask-size"                     => array(null, "-webkit-mask-size", null, null),
        "match-nearest-mail-blockquote-color" => array(null, "-webkit-match-nearest-mail-blockquote-color", null, null),
        "max-logical-height"            => array(null, "-webkit-max-logical-height", null, null),
        "max-logical-width"             => array(null, "-webkit-max-logical-width", null, null),
        "min-logical-height"            => array(null, "-webkit-min-logical-height", null, null),
        "min-logical-width"             => array(null, "-webkit-min-logical-width", null, null),
        "object-fit"                    => array(null, null, "-o-object-fit", null),
        "object-position"               => array(null, null, "-o-object-position", null),
        "opacity"                       => array(__CLASS__, "opacity"),
        "outline-radius"                => array("-moz-outline-radius", null, null, null),
        "outline-bottom-left-radius"    => array("-moz-outline-radius-bottomleft", null, null, null),
        "outline-bottom-right-radius"   => array("-moz-outline-radius-bottomright", null, null, null),
        "outline-top-left-radius"       => array("-moz-outline-radius-topleft", null, null, null),
        "outline-top-right-radius"      => array("-moz-outline-radius-topright", null, null, null),
        "padding-after"                 => array(null, "-webkit-padding-after", null, null),
        "padding-before"                => array(null, "-webkit-padding-before", null, null),
        "padding-end"                   => array("-moz-padding-end", "-webkit-padding-end", null, null),
        "padding-start"                 => array("-moz-padding-start", "-webkit-padding-start", null, null),
        "perspective"                   => array(null, "-webkit-perspective", null, null),
        "perspective-origin"            => array(null, "-webkit-perspective-origin", null, null),
        "perspective-origin-x"          => array(null, "-webkit-perspective-origin-x", null, null),
        "perspective-origin-y"          => array(null, "-webkit-perspective-origin-y", null, null),
        "rtl-ordering"                  => array(null, "-webkit-rtl-ordering", null, null),
        "scrollbar-3dlight-color"       => array(null, null, null, "-ms-scrollbar-3dlight-color"),
        "scrollbar-arrow-color"         => array(null, null, null, "-ms-scrollbar-arrow-color"),
        "scrollbar-base-color"          => array(null, null, null, "-ms-scrollbar-base-color"),
        "scrollbar-darkshadow-color"    => array(null, null, null, "-ms-scrollbar-darkshadow-color"),
        "scrollbar-face-color"          => array(null, null, null, "-ms-scrollbar-face-color"),
        "scrollbar-highlight-color"     => array(null, null, null, "-ms-scrollbar-highlight-color"),
        "scrollbar-shadow-color"        => array(null, null, null, "-ms-scrollbar-shadow-color"),
        "scrollbar-track-color"         => array(null, null, null, "-ms-scrollbar-track-color"),
        "stack-sizing"                  => array("-moz-stack-sizing", null, null, null),
        "svg-shadow"                    => array(null, "-webkit-svg-shadow", null, null),
        "tab-size"                      => array("-moz-tab-size", null, "-o-tab-size", null),
        "table-baseline"                => array(null, null, "-o-table-baseline", null),
        "text-align-last"               => array(null, null, null, "-ms-text-align-last"),
        "text-autospace"                => array(null, null, null, "-ms-text-autospace"),
        "text-combine"                  => array(null, "-webkit-text-combine", null, null),
        "text-decorations-in-effect"    => array(null, "-webkit-text-decorations-in-effect", null, null),
        "text-emphasis"                 => array(null, "-webkit-text-emphasis", null, null),
        "text-emphasis-color"           => array(null, "-webkit-text-emphasis-color", null, null),
        "text-emphasis-position"        => array(null, "-webkit-text-emphasis-position", null, null),
        "text-emphasis-style"           => array(null, "-webkit-text-emphasis-style", null, null),
        "text-fill-color"               => array(null, "-webkit-text-fill-color", null, null),
        "text-justify"                  => array(null, null, null, "-ms-text-justify"),
        "text-kashida-space"            => array(null, null, null, "-ms-text-kashida-space"),
        "text-overflow"                 => array(null, null, "-o-text-overflow", "-ms-text-overflow"),
        "text-security"                 => array(null, "-webkit-text-security", null, null),
        "text-size-adjust"              => array(null, "-webkit-text-size-adjust", null, "-ms-text-size-adjust"),
        "text-stroke"                   => array(null, "-webkit-text-stroke", null, null),
        "text-stroke-color"             => array(null, "-webkit-text-stroke-color", null, null),
        "text-stroke-width"             => array(null, "-webkit-text-stroke-width", null, null),
        "text-underline-position"       => array(null, null, null, "-ms-text-underline-position"),
        "transform"                     => array("-moz-transform", "-webkit-transform", "-o-transform", null),
        "transform-origin"              => array("-moz-transform-origin", "-webkit-transform-origin", "-o-transform-origin", null),
        "transform-origin-x"            => array(null, "-webkit-transform-origin-x", null, null),
        "transform-origin-y"            => array(null, "-webkit-transform-origin-y", null, null),
        "transform-origin-z"            => array(null, "-webkit-transform-origin-z", null, null),
        "transform-style"               => array(null, "-webkit-transform-style", null, null),
        "transition"                    => array("-moz-transition", "-webkit-transition", "-o-transition", null),
        "transition-delay"              => array("-moz-transition-delay", "-webkit-transition-delay", "-o-transition-delay", null),
        "transition-duration"           => array("-moz-transition-duration", "-webkit-transition-duration", "-o-transition-duration", null),
        "transition-property"           => array("-moz-transition-property", "-webkit-transition-property", "-o-transition-property", null),
        "transition-timing-function"    => array("-moz-transition-timing-function", "-webkit-transition-timing-function", "-o-transition-timing-function", null),
        "user-drag"                     => array(null, "-webkit-user-drag", null, null),
        "user-focus"                    => array("-moz-user-focus", null, null, null),
        "user-input"                    => array("-moz-user-input", null, null, null),
        "user-modify"                   => array("-moz-user-modify", "-webkit-user-modify", null, null),
        "user-select"                   => array("-moz-user-select", "-webkit-user-select", null, null),
        "white-space"                   => array(__CLASS__, "whiteSpace"),
        "window-shadow"                 => array("-moz-window-shadow", null, null, null),
        "word-break"                    => array(null, null, null, "-ms-word-break"),
        "word-wrap"                     => array(null, null, null, "-ms-word-wrap"),
        "writing-mode"                  => array(null, "-webkit-writing-mode", null, "-ms-writing-mode"),
        "zoom"                          => array(null, null, null, "-ms-zoom")
        );
    /**
     * Implements {@link aCssMinifierFilter::filter()}.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
     */
    public function apply(array &$tokens)
        {
        $r = 0;
        $transformations = &$this->transformations;
        for ($i = 0, $l = count($tokens); $i < $l; $i++)
            {
            if (get_class($tokens[$i]) === "CssRulesetDeclarationToken")
                {
                $tProperty = $tokens[$i]->Property;
                if (isset($transformations[$tProperty]))
                    {
                    $result = array();
                    if (is_callable($transformations[$tProperty]))
                        {
                        $result = call_user_func_array($transformations[$tProperty], array($tokens[$i]));
                        if (!is_array($result) && is_object($result))
                            {
                            $result = array($result);
                            }
                        }
                    else
                        {
                        $tValue         = $tokens[$i]->Value;
                        $tMediaTypes    = $tokens[$i]->MediaTypes;
                        foreach ($transformations[$tProperty] as $property)
                            {
                            if ($property !== null)
                                {
                                $result[] = new CssRulesetDeclarationToken($property, $tValue, $tMediaTypes);
                                }
                            }
                        }
                    if (count($result) > 0)
                        {
                        array_splice($tokens, $i + 1, 0, $result);
                        $i += count($result);
                        $l += count($result);
                        }
                    }
                }
            }
        return $r;
        }
    /**
     * Transforms the Internet Explorer specific declaration property "filter" to Internet Explorer 8+ compatible 
     * declaratiopn property "-ms-filter". 
     * 
     * @param aCssToken $token
     * @return array
     */
    private static function filter($token)
        {
        $r = array
            (
            new CssRulesetDeclarationToken("-ms-filter", "\"" . $token->Value . "\"", $token->MediaTypes),
            );
        return $r;
        }
    /**
     * Transforms "opacity: {value}" into browser specific counterparts.
     * 
     * @param aCssToken $token
     * @return array
     */
    private static function opacity($token)
        {
        // Calculate the value for Internet Explorer filter statement
        $ieValue = (int) ((float) $token->Value * 100);
        $r = array
            (
            // Internet Explorer >= 8
            new CssRulesetDeclarationToken("-ms-filter", "\"alpha(opacity=" . $ieValue . ")\"", $token->MediaTypes),
            // Internet Explorer >= 4 <= 7
            new CssRulesetDeclarationToken("filter", "alpha(opacity=" . $ieValue . ")", $token->MediaTypes),
            new CssRulesetDeclarationToken("zoom", "1", $token->MediaTypes)
            );
        return $r;
        }
    /**
     * Transforms "white-space: pre-wrap" into browser specific counterparts.
     * 
     * @param aCssToken $token
     * @return array
     */
    private static function whiteSpace($token)
        {
        if (strtolower($token->Value) === "pre-wrap")
            {
            $r = array
                (
                // Firefox < 3
                new CssRulesetDeclarationToken("white-space", "-moz-pre-wrap", $token->MediaTypes),
                // Webkit
                new CssRulesetDeclarationToken("white-space", "-webkit-pre-wrap", $token->MediaTypes),
                // Opera >= 4 <= 6
                new CssRulesetDeclarationToken("white-space", "-pre-wrap", $token->MediaTypes),
                // Opera >= 7
                new CssRulesetDeclarationToken("white-space", "-o-pre-wrap", $token->MediaTypes),
                // Internet Explorer >= 5.5
                new CssRulesetDeclarationToken("word-wrap", "break-word", $token->MediaTypes)
                );
            return $r;
            }
        else
            {
            return array();
            }
        }
    }

/**
 * This {@link aCssMinifierFilter minifier filter} will convert @keyframes at-rule block to browser specific counterparts.
 * 
 * @package     CssMin/Minifier/Filters
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssConvertLevel3AtKeyframesMinifierFilter extends aCssMinifierFilter
    {
    /**
     * Implements {@link aCssMinifierFilter::filter()}.
     * 
     * @param array $tokens Array of objects of type aCssToken
     * @return integer Count of added, changed or removed tokens; a return value larger than 0 will rebuild the array
     */
    public function apply(array &$tokens)
        {
        $r = 0;
        $transformations = array("-moz-keyframes", "-webkit-keyframes");
        for ($i = 0, $l = count($tokens); $i < $l; $i++)
            {
            if (get_class($tokens[$i]) === "CssAtKeyframesStartToken")
                {
                for ($ii = $i; $ii < $l; $ii++)
                    {
                    if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken")
                        {
                        break;
                        }
                    }
                if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken")
                    {
                    $add    = array();
                    $source = array();
                    for ($iii = $i; $iii <= $ii; $iii++)
                        {
                        $source[] = clone($tokens[$iii]);
                        }
                    foreach ($transformations as $transformation)
                        {
                        $t = array();
                        foreach ($source as $token)
                            {
                            $t[] = clone($token);
                            }
                        $t[0]->AtRuleName = $transformation;
                        $add = array_merge($add, $t);
                        }
                    if (isset($this->configuration["RemoveSource"]) && $this->configuration["RemoveSource"] === true)
                        {
                        array_splice($tokens, $i, $ii - $i + 1, $add);
                        }
                    else
                        {
                        array_splice($tokens, $ii + 1, 0, $add);
                        }
                    $l = count($tokens);
                    $i = $ii + count($add);
                    $r += count($add);
                    }
                }
            }
        return $r;
        }
    }

/**
 * This {@link aCssMinifierPlugin} will convert a color value in hsl notation to hexadecimal notation.
 * 
 * Example:
 * <code>
 * color: hsl(232,36%,48%);
 * </code>
 * 
 * Will get converted to:
 * <code>
 * color:#4e5aa7;
 * </code>
 * 
 * @package     CssMin/Minifier/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssConvertHslColorsMinifierPlugin extends aCssMinifierPlugin
    {
    /**
     * Regular expression matching the value.
     * 
     * @var string
     */
    private $reMatch = "/^hsl\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*%\s*,\s*([0-9]+)\s*%\s*\)/iS";
    /**
     * Implements {@link aCssMinifierPlugin::minify()}.
     * 
     * @param aCssToken $token Token to process
     * @return boolean Return TRUE to break the processing of this token; FALSE to continue
     */
    public function apply(aCssToken &$token)
        {
        if (stripos($token->Value, "hsl") !== false && preg_match($this->reMatch, $token->Value, $m))
            {
            $token->Value = str_replace($m[0], $this->hsl2hex($m[1], $m[2], $m[3]), $token->Value);
            }
        return false;
        }
    /**
     * Implements {@link aMinifierPlugin::getTriggerTokens()}
     * 
     * @return array
     */
    public function getTriggerTokens()
        {
        return array
            (
            "CssAtFontFaceDeclarationToken",
            "CssAtPageDeclarationToken",
            "CssRulesetDeclarationToken"
            );
        }
    /**
     * Convert a HSL value to hexadecimal notation.
     * 
     * Based on: {@link http://www.easyrgb.com/index.php?X=MATH&H=19#text19}.
     * 
     * @param integer $hue Hue
     * @param integer $saturation Saturation
     * @param integer $lightness Lightnesss
     * @return string
     */
    private function hsl2hex($hue, $saturation, $lightness)
        {
        $hue        = $hue / 360;
        $saturation = $saturation / 100;
        $lightness  = $lightness / 100;
        if ($saturation == 0)
            {
            $red    = $lightness * 255;
            $green  = $lightness * 255;
            $blue   = $lightness * 255;
            }
        else
            {
            if ($lightness < 0.5 )
                {
                $v2 = $lightness * (1 + $saturation);
                }
            else
                {
                $v2 = ($lightness + $saturation) - ($saturation * $lightness);
                }
            $v1     = 2 * $lightness - $v2;
            $red    = 255 * self::hue2rgb($v1, $v2, $hue + (1 / 3));
            $green  = 255 * self::hue2rgb($v1, $v2, $hue);
            $blue   = 255 * self::hue2rgb($v1, $v2, $hue - (1 / 3));
            }
        return "#" . str_pad(dechex(round($red)), 2, "0", STR_PAD_LEFT) . str_pad(dechex(round($green)), 2, "0", STR_PAD_LEFT) . str_pad(dechex(round($blue)), 2, "0", STR_PAD_LEFT);
        }
    /**
     * Apply hue to a rgb color value.
     * 
     * @param integer $v1 Value 1
     * @param integer $v2 Value 2
     * @param integer $hue Hue
     * @return integer
     */
    private function hue2rgb($v1, $v2, $hue)
        {
        if ($hue < 0)
            {
            $hue += 1;
            }
        if ($hue > 1)
            {
            $hue -= 1;
            }
        if ((6 * $hue) < 1)
            {
            return ($v1 + ($v2 - $v1) * 6 * $hue);
            }
        if ((2 * $hue) < 1)
            {
            return ($v2);
            }
        if ((3 * $hue) < 2)
            {
            return ($v1 + ($v2 - $v1) * (( 2 / 3) - $hue) * 6);
            }
        return $v1;
        }
    }

/**
 * This {@link aCssMinifierPlugin} will convert the font-weight values normal and bold to their numeric notation.
 * 
 * Example:
 * <code>
 * font-weight: normal;
 * font: bold 11px monospace;
 * </code>
 * 
 * Will get converted to:
 * <code>
 * font-weight:400;
 * font:700 11px monospace;
 * </code>
 *
 * @package     CssMin/Minifier/Pluginsn
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssConvertFontWeightMinifierPlugin extends aCssMinifierPlugin
    {
    /**
     * Array of included declaration properties this plugin will process; others declaration properties will get
     * ignored. 
     * 
     * @var array
     */
    private $include = array
        (
        "font",
        "font-weight"
        );
    /**
     * Regular expression matching the value.
     * 
     * @var string
     */
    private $reMatch = null;
    /**
     * Regular expression replace the value.
     * 
     * @var string
     */
    private $reReplace = "\"\${1}\" . \$this->transformation[\"\${2}\"] . \"\${3}\"";
    /**
     * Transformation table used by the {@link CssConvertFontWeightMinifierPlugin::$reReplace replace regular expression}.
     * 
     * @var array
     */
    private $transformation = array
        (
        "normal"    => "400",
        "bold"      => "700"
        );
    /**
     * Overwrites {@link aCssMinifierPlugin::__construct()}.
     * 
     * The constructor will create the {@link CssConvertFontWeightMinifierPlugin::$reReplace replace regular expression}
     * based on the {@link CssConvertFontWeightMinifierPlugin::$transformation transformation table}.
     * 
     * @param CssMinifier $minifier The CssMinifier object of this plugin.
     * @return void
     */
    public function __construct(CssMinifier $minifier)
        {
        $this->reMatch = "/(^|\s)+(" . implode("|", array_keys($this->transformation)). ")(\s|$)+/eiS";
        parent::__construct($minifier);
        }
    /**
     * Implements {@link aCssMinifierPlugin::minify()}.
     * 
     * @param aCssToken $token Token to process
     * @return boolean Return TRUE to break the processing of this token; FALSE to continue
     */
    public function apply(aCssToken &$token)
        {
        if (in_array($token->Property, $this->include) && preg_match($this->reMatch, $token->Value, $m))
            {
            $token->Value = preg_replace($this->reMatch, $this->reReplace, $token->Value);
            }
        return false;
        }
    /**
     * Implements {@link aMinifierPlugin::getTriggerTokens()}
     * 
     * @return array
     */
    public function getTriggerTokens()
        {
        return array
            (
            "CssAtFontFaceDeclarationToken",
            "CssAtPageDeclarationToken",
            "CssRulesetDeclarationToken"
            );
        }
    }

/**
 * This {@link aCssMinifierPlugin} will compress several unit values to their short notations. Examples:
 * 
 * <code>
 * padding: 0.5em;
 * border: 0px;
 * margin: 0 0 0 0;
 * </code>
 * 
 * Will get compressed to:
 * 
 * <code>
 * padding:.5px;
 * border:0;
 * margin:0;
 * </code>
 * 
 * --
 *
 * @package     CssMin/Minifier/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssCompressUnitValuesMinifierPlugin extends aCssMinifierPlugin
    {
    /**
     * Regular expression used for matching and replacing unit values.
     * 
     * @var array
     */
    private $re = array
        (
        "/(^| |-)0\.([0-9]+?)(0+)?(%|em|ex|px|in|cm|mm|pt|pc)/iS" => "\${1}.\${2}\${4}",
        "/(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)/iS" => "\${1}0",
        "/(^0\s0\s0\s0)|(^0\s0\s0$)|(^0\s0$)/iS" => "0"
        );
    /**
     * Regular expression matching the value.
     * 
     * @var string
     */
    private $reMatch = "/(^| |-)0\.([0-9]+?)(0+)?(%|em|ex|px|in|cm|mm|pt|pc)|(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)|(^0\s0\s0\s0$)|(^0\s0\s0$)|(^0\s0$)/iS";
    /**
     * Implements {@link aCssMinifierPlugin::minify()}.
     * 
     * @param aCssToken $token Token to process
     * @return boolean Return TRUE to break the processing of this token; FALSE to continue
     */
    public function apply(aCssToken &$token)
        {
        if (preg_match($this->reMatch, $token->Value))
            {
            foreach ($this->re as $reMatch => $reReplace)
                {
                $token->Value = preg_replace($reMatch, $reReplace, $token->Value);
                }
            }
        return false;
        }
    /**
     * Implements {@link aMinifierPlugin::getTriggerTokens()}
     * 
     * @return array
     */
    public function getTriggerTokens()
        {
        return array
            (
            "CssAtFontFaceDeclarationToken",
            "CssAtPageDeclarationToken",
            "CssRulesetDeclarationToken"
            );
        }
    }

/**
 * This {@link aCssMinifierPlugin} compress the content of expresssion() declaration values.
 * 
 * For compression of expressions {@link https://github.com/rgrove/jsmin-php/ JSMin} will get used. JSMin have to be 
 * already included or loadable via {@link http://goo.gl/JrW54 PHP autoloading}. 
 * 
 * @package     CssMin/Minifier/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssCompressExpressionValuesMinifierPlugin extends aCssMinifierPlugin
    {
    /**
     * Implements {@link aCssMinifierPlugin::minify()}.
     * 
     * @param aCssToken $token Token to process
     * @return boolean Return TRUE to break the processing of this token; FALSE to continue
     */
    public function apply(aCssToken &$token)
        {
        if (class_exists("JSMin") && stripos($token->Value, "expression(") !== false)
            {
            $value  = $token->Value;
            $value  = substr($token->Value, stripos($token->Value, "expression(") + 10);
            $value  = trim(JSMin::minify($value));
            $token->Value = "expression(" . $value . ")";
            }
        return false;
        }
    /**
     * Implements {@link aMinifierPlugin::getTriggerTokens()}
     * 
     * @return array
     */
    public function getTriggerTokens()
        {
        return array
            (
            "CssAtFontFaceDeclarationToken",
            "CssAtPageDeclarationToken",
            "CssRulesetDeclarationToken"
            );
        }
    }

/**
 * This {@link aCssMinifierPlugin} will convert hexadecimal color value with 6 chars to their 3 char hexadecimal 
 * notation (if possible). 
 * 
 * Example:
 * <code>
 * color: #aabbcc;
 * </code>
 * 
 * Will get converted to:
 * <code>
 * color:#abc;
 * </code>
 * 
 * @package     CssMin/Minifier/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssCompressColorValuesMinifierPlugin extends aCssMinifierPlugin
    {
    /**
     * Regular expression matching 6 char hexadecimal color values.
     * 
     * @var string
     */
    private $reMatch = "/\#([0-9a-f]{6})/iS";
    /**
     * Implements {@link aCssMinifierPlugin::minify()}.
     * 
     * @param aCssToken $token Token to process
     * @return boolean Return TRUE to break the processing of this token; FALSE to continue
     */
    public function apply(aCssToken &$token)
        {
        if (strpos($token->Value, "#") !== false && preg_match($this->reMatch, $token->Value, $m))
            {
            $value = strtolower($m[1]);
            if ($value[0] == $value[1] && $value[2] == $value[3] && $value[4] == $value[5])
                {
                $token->Value = str_replace($m[0], "#" . $value[0] . $value[2] . $value[4], $token->Value);
                }
            }
        return false;
        }
    /**
     * Implements {@link aMinifierPlugin::getTriggerTokens()}
     * 
     * @return array
     */
    public function getTriggerTokens()
        {
        return array
            (
            "CssAtFontFaceDeclarationToken",
            "CssAtPageDeclarationToken",
            "CssRulesetDeclarationToken"
            );
        }
    }

/**
 * This {@link aCssToken CSS token} represents a CSS comment.
 * 
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssCommentToken extends aCssToken
    {
    /**
     * Comment as Text.
     * 
     * @var string
     */
    public $Comment = "";
    /**
     * Set the properties of a comment token.
     * 
     * @param string $comment Comment including comment delimiters 
     * @return void
     */
    public function __construct($comment)
        {
        $this->Comment = $comment;
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return $this->Comment;
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for parsing comments.
 * 
 * Adds a {@link CssCommentToken} to the parser if a comment was found.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssCommentParserPlugin extends aCssParserPlugin
    {
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("*", "/");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return false;
        }
    /**
     * Stored buffer for restore.
     * 
     * @var string
     */
    private $restoreBuffer = "";
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        if ($char === "*" && $previousChar === "/" && $state !== "T_COMMENT")
            {
            $this->parser->pushState("T_COMMENT");
            $this->parser->setExclusive(__CLASS__);
            $this->restoreBuffer = substr($this->parser->getAndClearBuffer(), 0, -2);
            }
        elseif ($char === "/" && $previousChar === "*" && $state === "T_COMMENT")
            {
            $this->parser->popState();
            $this->parser->unsetExclusive();
            $this->parser->appendToken(new CssCommentToken("/*" . $this->parser->getAndClearBuffer()));
            $this->parser->setBuffer($this->restoreBuffer);
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * This {@link aCssToken CSS token} represents the start of a @variables at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtVariablesStartToken extends aCssAtBlockStartToken
    {
    /**
     * Media types of the @variables at-rule block.
     * 
     * @var array
     */
    public $MediaTypes = array();
    /**
     * Set the properties of a @variables at-rule token.
     * 
     * @param array $mediaTypes Media types
     * @return void
     */
    public function __construct($mediaTypes = null)
        {
        $this->MediaTypes = $mediaTypes ? $mediaTypes : array("all");
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "";
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for parsing @variables at-rule block with including declarations.
 * 
 * Found @variables at-rule blocks will add a {@link CssAtVariablesStartToken} and {@link CssAtVariablesEndToken} to the 
 * parser; including declarations as {@link CssAtVariablesDeclarationToken}.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtVariablesParserPlugin extends aCssParserPlugin
    {
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("@", "{", "}", ":", ";");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return array("T_DOCUMENT", "T_AT_VARIABLES::PREPARE", "T_AT_VARIABLES", "T_AT_VARIABLES_DECLARATION");
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        // Start of @variables at-rule block
        if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@variables")
            {
            $this->parser->pushState("T_AT_VARIABLES::PREPARE");
            $this->parser->clearBuffer();
            return $index + 10;
            }
        // Start of @variables declarations
        elseif ($char === "{" && $state === "T_AT_VARIABLES::PREPARE")
            {
            $this->parser->setState("T_AT_VARIABLES");
            $mediaTypes = array_filter(array_map("trim", explode(",", $this->parser->getAndClearBuffer("{"))));
            $this->parser->appendToken(new CssAtVariablesStartToken($mediaTypes));
            }
        // Start of @variables declaration
        if ($char === ":" && $state === "T_AT_VARIABLES")
            {
            $this->buffer = $this->parser->getAndClearBuffer(":");
            $this->parser->pushState("T_AT_VARIABLES_DECLARATION");
            }
        // Unterminated @variables declaration
        elseif ($char === ":" && $state === "T_AT_VARIABLES_DECLARATION")
            {
            // Ignore Internet Explorer filter declarations
            if ($this->buffer === "filter")
                {
                return false;
                }
            CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @variables declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
            }
        // End of @variables declaration
        elseif (($char === ";" || $char === "}") && $state === "T_AT_VARIABLES_DECLARATION")
            {
            $value = $this->parser->getAndClearBuffer(";}");
            if (strtolower(substr($value, -10, 10)) === "!important")
                {
                $value = trim(substr($value, 0, -10));
                $isImportant = true;
                }
            else
                {
                $isImportant = false;
                }
            $this->parser->popState();
            $this->parser->appendToken(new CssAtVariablesDeclarationToken($this->buffer, $value, $isImportant));
            $this->buffer = "";
            }
        // End of @variables at-rule block
        elseif ($char === "}" && $state === "T_AT_VARIABLES")
            {
            $this->parser->popState();
            $this->parser->clearBuffer();
            $this->parser->appendToken(new CssAtVariablesEndToken());
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * This {@link aCssToken CSS token} represents the end of a @variables at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtVariablesEndToken extends aCssAtBlockEndToken
    {
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "";
        }
    }

/**
 * This {@link aCssToken CSS token} represents a declaration of a @variables at-rule block.
 * 
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtVariablesDeclarationToken extends aCssDeclarationToken
    {
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "";
        }
    }

/**
* This {@link aCssToken CSS token} represents the start of a @page at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtPageStartToken extends aCssAtBlockStartToken
    {
    /**
     * Selector.
     * 
     * @var string
     */
    public $Selector = "";
    /**
     * Sets the properties of the @page at-rule.
     * 
     * @param string $selector Selector
     * @return void
     */
    public function __construct($selector = "")
        {
        $this->Selector = $selector;
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "@page" . ($this->Selector ? " " . $this->Selector : "") . "{";
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for parsing @page at-rule block with including declarations.
 * 
 * Found @page at-rule blocks will add a {@link CssAtPageStartToken} and {@link CssAtPageEndToken} to the 
 * parser; including declarations as {@link CssAtPageDeclarationToken}.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtPageParserPlugin extends aCssParserPlugin
    {
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("@", "{", "}", ":", ";");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return array("T_DOCUMENT", "T_AT_PAGE::SELECTOR", "T_AT_PAGE", "T_AT_PAGE_DECLARATION");
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        // Start of @page at-rule block
        if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 5)) === "@page")
            {
            $this->parser->pushState("T_AT_PAGE::SELECTOR");
            $this->parser->clearBuffer();
            return $index + 5;
            }
        // Start of @page declarations
        elseif ($char === "{" && $state === "T_AT_PAGE::SELECTOR")
            {
            $selector = $this->parser->getAndClearBuffer("{");
            $this->parser->setState("T_AT_PAGE");
            $this->parser->clearBuffer();
            $this->parser->appendToken(new CssAtPageStartToken($selector));
            }
        // Start of @page declaration
        elseif ($char === ":" && $state === "T_AT_PAGE")
            {
            $this->parser->pushState("T_AT_PAGE_DECLARATION");
            $this->buffer = $this->parser->getAndClearBuffer(":", true);
            }
        // Unterminated @font-face declaration
        elseif ($char === ":" && $state === "T_AT_PAGE_DECLARATION")
            {
            // Ignore Internet Explorer filter declarations
            if ($this->buffer === "filter")
                {
                return false;
                }
            CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @page declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
            }
        // End of @page declaration
        elseif (($char === ";" || $char === "}") && $state == "T_AT_PAGE_DECLARATION")
            {
            $value = $this->parser->getAndClearBuffer(";}");
            if (strtolower(substr($value, -10, 10)) == "!important")
                {
                $value = trim(substr($value, 0, -10));
                $isImportant = true;
                }
            else
                {
                $isImportant = false;
                }
            $this->parser->popState();
            $this->parser->appendToken(new CssAtPageDeclarationToken($this->buffer, $value, $isImportant));
            // --
            if ($char === "}")
                {
                $this->parser->popState();
                $this->parser->appendToken(new CssAtPageEndToken());
                }
            $this->buffer = "";
            }
        // End of @page at-rule block
        elseif ($char === "}" && $state === "T_AT_PAGE")
            {
            $this->parser->popState();
            $this->parser->clearBuffer();
            $this->parser->appendToken(new CssAtPageEndToken());
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * This {@link aCssToken CSS token} represents the end of a @page at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtPageEndToken extends aCssAtBlockEndToken
    {
    
    }

/**
 * This {@link aCssToken CSS token} represents a declaration of a @page at-rule block.
 * 
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtPageDeclarationToken extends aCssDeclarationToken
    {
    
    }

/**
 * This {@link aCssToken CSS token} represents the start of a @media at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtMediaStartToken extends aCssAtBlockStartToken
    {
    /**
     * Sets the properties of the @media at-rule.
     * 
     * @param array $mediaTypes Media types
     * @return void
     */
    public function __construct(array $mediaTypes = array())
        {
        $this->MediaTypes = $mediaTypes;
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "@media " . implode(",", $this->MediaTypes) . "{";
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for parsing @media at-rule block.
 * 
 * Found @media at-rule blocks will add a {@link CssAtMediaStartToken} and {@link CssAtMediaEndToken} to the parser. 
 * This plugin will also set the the current media types using {@link CssParser::setMediaTypes()} and
 * {@link CssParser::unsetMediaTypes()}.
 *
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtMediaParserPlugin extends aCssParserPlugin
    {
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("@", "{", "}");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return array("T_DOCUMENT", "T_AT_MEDIA::PREPARE", "T_AT_MEDIA");
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 6)) === "@media")
            {
            $this->parser->pushState("T_AT_MEDIA::PREPARE");
            $this->parser->clearBuffer();
            return $index + 6;
            }
        elseif ($char === "{" && $state === "T_AT_MEDIA::PREPARE")
            {
            $mediaTypes = array_filter(array_map("trim", explode(",", $this->parser->getAndClearBuffer("{"))));
            $this->parser->setMediaTypes($mediaTypes);
            $this->parser->setState("T_AT_MEDIA");
            $this->parser->appendToken(new CssAtMediaStartToken($mediaTypes));
            }
        elseif ($char === "}" && $state === "T_AT_MEDIA")
            {
            $this->parser->appendToken(new CssAtMediaEndToken());
            $this->parser->clearBuffer();
            $this->parser->unsetMediaTypes();
            $this->parser->popState();
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * This {@link aCssToken CSS token} represents the end of a @media at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtMediaEndToken extends aCssAtBlockEndToken
    {
    
    }

/**
 * This {@link aCssToken CSS token} represents the start of a @keyframes at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtKeyframesStartToken extends aCssAtBlockStartToken
    {
    /**
     * Name of the at-rule.
     * 
     * @var string
     */
    public $AtRuleName = "keyframes";
    /**
     * Name
     * 
     * @var string
     */
    public $Name = "";
    /**
     * Sets the properties of the @page at-rule.
     * 
     * @param string $selector Selector
     * @return void
     */
    public function __construct($name, $atRuleName = null)
        {
        $this->Name = $name;
        if (!is_null($atRuleName))
            {
            $this->AtRuleName = $atRuleName;
            }
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "@" . $this->AtRuleName . " \"" . $this->Name . "\"{";
        }
    }

/**
 * This {@link aCssToken CSS token} represents the start of a ruleset of a @keyframes at-rule block.
 * 
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtKeyframesRulesetStartToken extends aCssRulesetStartToken
    {
    /**
     * Array of selectors.
     * 
     * @var array
     */
    public $Selectors = array();
    /**
     * Set the properties of a ruleset token.
     * 
     * @param array $selectors Selectors of the ruleset 
     * @return void
     */
    public function __construct(array $selectors = array())
        {
        $this->Selectors = $selectors;
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return implode(",", $this->Selectors) . "{";
        }
    }

/**
 * This {@link aCssToken CSS token} represents the end of a ruleset of a @keyframes at-rule block.
 * 
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtKeyframesRulesetEndToken extends aCssRulesetEndToken
    {
    
    }

/**
 * This {@link aCssToken CSS token} represents a ruleset declaration of a @keyframes at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtKeyframesRulesetDeclarationToken extends aCssDeclarationToken
    {
    
    }

/**
 * {@link aCssParserPlugin Parser plugin} for parsing @keyframes at-rule blocks, rulesets and declarations.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtKeyframesParserPlugin extends aCssParserPlugin
    {
    /**
     * @var string Keyword
     */
    private $atRuleName = "";
    /**
     * Selectors.
     * 
     * @var array
     */
    private $selectors = array();
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("@", "{", "}", ":", ",", ";");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return array("T_DOCUMENT", "T_AT_KEYFRAMES::NAME", "T_AT_KEYFRAMES", "T_AT_KEYFRAMES_RULESETS", "T_AT_KEYFRAMES_RULESET", "T_AT_KEYFRAMES_RULESET_DECLARATION");
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        // Start of @keyframes at-rule block
        if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@keyframes") 
            {
            $this->atRuleName = "keyframes";
            $this->parser->pushState("T_AT_KEYFRAMES::NAME");
            $this->parser->clearBuffer();
            return $index + 10;
            }
        // Start of @keyframes at-rule block (@-moz-keyframes)
        elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 15)) === "@-moz-keyframes")
            {
            $this->atRuleName = "-moz-keyframes";
            $this->parser->pushState("T_AT_KEYFRAMES::NAME");
            $this->parser->clearBuffer();
            return $index + 15;
            }
        // Start of @keyframes at-rule block (@-webkit-keyframes)
        elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 18)) === "@-webkit-keyframes")
            {
            $this->atRuleName = "-webkit-keyframes";
            $this->parser->pushState("T_AT_KEYFRAMES::NAME");
            $this->parser->clearBuffer();
            return $index + 18;
            }
        // Start of @keyframes rulesets
        elseif ($char === "{" && $state === "T_AT_KEYFRAMES::NAME")
            {
            $name = $this->parser->getAndClearBuffer("{\"'");
            $this->parser->setState("T_AT_KEYFRAMES_RULESETS");
            $this->parser->clearBuffer();
            $this->parser->appendToken(new CssAtKeyframesStartToken($name, $this->atRuleName));
            }
        // Start of @keyframe ruleset and selectors
        if ($char === "," && $state === "T_AT_KEYFRAMES_RULESETS")
            {
            $this->selectors[] = $this->parser->getAndClearBuffer(",{");
            }
        // Start of a @keyframes ruleset
        elseif ($char === "{" && $state === "T_AT_KEYFRAMES_RULESETS")
            {
            if ($this->parser->getBuffer() !== "")
                {
                $this->selectors[] = $this->parser->getAndClearBuffer(",{");
                $this->parser->pushState("T_AT_KEYFRAMES_RULESET");
                $this->parser->appendToken(new CssAtKeyframesRulesetStartToken($this->selectors));
                $this->selectors = array();
                }
            }
        // Start of @keyframes ruleset declaration
        elseif ($char === ":" && $state === "T_AT_KEYFRAMES_RULESET")
            {
            $this->parser->pushState("T_AT_KEYFRAMES_RULESET_DECLARATION");
            $this->buffer = $this->parser->getAndClearBuffer(":;", true);
            }
        // Unterminated @keyframes ruleset declaration
        elseif ($char === ":" && $state === "T_AT_KEYFRAMES_RULESET_DECLARATION")
            {
            // Ignore Internet Explorer filter declarations
            if ($this->buffer === "filter")
                {
                return false;
                }
            CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @keyframes ruleset declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
            }
        // End of declaration
        elseif (($char === ";" || $char === "}") && $state === "T_AT_KEYFRAMES_RULESET_DECLARATION")
            {
            $value = $this->parser->getAndClearBuffer(";}");
            if (strtolower(substr($value, -10, 10)) === "!important")
                {
                $value = trim(substr($value, 0, -10));
                $isImportant = true;
                }
            else
                {
                $isImportant = false;
                }
            $this->parser->popState();
            $this->parser->appendToken(new CssAtKeyframesRulesetDeclarationToken($this->buffer, $value, $isImportant));
            // Declaration ends with a right curly brace; so we have to end the ruleset
            if ($char === "}")
                {
                $this->parser->appendToken(new CssAtKeyframesRulesetEndToken());
                $this->parser->popState();
                }
            $this->buffer = "";
            }
        // End of @keyframes ruleset
        elseif ($char === "}" && $state === "T_AT_KEYFRAMES_RULESET")
            {
            $this->parser->clearBuffer();
            
            $this->parser->popState();
            $this->parser->appendToken(new CssAtKeyframesRulesetEndToken());
            }
        // End of @keyframes rulesets
        elseif ($char === "}" && $state === "T_AT_KEYFRAMES_RULESETS")
            {
            $this->parser->clearBuffer();
            $this->parser->popState();
            $this->parser->appendToken(new CssAtKeyframesEndToken());
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * This {@link aCssToken CSS token} represents the end of a @keyframes at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtKeyframesEndToken extends aCssAtBlockEndToken
    {
    
    }

/**
 * This {@link aCssToken CSS token} represents a @import at-rule.
 * 
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1.b1 (2001-02-22)
 */
class CssAtImportToken extends aCssToken
    {
    /**
     * Import path of the @import at-rule.
     * 
     * @var string
     */
    public $Import = "";
    /**
     * Media types of the @import at-rule.
     * 
     * @var array
     */
    public $MediaTypes = array();
    /**
     * Set the properties of a @import at-rule token.
     * 
     * @param string $import Import path
     * @param array $mediaTypes Media types
     * @return void
     */
    public function __construct($import, $mediaTypes)
        {
        $this->Import       = $import;
        $this->MediaTypes   = $mediaTypes ? $mediaTypes : array();
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "@import \"" . $this->Import . "\"" . (count($this->MediaTypes) > 0 ? " "  . implode(",", $this->MediaTypes) : ""). ";";
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for parsing @import at-rule.
 * 
 * If a @import at-rule was found this plugin will add a {@link CssAtImportToken} to the parser.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtImportParserPlugin extends aCssParserPlugin
    {
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("@", ";", ",", "\n");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return array("T_DOCUMENT", "T_AT_IMPORT");
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 7)) === "@import")
            {
            $this->parser->pushState("T_AT_IMPORT");
            $this->parser->clearBuffer();
            return $index + 7;
            }
        elseif (($char === ";" || $char === "\n") && $state === "T_AT_IMPORT")
            {
            $this->buffer = $this->parser->getAndClearBuffer(";");
            $pos = false;
            foreach (array(")", "\"", "'") as $needle)
                {
                if (($pos = strrpos($this->buffer, $needle)) !== false)
                    {
                    break;
                    }
                }
            $import = substr($this->buffer, 0, $pos + 1);
            if (stripos($import, "url(") === 0)
                {
                $import = substr($import, 4, -1);
                }
            $import = trim($import, " \t\n\r\0\x0B'\"");
            $mediaTypes = array_filter(array_map("trim", explode(",", trim(substr($this->buffer, $pos + 1), " \t\n\r\0\x0B{"))));
            if ($pos)
                {
                $this->parser->appendToken(new CssAtImportToken($import, $mediaTypes));
                }
            else
                {
                CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Invalid @import at-rule syntax", $this->parser->buffer));
                }
            $this->parser->popState();
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * This {@link aCssToken CSS token} represents the start of a @font-face at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtFontFaceStartToken extends aCssAtBlockStartToken
    {
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "@font-face{";
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for parsing @font-face at-rule block with including declarations.
 * 
 * Found @font-face at-rule blocks will add a {@link CssAtFontFaceStartToken} and {@link CssAtFontFaceEndToken} to the 
 * parser; including declarations as {@link CssAtFontFaceDeclarationToken}.
 * 
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtFontFaceParserPlugin extends aCssParserPlugin
    {
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("@", "{", "}", ":", ";");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return array("T_DOCUMENT", "T_AT_FONT_FACE::PREPARE", "T_AT_FONT_FACE", "T_AT_FONT_FACE_DECLARATION");
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        // Start of @font-face at-rule block
        if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@font-face")
            {
            $this->parser->pushState("T_AT_FONT_FACE::PREPARE");
            $this->parser->clearBuffer();
            return $index + 10;
            }
        // Start of @font-face declarations
        elseif ($char === "{" && $state === "T_AT_FONT_FACE::PREPARE")
            {
            $this->parser->setState("T_AT_FONT_FACE");
            $this->parser->clearBuffer();
            $this->parser->appendToken(new CssAtFontFaceStartToken());
            }
        // Start of @font-face declaration
        elseif ($char === ":" && $state === "T_AT_FONT_FACE")
            {
            $this->parser->pushState("T_AT_FONT_FACE_DECLARATION");
            $this->buffer = $this->parser->getAndClearBuffer(":", true);
            }
        // Unterminated @font-face declaration
        elseif ($char === ":" && $state === "T_AT_FONT_FACE_DECLARATION")
            {
            // Ignore Internet Explorer filter declarations
            if ($this->buffer === "filter")
                {
                return false;
                }
            CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @font-face declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
            }
        // End of @font-face declaration
        elseif (($char === ";" || $char === "}") && $state === "T_AT_FONT_FACE_DECLARATION")
            {
            $value = $this->parser->getAndClearBuffer(";}");
            if (strtolower(substr($value, -10, 10)) === "!important")
                {
                $value = trim(substr($value, 0, -10));
                $isImportant = true;
                }
            else
                {
                $isImportant = false;
                }
            $this->parser->popState();
            $this->parser->appendToken(new CssAtFontFaceDeclarationToken($this->buffer, $value, $isImportant));
            $this->buffer = "";
            // --
            if ($char === "}")
                {
                $this->parser->appendToken(new CssAtFontFaceEndToken());
                $this->parser->popState();
                }
            }
        // End of @font-face at-rule block
        elseif ($char === "}" && $state === "T_AT_FONT_FACE")
            {
            $this->parser->appendToken(new CssAtFontFaceEndToken());
            $this->parser->clearBuffer();
            $this->parser->popState();
            }
        else
            {
            return false;
            }
        return true;
        }
    }

/**
 * This {@link aCssToken CSS token} represents the end of a @font-face at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtFontFaceEndToken extends aCssAtBlockEndToken
    {
    
    }

/**
 * This {@link aCssToken CSS token} represents a declaration of a @font-face at-rule block.
 *
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtFontFaceDeclarationToken extends aCssDeclarationToken
    {
    
    }

/**
 * This {@link aCssToken CSS token} represents a @charset at-rule.
 * 
 * @package     CssMin/Tokens
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtCharsetToken extends aCssToken
    {
    /**
     * Charset of the @charset at-rule.
     * 
     * @var string
     */
    public $Charset = "";
    /**
     * Set the properties of @charset at-rule token. 
     * 
     * @param string $charset Charset of the @charset at-rule token
     * @return void
     */
    public function __construct($charset)
        {
        $this->Charset = $charset;
        }
    /**
     * Implements {@link aCssToken::__toString()}.
     * 
     * @return string
     */
    public function __toString()
        {
        return "@charset " . $this->Charset . ";";
        }
    }

/**
 * {@link aCssParserPlugin Parser plugin} for parsing @charset at-rule.
 * 
 * If a @charset at-rule was found this plugin will add a {@link CssAtCharsetToken} to the parser.
 *
 * @package     CssMin/Parser/Plugins
 * @link        http://code.google.com/p/cssmin/
 * @author      Joe Scylla <joe.scylla@gmail.com>
 * @copyright   2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
 * @license     http://opensource.org/licenses/mit-license.php MIT License
 * @version     3.0.1
 */
class CssAtCharsetParserPlugin extends aCssParserPlugin
    {
    /**
     * Implements {@link aCssParserPlugin::getTriggerChars()}.
     * 
     * @return array
     */
    public function getTriggerChars()
        {
        return array("@", ";", "\n");
        }
    /**
     * Implements {@link aCssParserPlugin::getTriggerStates()}.
     * 
     * @return array
     */
    public function getTriggerStates()
        {
        return array("T_DOCUMENT", "T_AT_CHARSET");
        }
    /**
     * Implements {@link aCssParserPlugin::parse()}.
     * 
     * @param integer $index Current index
     * @param string $char Current char
     * @param string $previousChar Previous char
     * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
     */
    public function parse($index, $char, $previousChar, $state)
        {
        if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 8)) === "@charset")
            {
            $this->parser->pushState("T_AT_CHARSET");
            $this->parser->clearBuffer();
            return $index + 8;
            }
        elseif (($char === ";" || $char === "\n") && $state === "T_AT_CHARSET")
            {
            $charset = $this->parser->getAndClearBuffer(";");
            $this->parser->popState();
            $this->parser->appendToken(new CssAtCharsetToken($charset));
            }
        else
            {
            return false;
            }
        return true;
        }
    }
}
