Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 12 commits
  • 15 files changed
  • 0 commit comments
  • 1 contributor
View
7 NEWS
@@ -19,12 +19,13 @@ Key: # = backwards incompatible change, ! = new feature, - = bugfix.
be turned off by setting 'discard_invalid_selectors' to false
! All HTML outputs uses HTML 5
! Gzipped original and minified size are showed
-! Support minimizing HSL color
+! Support minimizing HSL, RGBA and HSLA color
! HEX colors are converted to lowercase to smaller compressed size
! New CSS3 properties
! Method for merging @import files
! Properly sorting IE 6 and 7 hack properties (thanks weikinhuang)
! Support for CSS3 animations
+! Support for optimizing colors inside gradient function
- Added localizations for css_optimiser.php
- Fixed bug with cookie setting for custom templates
- Minor security problem in css_optimiser.php fixed
@@ -36,5 +37,7 @@ Key: # = backwards incompatible change, ! = new feature, - = bugfix.
- Fixed incorrect parsing of !imporant close to numbers
- Fixed incorrect removing url() from @import and parsing with media type specified
- Fixed removing quotation marks from @font-face src property (thanks oroboto)
+- Fixed removing quotation marks from url() if contains '('
+- Fixed parsing gradient function
- Allowed Copy to Clipboard for Firefox if preference set (if not, gives
- instructions on how to add and warning re: security)
+ instructions on how to add and warning re: security
View
3  TODO
@@ -0,0 +1,3 @@
+- Merge overflow-y and overflow-x to overflow property (http://www.w3.org/TR/css3-box/#overflow)
+- Complete support for CSS3 @page at rule (http://www.w3.org/TR/css3-page/)
+- outline shorthands
View
99 lib/CSSTidy.php
@@ -68,7 +68,7 @@ class CSSTidy
* @var array
* @version 1.0
*/
- public static $whitespace = array(' ', "\n", "\t", "\r", "\x0B");
+ public static $whitespace = array(' ', "\n", "\r", "\x0B");
/**
* All CSS tokens used by csstidy
@@ -95,7 +95,9 @@ class CSSTidy
'@namespace' => 'iv',
'@media' => 'at',
'@keyframes' => 'at',
- //'font-feature-values ' => 'at', // Not fully supported yet
+ '@-moz-keyframes' => 'at', // vendor prefixed
+ '@-webkit-keyframes' => 'at',
+ //'@font-feature-values ' => 'at', // Not fully supported yet
);
@@ -239,10 +241,14 @@ class CSSTidy
'animation-play-state' => 'CSS3.0',
'animation-delay' => 'CSS3.0',
'animation' => 'CSS3.0',
-
+ // Backgrounds
'background-size' => 'CSS3.0',
'background-origin' => 'CSS3.0',
'border-radius' => 'CSS3.0',
+ 'border-top-right-radius' => 'CSS3.0',
+ 'border-bottom-right-radius' => 'CSS3.0',
+ 'border-bottom-left-radius' => 'CSS3.0',
+ 'border-top-left-radius' => 'CSS3.0',
'border-image' => 'CSS3.0',
'border-top-left-radius' => 'CSS3.0',
'border-top-right-radius' => 'CSS3.0',
@@ -267,6 +273,15 @@ class CSSTidy
'user-select' => 'CSS3.0',
// Images
'image-rendering' => 'CSS3.0',
+ 'image-resolution' => 'CSS3.0',
+ 'image-orientation' => 'CSS3.0',
+ // Transform
+ 'transform' => 'CSS3.0',
+ 'transform-origin' => 'CSS3.0',
+ 'transform-style' => 'CSS3.0',
+ 'perspective' => 'CSS3.0',
+ 'perspective-origin' => 'CSS3.0',
+ 'backface-visibility' => 'CSS3.0',
);
/** @var \CSSTidy\Optimise */
@@ -332,11 +347,14 @@ public function parse($string)
$this->optimise = new Optimise($this->logger, $this->configuration);
$this->parsed = $parsed = new Parsed($this->configuration, $string);
- $string = str_replace("\r\n", "\n", $string) . ' ';
+ // Normalize new line characters
+ $string = str_replace(array("\r\n", "\r"), array("\n", "\n"), $string) . ' ';
+ // Initialize variables
$preserveCss = $this->configuration->getPreserveCss();
$currentComment = $currentString = $stringChar = $from = $subValue = $value = $property = $selector = $at = '';
$quotedString = $strInStr = $invalidAtRule = false;
+ $bracketCount = 0;
/*
* Possible values:
@@ -353,7 +371,7 @@ public function parse($string)
for ($i = 0, $size = strlen($string); $i < $size; $i++) {
$current = $string{$i};
- if ($current === "\n" || $current === "\r") {
+ if ($current === "\n") {
$this->logger->incrementLine();
}
@@ -416,7 +434,7 @@ public function parse($string)
$selectorSeparate[] = strlen($selector);
} elseif ($current === '\\') {
$selector .= $this->unicode($string, $i);
- } elseif ($current === '*' && @in_array($string{$i + 1}, array('.', '#', '[', ':'))) {
+ } elseif ($current === '*' && isset($string{$i + 1}) && in_array($string{$i + 1}, array('.', '#', '[', ':'))) {
// remove unnecessary universal selector, FS#147
} else {
$selector .= $current;
@@ -465,7 +483,7 @@ public function parse($string)
/* Case in-value */
case 'iv':
- $pn = (($current === "\n" || $current === "\r") && $this->propertyIsNext($string, $i + 1) || $i == strlen($string) - 1);
+ $pn = ($current === "\n" && $this->propertyIsNext($string, $i + 1) || $i == strlen($string) - 1);
if ($this->isToken($string, $i) || $pn) {
if ($current === '/' && isset($string{$i + 1}) && $string{$i + 1} === '*') {
$status = 'ic';
@@ -474,6 +492,9 @@ public function parse($string)
} elseif (($current === '"' || $current === "'" || $current === '(')) {
$currentString = $current;
$stringChar = ($current === '(') ? ')' : $current;
+ if ($current === '(') {
+ $bracketCount = 1;
+ }
$status = 'instr';
$from = 'iv';
} elseif ($current === ',') {
@@ -491,11 +512,25 @@ public function parse($string)
$status = 'is';
switch ($selector) {
- case '@charset': $parsed->charset = $subValues[0];
+ case '@charset':
+ $parsed->charset = $subValues[0];
+ if (!empty($parsed->css) || !empty($parsed->import) || !empty($parsed->namespace)) {
+ $this->logger->log("@charset must be before anything", Logger::WARNING);
+ }
break;
- case '@namespace': $parsed->namespace = implode(' ', $subValues);
+
+ case '@namespace':
+ $parsed->namespace = implode(' ', $subValues);
break;
- case '@import': $parsed->import[] = implode(' ', $subValues);
+
+ case '@import':
+ $parsed->import[] = implode(' ', $subValues);
+ if (!empty($parsed->css)) {
+ $this->logger->log("@import must be before anything selectors", Logger::WARNING);
+ } else if (!empty($at)) {
+ $this->logger->log("@import cannot be inside @media", Logger::WARNING);
+ }
+
break;
}
@@ -507,6 +542,7 @@ public function parse($string)
} elseif ($current !== '}') {
$subValue .= $current;
}
+
if (($current === '}' || $current === ';' || $pn) && !empty($selector)) {
if ($at == '' && !$preserveCss) {
$at = $parsed->newMediaSection(self::DEFAULT_AT);
@@ -578,20 +614,27 @@ public function parse($string)
/* Case in string */
case 'instr':
- if ($stringChar === ')' && ($current === '"' || $current === '\'') && !$strInStr && !self::escaped($string, $i)) {
- $strInStr = true;
- } elseif ($stringChar === ')' && ($current === '"' || $current === '\'') && $strInStr && !self::escaped($string, $i)) {
- $strInStr = false;
+ if ($stringChar === ')') {
+ if (($current === '"' || $current === '\'') && !self::escaped($string, $i)) {
+ $strInStr = !$strInStr;
+ } else if ($current === '(' && !$strInStr) {
+ ++$bracketCount;
+ } else if ($current === ')' && !$strInStr && --$bracketCount !== 0) {
+ $currentString .= ')';
+ break;
+ }
}
- $temp_add = $current; // ...and no not-escaped backslash at the previous position
- if (($current === "\n" || $current === "\r") && !($string{$i - 1} === '\\' && !self::escaped($string, $i - 1))) {
+
+ // ...and no not-escaped backslash at the previous position
+ $temp_add = $current;
+ if ($current === "\n" && !($string{$i - 1} === '\\' && !self::escaped($string, $i - 1))) {
$temp_add = "\\A ";
$this->logger->log('Fixed incorrect newline in string', Logger::WARNING);
}
// this optimisation remove space in css3 properties (see vendor-prefixed/webkit-gradient.csst)
- #if (!($stringChar === ')' && in_array($current, $GLOBALS['csstidy']['whitespace']) && !$strInStr)) {
+ //if (!($stringChar === ')' && in_array($current, self::$whitespace) && !$strInStr)) {
$currentString .= $temp_add;
- #}
+ //}
if ($current === $stringChar && !self::escaped($string, $i) && !$strInStr) {
$status = $from;
if (!preg_match('|[' . implode('', self::$whitespace) . ']|uis', $currentString) && $property !== 'content') {
@@ -600,8 +643,11 @@ public function parse($string)
// Temporarily disable this optimization to avoid problems with @charset rule, quote properties, and some attribute selectors...
// Attribute selectors fixed, added quotes to @chartset, no problems with properties detected. Enabled
$currentString = substr($currentString, 1, -1);
- } else if (isset($currentString{3}) && ($currentString[1] === '"' || $currentString[1] === '\'')) /* () */ {
- $currentString = $currentString[0] . substr($currentString, 2, -2) . substr($currentString, -1);
+ } else if (isset($currentString{3}) && ($currentString{1} === '"' || $currentString{1} === '\'')) /* () */ {
+ $inside = substr($currentString, 2, -2);
+ if (strpos($inside, '(') === false) { // if inside string contains '(', don't remove quotations mark
+ $currentString = $currentString{0} . $inside . substr($currentString, -1);
+ }
}
} else {
$quotedString = false;
@@ -714,8 +760,8 @@ public function mergeImports($string, $fileDirectory = '')
/**
* Explodes selectors
- * @access private
- * @version 1.0
+ * @param string $selector
+ * @param string $at
*/
protected function explodeSelectors($selector, $at)
{
@@ -752,9 +798,7 @@ protected function explodeSelectors($selector, $at)
* Parse unicode notations and find a replacement character
* @param string $string
* @param integer $i
- * @access private
* @return string
- * @version 1.2
*/
protected function unicode($string, &$i)
{
@@ -799,9 +843,7 @@ protected function unicode($string, &$i)
* Checks if a character is escaped (and returns true if it is)
* @param string $string
* @param integer $pos
- * @access public
* @return bool
- * @version 1.02
*/
public static function escaped($string, $pos)
{
@@ -812,8 +854,6 @@ public static function escaped($string, $pos)
* Checks if $value is !important.
* @param string $value
* @return bool
- * @access public
- * @version 1.0
*/
public static function isImportant($value)
{
@@ -867,8 +907,7 @@ protected function propertyIsNext($istring, $pos)
* Checks if there is a token at the current position
* @param string $string
* @param integer $i
- * @access public
- * @version 1.11
+ * @return bool
*/
protected function isToken($string, $i)
{
View
456 lib/Optimise.php
@@ -55,7 +55,6 @@ class Optimise
* @see compress_numbers();
* @static
* @var array
- * @version 1.0
*/
public static $colorValues = array(
'background-color' => true,
@@ -74,9 +73,8 @@ class Optimise
* @see compress_numbers()
* @static
* @var array
- * @version 1.0
*/
- public static $units = array('in','cm','mm','pt','pc','px','rem','em','%','ex','gd','vw','vh','vm','deg','grad','rad','ms','s','khz','hz');
+ public static $units = array('in','cm','mm','pt','pc','px','rem','em','%','ex','gd','vw','vh','vm','deg','grad','rad','ms','s','khz','hz','dpi','dpcm','dppx');
/**
* Properties that need a value with unit
@@ -85,7 +83,6 @@ class Optimise
* @see compress_numbers();
* @static
* @var array
- * @version 1.2
*/
public static $unitValues = array ('background', 'background-position', 'border', 'border-top', 'border-right', 'border-bottom', 'border-left', 'border-width',
'border-top-width', 'border-right-width', 'border-left-width', 'border-bottom-width', 'bottom', 'border-spacing',
@@ -96,11 +93,9 @@ class Optimise
/**
* A list of all shorthand properties that are devided into four properties and/or have four subvalues
*
- * @global array $GLOBALS['csstidy']['shorthands']
* @todo Are there new ones in CSS3?
* @see dissolve_4value_shorthands()
* @see merge_4value_shorthands()
- * @version 1.0
*/
public static $shorthands = array(
'border-color' => array('border-top-color','border-right-color','border-bottom-color','border-left-color'),
@@ -108,6 +103,7 @@ class Optimise
'border-width' => array('border-top-width','border-right-width','border-bottom-width','border-left-width'),
'margin' => array('margin-top','margin-right','margin-bottom','margin-left'),
'padding' => array('padding-top','padding-right','padding-bottom','padding-left'),
+ 'border-radius' => array('border-radius-top-left', 'border-radius-top-right', 'border-radius-bottom-right', 'border-radius-bottom-left')
);
public static $replaceColors = array(
@@ -326,13 +322,21 @@ public function value($property, $value)
{
// optimise shorthand properties
if (isset(self::$shorthands[$property])) {
- $temp = $this->shorthand($value); // FIXME - move
+ if ($property === 'border-radius') {
+ $temp = $this->borderRadiusShorthand($value);
+ } else {
+ $temp = $this->shorthand($value); // FIXME - move
+ }
if ($temp != $value) {
$this->logger->log("Optimised shorthand notation ($property): Changed '$value' to '$temp'", Logger::INFORMATION);
}
$value = $temp;
}
+ if ($property === 'background-image' && $this->configuration->getCompressColors()) {
+ $value = $this->optimizeGradients($value);
+ }
+
// Remove whitespace at ! important
$tmp = $this->compressImportant($value);
if ($value != $tmp) {
@@ -360,14 +364,12 @@ public function shorthands(Parsed $parsed, $at, $selector, $property, $value)
if ($property === 'font' && $this->configuration->getOptimiseShorthands() > Configuration::COMMON) {
$parsed->css[$at][$selector]['font'] = '';
$parsed->mergeCssBlocks($at, $selector, $this->dissolveShortFont($value));
- }
-
- if ($property === 'background' && $this->configuration->getOptimiseShorthands() > Configuration::FONT) {
+ } else if ($property === 'background' && $this->configuration->getOptimiseShorthands() > Configuration::FONT) {
$parsed->css[$at][$selector]['background'] = '';
$parsed->mergeCssBlocks($at, $selector, $this->dissolveShortBackground($value));
- }
-
- if (isset(self::$shorthands[$property])) {
+ } else if ($property === 'border-radius') {
+ return;
+ } else if (isset(self::$shorthands[$property])) {
$parsed->mergeCssBlocks($at, $selector, $this->dissolveFourValueShorthands($property, $value));
$parsed->css[$at][$selector][$property] = '';
}
@@ -430,13 +432,52 @@ public function subValue($property, $subValue)
}
/**
+ * Removes unnecessary whitespace in ! important
+ * @param string $string
+ * @return string
+ * @access public
+ * @version 1.1
+ */
+ public function compressImportant($string)
+ {
+ if (CSSTidy::isImportant($string)) {
+ $string = CSSTidy::removeImportant($string, false) . '!important';
+ }
+ return $string;
+ }
+
+ /**
+ * Optimize border-radius property
+ *
+ * @param string $value
+ * @return string
+ */
+ protected function borderRadiusShorthand($value)
+ {
+ $parts = explode('/', $value);
+
+ if (empty($parts)) { // / delimiter in string not found
+ return $value;
+ }
+
+ if (isset($parts[2])) {
+ return $value; // border-radius value can contains only two parts
+ }
+
+ foreach ($parts as &$part) {
+ $part = $this->shorthand(trim($part));
+ }
+
+ return implode('/', $parts);
+ }
+
+ /**
* Compresses shorthand values. Example: margin:1px 1px 1px 1px -> margin:1px
* @param string $value
- * @access public
* @return string
* @version 1.0
*/
- public function shorthand($value)
+ protected function shorthand($value)
{
$important = '';
if (CSSTidy::isImportant($value)) {
@@ -475,21 +516,6 @@ public function shorthand($value)
}
/**
- * Removes unnecessary whitespace in ! important
- * @param string $string
- * @return string
- * @access public
- * @version 1.1
- */
- public function compressImportant($string)
- {
- if (CSSTidy::isImportant($string)) {
- $string = CSSTidy::removeImportant($string, false) . '!important';
- }
- return $string;
- }
-
- /**
* Color compression function. Converts all rgb() values to #-values and uses the short-form if possible. Also replaces 4 color names by #-values.
* @param string $color
* @return string
@@ -518,9 +544,10 @@ protected function cutColor($color)
);
// rgb(0,0,0) -> #000000 (or #000 in this case later)
- $type = strtolower(substr($color, 0, 4));
- if ($type === 'rgb(' || $type === 'hsl(') {
- $colorTmp = substr($color, 4, strlen($color) - 5);
+ $type = strtolower(strstr($color, '(', true));
+
+ if ($type === 'rgb' || $type === 'hsl' ) {
+ $colorTmp = substr($color, 4, -1);
$colorTmp = explode(',', $colorTmp);
if (count($colorTmp) > 3) {
@@ -531,23 +558,30 @@ protected function cutColor($color)
return $color;
}
- if ($type === 'rgb(') {
- $parts = $this->convertRgbToHex($colorTmp);
+ if ($type === 'rgb') {
+ $color = $this->convertRgbToHex($colorTmp);
} else {
- $parts = $this->convertHslToHex($colorTmp);
+ $color = $this->convertHslToHex($colorTmp);
}
+ } else if ($type === 'rgba' || $type === 'hsla') {
+ $colorTmp = substr($color, 5, -1);
+ $colorTmp = explode(',', $colorTmp);
- $color = '#';
+ if (count($colorTmp) > 4) {
+ $this->logger->log(strtoupper($type) . " color value supports only four items", Logger::WARNING);
+ $colorTmp = array_slice($colorTmp, 0, 4);
+ } else if (count($colorTmp) !== 4) {
+ $this->logger->log(strtoupper($type) ." color value supports only four items", Logger::ERROR);
+ return $color;
+ }
- foreach ($parts as $part) {
- if ($part < 16) {
- $color .= '0' . dechex($part);
- } else {
- if ($part > 255) {
- $part = 255;
- }
- $color .= dechex($part);
- }
+ if ($colorTmp[3] == 0) { // no alpha is set -> convert to HEX
+ $colorTmp = array_slice($colorTmp, 0, 3);
+ $color = ($type === 'rgba' ? $this->convertRgbToHex($colorTmp) : $this->convertHslToHex($colorTmp));
+ } else if ($colorTmp[3] == 1) { // full transparency
+ $color = 'rgba(0,0,0,1)';
+ } else {
+ $color = "$type($colorTmp[0],$colorTmp[1],$colorTmp[2],{$this->compressNumber($colorTmp[3])})";
}
} else {
// Fix bad color names
@@ -586,7 +620,7 @@ protected function convertRgbToHex(array $parts)
}
}
- return $parts;
+ return $this->threeByteArrayToHex($parts);
}
/**
@@ -643,7 +677,32 @@ protected function convertHslToHex(array $parts)
$rgb = round($rgb * 255);
}
- return $output;
+ return $this->threeByteArrayToHex($output);
+ }
+
+ /**
+ * @param array $array
+ * @return string
+ */
+ protected function threeByteArrayToHex(array $array)
+ {
+ $hex = '#';
+
+ foreach ($array as $byte) {
+ if ($byte < 16) {
+ if ($byte < 0) {
+ $byte = 0;
+ }
+ $hex .= '0' . dechex($byte);
+ } else {
+ if ($byte > 255) {
+ $byte = 255;
+ }
+ $hex .= dechex($byte);
+ }
+ }
+
+ return $hex;
}
/**
@@ -707,14 +766,7 @@ protected function analyseCssNumber($string)
return $return;
}
- $return[0] = floatval($string);
- if (abs($return[0]) > 0 && abs($return[0]) < 1) {
- if ($return[0] < 0) {
- $return[0] = '-' . ltrim(substr($return[0], 1), '0');
- } else {
- $return[0] = ltrim($return[0], '0');
- }
- }
+ $return[0] = $this->compressNumber($string);
/*preg_match('~([-]?([0-9]*\.[0-9]+|[0-9]+))(.*)~si', $string, $matches);
@@ -728,6 +780,7 @@ protected function analyseCssNumber($string)
return false;
}*/
+ // TODO: Optimize
// Look for unit and split from value if exists
foreach (self::$units as $unit) {
if (!($unitInString = stristr($string, $unit))) { // mb_strpos() fails with "false"
@@ -741,22 +794,41 @@ protected function analyseCssNumber($string)
break;
}
}
+
if (!is_numeric($string)) {
return false;
}
+
return $return;
}
/**
+ * Removes 0 from decimal number between -1 - 1
+ * Example: 0.3 -> .3; -0.3 -> -.3
+ * @param string $string
+ * @return string without any non numeric character
+ */
+ protected function compressNumber($string)
+ {
+ $float = floatval($string);
+ if (abs($float) > 0 && abs($float) < 1) {
+ if ($float < 0) {
+ return '-' . ltrim(substr($float, 1), '0');
+ } else {
+ return ltrim($float, '0');
+ }
+ }
+
+ return $float;
+ }
+
+ /**
* Merges selectors with same properties. Example: a{color:red} b{color:red} -> a,b{color:red}
* Very basic and has at least one bug. Hopefully there is a replacement soon.
* @param array $array
- * @return array
- * @version 1.2
*/
- protected function mergeSelectors(&$array)
+ protected function mergeSelectors(array &$css)
{
- $css = $array;
foreach ($css as $key => $value) {
if (!isset($css[$key])) {
continue;
@@ -787,7 +859,6 @@ protected function mergeSelectors(&$array)
$css[$newSelector] = $value;
}
}
- $array = $css;
}
/**
@@ -798,7 +869,8 @@ protected function mergeSelectors(&$array)
* @version 1.4
* @param array $array
*/
- protected function discardInvalidSelectors(array &$array) {
+ protected function discardInvalidSelectors(array &$array)
+ {
foreach ($array as $selector => $decls) {
$ok = true;
$selectors = array_map('trim', explode(',', $selector));
@@ -835,7 +907,7 @@ protected function dissolveFourValueShorthands($property, $value)
$important = '';
if (CSSTidy::isImportant($value)) {
- $value = CSSTidy::removeImportant($value);
+ $value = CSSTidy::removeImportant($value, false);
$important = '!important';
}
@@ -873,51 +945,6 @@ protected function dissolveFourValueShorthands($property, $value)
}
/**
- * Explodes a string as explode() does, however, not if $sep is escaped or within a string.
- * @param string $sep seperator
- * @param string $string
- * @return array
- * @version 1.0
- */
- protected function explodeWs($sep, $string)
- {
- $status = 'st';
- $to = '';
-
- $output = array();
- $num = 0;
- $stringLength = strlen($string);
- for ($i = 0, $len = $stringLength; $i < $len; $i++) {
- switch ($status) {
- case 'st':
- if ($string{$i} == $sep && !CSSTidy::escaped($string, $i)) {
- ++$num;
- } elseif ($string{$i} === '"' || $string{$i} === '\'' || $string{$i} === '(' && !CSSTidy::escaped($string, $i)) {
- $status = 'str';
- $to = ($string{$i} === '(') ? ')' : $string{$i};
- (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
- } else {
- (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
- }
- break;
-
- case 'str':
- if ($string{$i} == $to && !CSSTidy::escaped($string, $i)) {
- $status = 'st';
- }
- (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
- break;
- }
- }
-
- if (isset($output[0])) {
- return $output;
- } else {
- return array($output);
- }
- }
-
- /**
* Merges Shorthand properties again, the opposite of dissolve_4value_shorthands()
* @param array $array
* @return array
@@ -937,7 +964,7 @@ function mergeFourValueShorthands(array $array)
$val = $array[$value[$i]];
if (CSSTidy::isImportant($val)) {
$important = '!important';
- $return[$key] .= CSSTidy::removeImportant($val) . ' ';
+ $return[$key] .= CSSTidy::removeImportant($val, false) . ' ';
} else {
$return[$key] .= $val . ' ';
}
@@ -953,8 +980,6 @@ function mergeFourValueShorthands(array $array)
* Dissolve background property
* @param string $str_value
* @return array
- * @version 1.0
- * @see merge_bg()
* @todo full CSS 3 compliance
*/
protected function dissolveShortBackground($str_value)
@@ -975,11 +1000,11 @@ protected function dissolveShortBackground($str_value)
if (CSSTidy::isImportant($str_value)) {
$important = ' !important';
- $str_value = CSSTidy::removeImportant($str_value);
+ $str_value = CSSTidy::removeImportant($str_value, false);
}
$str_value = $this->explodeWs(',', $str_value);
- for ($i = 0; $i < count($str_value); $i++) {
+ foreach ($str_value as $strVal) {
$have = array(
'clip' => false,
'pos' => false,
@@ -987,84 +1012,81 @@ protected function dissolveShortBackground($str_value)
'bg' => false,
);
- if (is_array($str_value[$i])) {
- $str_value[$i] = $str_value[$i][0];
+ if (is_array($strVal)) {
+ $strVal = $strVal[0];
}
- $str_value[$i] = $this->explodeWs(' ', trim($str_value[$i]));
+ $strVal = $this->explodeWs(' ', trim($strVal));
- for ($j = 0; $j < count($str_value[$i]); $j++) {
- if ($have['bg'] === false && (substr($str_value[$i][$j], 0, 4) === 'url(' || $str_value[$i][$j] === 'none')) {
- $return['background-image'] .= $str_value[$i][$j] . ',';
+ foreach ($strVal as $current) {
+ if ($have['bg'] === false && (substr($current, 0, 4) === 'url(' || $current === 'none')) {
+ $return['background-image'] .= $current . ',';
$have['bg'] = true;
- } elseif (in_array($str_value[$i][$j], $repeat, true)) {
- $return['background-repeat'] .= $str_value[$i][$j] . ',';
- } elseif (in_array($str_value[$i][$j], $attachment, true)) {
- $return['background-attachment'] .= $str_value[$i][$j] . ',';
- } elseif (in_array($str_value[$i][$j], $clip, true) && !$have['clip']) {
- $return['background-clip'] .= $str_value[$i][$j] . ',';
+ } elseif (in_array($current, $repeat, true)) {
+ $return['background-repeat'] .= $current . ',';
+ } elseif (in_array($current, $attachment, true)) {
+ $return['background-attachment'] .= $current . ',';
+ } elseif (in_array($current, $clip, true) && !$have['clip']) {
+ $return['background-clip'] .= $current . ',';
$have['clip'] = true;
- } elseif (in_array($str_value[$i][$j], $origin, true)) {
- $return['background-origin'] .= $str_value[$i][$j] . ',';
- } elseif ($str_value[$i][$j]{0} === '(') {
- $return['background-size'] .= substr($str_value[$i][$j], 1, -1) . ',';
- } elseif (in_array($str_value[$i][$j], $pos, true) || is_numeric($str_value[$i][$j]{0}) || $str_value[$i][$j]{0} === null || $str_value[$i][$j]{0} === '-' || $str_value[$i][$j]{0} === '.') {
- $return['background-position'] .= $str_value[$i][$j];
- if (!$have['pos'])
- $return['background-position'] .= ' '; else
- $return['background-position'].= ',';
+ } elseif (in_array($current, $origin, true)) {
+ $return['background-origin'] .= $current . ',';
+ } elseif ($current{0} === '(') {
+ $return['background-size'] .= substr($current, 1, -1) . ',';
+ } elseif (in_array($current, $pos, true) || is_numeric($current{0}) || $current{0} === null || $current{0} === '-' || $current{0} === '.') {
+ $return['background-position'] .= $current . ($have['pos'] ? ',' : ' ');
$have['pos'] = true;
- }
- elseif (!$have['color']) {
- $return['background-color'] .= $str_value[$i][$j] . ',';
+ } elseif (!$have['color']) {
+ $return['background-color'] .= $current . ',';
$have['color'] = true;
}
}
}
- foreach (self::$backgroundPropDefault as $bg_prop => $default_value) {
- if ($return[$bg_prop] !== null) {
- $return[$bg_prop] = substr($return[$bg_prop], 0, -1) . $important;
+ foreach (self::$backgroundPropDefault as $backgroundProperty => $defaultValue) {
+ if ($return[$backgroundProperty] !== null) {
+ $return[$backgroundProperty] = substr($return[$backgroundProperty], 0, -1) . $important;
} else {
- $return[$bg_prop] = $default_value . $important;
+ $return[$backgroundProperty] = $defaultValue . $important;
}
}
+
return $return;
}
/**
* Merges all background properties
- * @param array $input_css
+ * @param array $inputCss
* @return array
* @version 1.0
* @see dissolve_short_bg()
* @todo full CSS 3 compliance
*/
- protected function mergeBackground($input_css)
+ protected function mergeBackground($inputCss)
{
// Max number of background images. CSS3 not yet fully implemented
- $number_of_values = @max(count($this->explodeWs(',', $input_css['background-image'])), count($this->explodeWs(',', $input_css['background-color'])), 1);
+ $numberOfValues = @max(count($this->explodeWs(',', $inputCss['background-image'])), count($this->explodeWs(',', $inputCss['background-color'])), 1);
// Array with background images to check if BG image exists
- $bg_img_array = @$this->explodeWs(',', CSSTidy::removeImportant($input_css['background-image']));
- $new_bg_value = '';
+ $bg_img_array = @$this->explodeWs(',', CSSTidy::removeImportant($inputCss['background-image']));
+ $newBackgroundValue = '';
$important = '';
// if background properties is here and not empty, don't try anything
- if (isset($input_css['background']) && $input_css['background']) {
- return $input_css;
+ if (isset($inputCss['background']) && $inputCss['background']) {
+ return $inputCss;
}
- for ($i = 0; $i < $number_of_values; $i++) {
- foreach (self::$backgroundPropDefault as $bg_property => $default_value) {
+ for ($i = 0; $i < $numberOfValues; $i++) {
+ foreach (self::$backgroundPropDefault as $bg_property => $defaultValue) {
// Skip if property does not exist
- if (!isset($input_css[$bg_property])) {
+ if (!isset($inputCss[$bg_property])) {
continue;
}
- $cur_value = $input_css[$bg_property];
+ $currentValue = $inputCss[$bg_property];
// skip all optimisation if gradient() somewhere
- if (stripos($cur_value, "gradient(") !== false) {
- return $input_css;
+ if (stripos($currentValue, "gradient(") !== false) {
+ return $inputCss;
}
// Skip some properties if there is no background image
@@ -1075,44 +1097,46 @@ protected function mergeBackground($input_css)
}
// Remove !important
- if (CSSTidy::isImportant($cur_value)) {
+ if (CSSTidy::isImportant($currentValue)) {
$important = ' !important';
- $cur_value = CSSTidy::removeImportant($cur_value);
+ $currentValue = CSSTidy::removeImportant($currentValue, false);
}
// Do not add default values
- if ($cur_value === $default_value) {
+ if ($currentValue === $defaultValue) {
continue;
}
- $temp = $this->explodeWs(',', $cur_value);
+ $temp = $this->explodeWs(',', $currentValue);
if (isset($temp[$i])) {
if ($bg_property === 'background-size') {
- $new_bg_value .= '(' . $temp[$i] . ') ';
+ $newBackgroundValue .= '(' . $temp[$i] . ') ';
} else {
- $new_bg_value .= $temp[$i] . ' ';
+ $newBackgroundValue .= $temp[$i] . ' ';
}
}
}
- $new_bg_value = trim($new_bg_value);
- if ($i != $number_of_values - 1)
- $new_bg_value .= ',';
+ $newBackgroundValue = trim($newBackgroundValue);
+ if ($i != $numberOfValues - 1) {
+ $newBackgroundValue .= ',';
+ }
}
// Delete all background-properties
- foreach (self::$backgroundPropDefault as $bg_property => $default_value) {
- unset($input_css[$bg_property]);
+ foreach (self::$backgroundPropDefault as $bg_property => $foo) {
+ unset($inputCss[$bg_property]);
}
// Add new background property
- if ($new_bg_value !== '')
- $input_css['background'] = $new_bg_value . $important;
- elseif(isset ($input_css['background']))
- $input_css['background'] = 'none';
+ if ($newBackgroundValue !== '') {
+ $inputCss['background'] = $newBackgroundValue . $important;
+ } elseif (isset($inputCss['background'])) {
+ $inputCss['background'] = 'none';
+ }
- return $input_css;
+ return $inputCss;
}
/**
@@ -1278,4 +1302,96 @@ protected function mergeFont($inputCss)
return $inputCss;
}
+
+ /**
+ * Compress color inside gradient definition
+ * @param string $string
+ * @return string
+ */
+ protected function optimizeGradients($string)
+ {
+ /*
+ * Gradinet functions and color start from
+ * -webkit-gradient syntax is not supported
+ */
+ static $supportedGradients = array(
+ '-webkit-repeating-linear-gradient' => 1,
+ '-moz-repeating-linear-gradient' => 1,
+ '-o-repeating-linear-gradient' => 1,
+ 'repeating-linear-gradient' => 1,
+ '-webkit-linear-gradient' => 1,
+ '-moz-linear-gradient' => 1,
+ '-o-linear-gradient' => 1,
+ 'linear-gradient' => 1,
+ '-webkit-repeating-radial-gradient' => 2,
+ '-moz-repeating-radial-gradient' => 2,
+ '-o-repeating-radial-gradient' => 2,
+ 'repeating-radial-gradient' => 2,
+ '-webkit-radial-gradient' => 2,
+ '-moz-radial-gradient' => 2,
+ '-o-radial-gradient' => 2,
+ 'radial-gradient' => 2,
+ );
+
+ $type = strstr($string, '(', true);
+
+ if ($type === false || !isset($supportedGradients[$type])) {
+ return $string; // value is not gradient or unsupported type
+ }
+
+ $string = substr($string, strlen($type) + 1, -1); // Remove linear-gradient()
+ $parts = $this->explodeWs(',', $string);
+
+ $start = $supportedGradients[$type];
+ foreach ($parts as $i => &$part) {
+ if ($i < $start) {
+ continue;
+ }
+
+ $colorAndLength = $this->explodeWs(' ', $part);
+ $colorAndLength[0] = $this->cutColor($colorAndLength[0]);
+ $part = implode(' ', $colorAndLength);
+ }
+
+ return "$type(" . implode(',', $parts) . ')';
+ }
+
+ /**
+ * Explodes a string as explode() does, however, not if $sep is escaped or within a string.
+ * @param string $sep separator
+ * @param string $string
+ * @return array
+ */
+ protected function explodeWs($sep, $string)
+ {
+ if ($string === '' || $string === $sep) {
+ return array();
+ }
+
+ $insideString = false;
+ $to = '';
+ $output = array(0 => '');
+ $num = 0;
+
+ for ($i = 0, $len = strlen($string); $i < $len; $i++) {
+ if ($insideString) {
+ if ($string{$i} === $to && !CSSTidy::escaped($string, $i)) {
+ $insideString = false;
+ }
+ } else {
+ if ($string{$i} === $sep && !CSSTidy::escaped($string, $i)) {
+ ++$num;
+ $output[$num] = '';
+ continue;
+ } else if ($string{$i} === '"' || $string{$i} === '\'' || $string{$i} === '(' && !CSSTidy::escaped($string, $i)) {
+ $insideString = true;
+ $to = ($string{$i} === '(') ? ')' : $string{$i};
+ }
+ }
+
+ $output[$num] .= $string{$i};
+ }
+
+ return $output;
+ }
}
View
20 testing/unit-tests/csst/shorthands/border-radius.csst
@@ -0,0 +1,20 @@
+--TEST--
+Test padding optimization
+--CSS--
+a {
+ border-radius: 0 0 10px 0;
+}
+b {
+ border-radius: 0 10px 0 0 / 1px 1px 1px 1px;
+}
+--EXPECT--
+array (
+ 'a' =>
+ array (
+ 'border-radius' => '0 0 10px',
+ ),
+ 'b' =>
+ array (
+ 'border-radius' => '0 10px 0 0/1px',
+ ),
+)
View
2  testing/unit-tests/csst/shorthands/bugshorthand3.csst
@@ -42,7 +42,7 @@ array (
array (
'background-color' => '#777',
'background' => '-webkit-gradient(linear,left top,left bottom,from(#999999),to(#666666))',
- 'background-image' => '-moz-linear-gradient(top,#999999,#666666)',
+ 'background-image' => '-moz-linear-gradient(top,#999,#666)',
),
'.sans' =>
array (
View
11 testing/unit-tests/csst/special/at-rules.csst
@@ -8,7 +8,10 @@ Import
@import 'bluish.css';
@import "bluish.css";
@import url("bluish.css") projection, tv;
-@import "bluish.css" projection, tv;
+@import "bluish.css" screen and (orientation:landscape);
+@page :pseudo-class {
+ margin:2in;
+}
--PRINT--
@charset "ISO-8859-1";
@import"bluish.css";
@@ -17,4 +20,8 @@ Import
@import"bluish.css";
@import"bluish.css";
@import"bluish.css" projection, tv;
-@import"bluish.css" projection, tv;
+@import"bluish.css" screen and (orientation:landscape);
+
+@page :pseudo-class {
+margin:2in
+}
View
2  testing/unit-tests/csst/special/complex-gradient.csst
@@ -17,7 +17,7 @@ array (
array (
'background-color' => '#777',
'background' => '-webkit-gradient(linear,left top,left bottom,from(#999999),to(#666666))',
- 'background-image' => '-moz-linear-gradient(top,#999999,#666666)',
+ 'background-image' => '-moz-linear-gradient(top,#999,#666)',
'filter' => 'progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=\'#999999\',endColorstr=\'#666666\')',
'filter ' => 'progid:DXImageTransform.Microsoft.Shadow(color=#666666,direction=146,Strength=5)',
'-ms-filter' => '"progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=\'#999999\',endColorstr=\'#666666\')"',
View
13 testing/unit-tests/csst/special/css3-images.csst
@@ -0,0 +1,13 @@
+--TEST--
+multiples cursor rules
+--CSS--
+a {background-image: image("wavy.svg", 'wavy.png' , "wavy.gif", rgba(0,0,255,0.5));}
+sprites {background-image: url('logos.svg#svgView(viewBox(0,0,200,200))')}
+--PRINT--
+a {
+background-image:image("wavy.svg",'wavy.png',"wavy.gif",rgba(0,0,255,0.5))
+}
+
+sprites {
+background-image:url('logos.svg#svgView(viewBox(0,0,200,200))')
+}
View
13 testing/unit-tests/csst/special/css3-multiple-background.csst
@@ -0,0 +1,13 @@
+--TEST--
+multiples cursor rules
+--CSS--
+a {background: url("http://demos.hacks.mozilla.org/(openweb/resources/images/logos/firefox-48.png"),
+ -moz-linear-gradient(left, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)),
+ url(http://demos.hacks.mozilla.org/openweb/resources/images/patterns/flowers-pattern.jpg)}
+--EXPECT--
+array (
+'a' =>
+ array (
+ 'background' => 'url("http://demos.hacks.mozilla.org/(openweb/resources/images/logos/firefox-48.png"),-moz-linear-gradient(left,rgba(255,255,255,0),rgba(255,255,255,1)),url(http://demos.hacks.mozilla.org/openweb/resources/images/patterns/flowers-pattern.jpg)',
+ )
+)
View
12 testing/unit-tests/csst/special/media-queries.csst
@@ -0,0 +1,12 @@
+--TEST--
+Media queries
+--CSS--
+@media handheld and (grid) and (device-max-height: 7em) {
+ a {color:red}
+}
+--PRINT--
+@media handheld and (grid) and (device-max-height: 7em) {
+a {
+color:red
+}
+}
View
4 testing/unit-tests/csst/values/colors.csst
@@ -12,6 +12,8 @@ a {
hsl:hsl(25, 100%, 50%);
hsl2:hsl(210, 100%, 50%);
hsl3:hsl(270, 25%, 25%);
+ rgba:rgba(200,200,200,0.5);
+ hsla:hsla(120,50%,50%,0.3);
}
--EXPECT--
array (
@@ -27,5 +29,7 @@ array (
'hsl' => '#ff6a00',
'hsl2' => '#0080ff',
'hsl3' => '#403050',
+ 'rgba' => 'rgba(200,200,200,.5)',
+ 'hsla' => 'hsla(120,50%,50%,.3)',
),
)
View
13 testing/unit-tests/csst/values/transform.csst
@@ -0,0 +1,13 @@
+--TEST--
+Test color optimisation
+--CSS--
+div {
+ transform: translate(80px, 80px) scale(1.5, 1.5) rotate(45deg);
+}
+--EXPECT--
+array (
+ 'div' =>
+ array (
+ 'transform' => 'translate(80px,80px) scale(1.5,1.5) rotate(45deg)'
+ ),
+)
View
17 testing/unit-tests/csst/vendor-prefixed/ie-filter.csst
@@ -0,0 +1,17 @@
+--TEST--
+Internet Explorer hacks
+--CSS--
+a {filter:
+ progid:DXImageTransform.Microsoft.MotionBlur(strength=13, direction=310)
+ progid:DXImageTransform.Microsoft.Blur(pixelradius=2)
+ progid:DXImageTransform.Microsoft.Wheel(duration=3);
+}
+--PRINT--
+a {
+filter:progid:DXImageTransform.Microsoft.MotionBlur(strength=13,direction=310) progid:DXImageTransform.Microsoft.Blur(pixelradius=2) progid:DXImageTransform.Microsoft.Wheel(duration=3)
+}
+--SETTINGS--
+discard_invalid_properties=false
+optimise_shorthands=0
+css_level='CSS2.1'
+sort_properties=true
View
24 testing/unit-tests/csst/vendor-prefixed/mozilla-gradient.csst
@@ -0,0 +1,24 @@
+--TEST--
+-webkit-gradient properties issue/3
+--CSS--
+.linear {
+ background-image: -moz-linear-gradient(top, hsl(0, 80%, 70%), #BADA55);
+}
+.radial {
+ background-image: -moz-radial-gradient(45px 45px, ellipse farthest-corner, aqua 0%, rgba(0, 0, 255, 0) 100%, blue 95%);
+}
+--EXPECT--
+array (
+ '.linear' =>
+ array (
+ 'background-image' => '-moz-linear-gradient(top,#f07575,#bada55)',
+ ),
+ '.radial' =>
+ array (
+ 'background-image' => '-moz-radial-gradient(45px 45px,ellipse farthest-corner,aqua 0%,#00f 100%,blue 95%)',
+ ),
+)
+--SETTINGS--
+discard_invalid_properties=false
+optimise_shorthands=0
+css_level='CSS2.1'

No commit comments for this range

Something went wrong with that request. Please try again.