/** * 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; }
/** * 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; }
/** * 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; }
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); } } } }
/** * 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; }