/**
  * 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\v'\"");
         $mediaTypes = array_filter(array_map("trim", explode(",", trim(substr($this->buffer, $pos + 1), " \t\n\r\v{"))));
         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;
 }
Example #2
0
 /**
  * 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);
     }
 }
 /**
  * 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;
                 } 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);
 }
 /**
  * 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(",{");
     } 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();
         }
     } elseif ($char === ":" && $state === "T_RULESET") {
         $this->parser->pushState("T_RULESET_DECLARATION");
         $this->buffer = $this->parser->getAndClearBuffer(":;", true);
     } 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() . "_"));
     } 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 = "";
     } 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;
 }
 /**
  * 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;
     } 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));
     } elseif ($char === ":" && $state === "T_AT_PAGE") {
         $this->parser->pushState("T_AT_PAGE_DECLARATION");
         $this->buffer = $this->parser->getAndClearBuffer(":", true);
     } 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() . "_"));
     } 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 = "";
     } elseif ($char === "}" && $state === "T_AT_PAGE") {
         $this->parser->popState();
         $this->parser->clearBuffer();
         $this->parser->appendToken(new CssAtPageEndToken());
     } else {
         return false;
     }
     return true;
 }
Example #6
0
 /**
  * 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);
     }
 }
 /**
  * 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;
     } 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");
     } 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() . "_"));
     } 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 = "";
     } elseif ($char === "}" && $state === "T_AT_VARIABLES") {
         $this->parser->popState();
         $this->parser->clearBuffer();
         $this->parser->appendToken(new CssAtVariablesEndToken());
     } else {
         return false;
     }
     return true;
 }
 /**
  * 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__);
     } elseif ($char === "\n" && $previousChar === "\\" && $state === "T_STRING") {
         $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2));
     } 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;
     } 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;
 }
 /**
  * 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__);
     } elseif ($char === "\n" && $previousChar === "\\" && $state === "T_URL") {
         $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2));
     } 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 . "_"));
     } elseif ($char === ")" && $state === "T_URL") {
         $this->parser->popState();
         $this->parser->unsetExclusive();
     } else {
         return false;
     }
     return true;
 }
 /**
  * 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;
 }
Example #11
0
 public function parse($index, $char, $previousChar, $state)
 {
     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;
     } elseif ($char === "{" && $state === "T_AT_FONT_FACE::PREPARE") {
         $this->parser->setState("T_AT_FONT_FACE");
         $this->parser->clearBuffer();
         $this->parser->appendToken(new CssAtFontFaceStartToken());
     } elseif ($char === ":" && $state === "T_AT_FONT_FACE") {
         $this->parser->pushState("T_AT_FONT_FACE_DECLARATION");
         $this->buffer = $this->parser->getAndClearBuffer(":", true);
     } elseif ($char === ":" && $state === "T_AT_FONT_FACE_DECLARATION") {
         if ($this->buffer === "filter") {
             return false;
         }
         CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @font-face declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
     } 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();
         }
     } elseif ($char === "}" && $state === "T_AT_FONT_FACE") {
         $this->parser->appendToken(new CssAtFontFaceEndToken());
         $this->parser->clearBuffer();
         $this->parser->popState();
     } else {
         return false;
     }
     return true;
 }
 /**
  * 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]));
             } 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;
                             } 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);
             }
         }
     }
 }
Example #13
0
 /**
  * Implements {@link aCssParserPlugin::parse()}.
  *
  * @param int    $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;
     } elseif ($char === '{' && $state === 'T_AT_FONT_FACE::PREPARE') {
         $this->parser->setState('T_AT_FONT_FACE');
         $this->parser->clearBuffer();
         $this->parser->appendToken(new CssAtFontFaceStartToken());
     } elseif ($char === ':' && $state === 'T_AT_FONT_FACE') {
         $this->parser->pushState('T_AT_FONT_FACE_DECLARATION');
         $this->buffer = $this->parser->getAndClearBuffer(':', true);
     } 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() . '_'));
     } 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();
         }
     } elseif ($char === '}' && $state === 'T_AT_FONT_FACE') {
         $this->parser->appendToken(new CssAtFontFaceEndToken());
         $this->parser->clearBuffer();
         $this->parser->popState();
     } else {
         return false;
     }
     return true;
 }