diff --git a/lib/Less/Functions.php b/lib/Less/Functions.php index 1f7a2231..a32ae17d 100644 --- a/lib/Less/Functions.php +++ b/lib/Less/Functions.php @@ -170,7 +170,7 @@ public function hue( $color = null ) { } $c = $color->toHSL(); - return new Less_Tree_Dimension( Less_Parser::round( $c['h'] ) ); + return new Less_Tree_Dimension( $c['h'] ); } public function saturation( $color = null ) { @@ -179,7 +179,7 @@ public function saturation( $color = null ) { } $c = $color->toHSL(); - return new Less_Tree_Dimension( Less_Parser::round( $c['s'] * 100 ), '%' ); + return new Less_Tree_Dimension( $c['s'] * 100, '%' ); } public function lightness( $color = null ) { @@ -188,7 +188,7 @@ public function lightness( $color = null ) { } $c = $color->toHSL(); - return new Less_Tree_Dimension( Less_Parser::round( $c['l'] * 100 ), '%' ); + return new Less_Tree_Dimension( $c['l'] * 100, '%' ); } public function hsvhue( $color = null ) { @@ -197,7 +197,7 @@ public function hsvhue( $color = null ) { } $hsv = $color->toHSV(); - return new Less_Tree_Dimension( Less_Parser::round( $hsv['h'] ) ); + return new Less_Tree_Dimension( $hsv['h'] ); } public function hsvsaturation( $color = null ) { @@ -206,7 +206,7 @@ public function hsvsaturation( $color = null ) { } $hsv = $color->toHSV(); - return new Less_Tree_Dimension( Less_Parser::round( $hsv['s'] * 100 ), '%' ); + return new Less_Tree_Dimension( $hsv['s'] * 100, '%' ); } public function hsvvalue( $color = null ) { @@ -215,7 +215,7 @@ public function hsvvalue( $color = null ) { } $hsv = $color->toHSV(); - return new Less_Tree_Dimension( Less_Parser::round( $hsv['v'] * 100 ), '%' ); + return new Less_Tree_Dimension( $hsv['v'] * 100, '%' ); } public function red( $color = null ) { @@ -256,7 +256,7 @@ public function luma( $color = null ) { throw new Less_Exception_Compiler( 'The first argument to luma must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) ); } - return new Less_Tree_Dimension( Less_Parser::round( $color->luma() * $color->alpha * 100 ), '%' ); + return new Less_Tree_Dimension( $color->luma() * $color->alpha * 100, '%' ); } public function luminance( $color = null ) { @@ -269,10 +269,10 @@ public function luminance( $color = null ) { + ( 0.7152 * $color->rgb[1] / 255 ) + ( 0.0722 * $color->rgb[2] / 255 ); - return new Less_Tree_Dimension( Less_Parser::round( $luminance * $color->alpha * 100 ), '%' ); + return new Less_Tree_Dimension( $luminance * $color->alpha * 100, '%' ); } - public function saturate( $color = null, $amount = null ) { + public function saturate( $color = null, $amount = null, $method = null ) { // filter: saturate(3.2); // should be kept as is, so check for color if ( $color instanceof Less_Tree_Dimension ) { @@ -288,8 +288,11 @@ public function saturate( $color = null, $amount = null ) { $hsl = $color->toHSL(); - $hsl['s'] += $amount->value / 100; - $hsl['s'] = self::clamp( $hsl['s'] ); + if ( isset( $method ) && $method->value === "relative" ) { + $hsl['s'] += $hsl['s'] * $amount->value / 100; + } else { + $hsl['s'] += $amount->value / 100; + } $hsl['s'] = self::clamp( $hsl['s'] ); return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] ); } @@ -298,7 +301,7 @@ public function saturate( $color = null, $amount = null ) { * @param Less_Tree_Color|null $color * @param Less_Tree_Dimension|null $amount */ - public function desaturate( $color = null, $amount = null ) { + public function desaturate( $color = null, $amount = null, $method = null ) { if ( !$color instanceof Less_Tree_Color ) { throw new Less_Exception_Compiler( 'The first argument to desaturate must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) ); } @@ -307,13 +310,19 @@ public function desaturate( $color = null, $amount = null ) { } $hsl = $color->toHSL(); - $hsl['s'] -= $amount->value / 100; + + if ( isset( $method ) && $method->value === "relative" ) { + $hsl['s'] -= $hsl['s'] * $amount->value / 100; + } else { + $hsl['s'] -= $amount->value / 100; + } + $hsl['s'] = self::clamp( $hsl['s'] ); return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] ); } - public function lighten( $color = null, $amount = null ) { + public function lighten( $color = null, $amount = null, $method = null ) { if ( !$color instanceof Less_Tree_Color ) { throw new Less_Exception_Compiler( 'The first argument to lighten must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) ); } @@ -323,13 +332,18 @@ public function lighten( $color = null, $amount = null ) { $hsl = $color->toHSL(); - $hsl['l'] += $amount->value / 100; + if ( isset( $method ) && $method->value === "relative" ) { + $hsl['l'] += $hsl['l'] * $amount->value / 100; + } else { + $hsl['l'] += $amount->value / 100; + } + $hsl['l'] = self::clamp( $hsl['l'] ); return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] ); } - public function darken( $color = null, $amount = null ) { + public function darken( $color = null, $amount = null, $method = null ) { if ( !$color instanceof Less_Tree_Color ) { throw new Less_Exception_Compiler( 'The first argument to darken must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) ); } @@ -338,13 +352,17 @@ public function darken( $color = null, $amount = null ) { } $hsl = $color->toHSL(); - $hsl['l'] -= $amount->value / 100; + if ( isset( $method ) && $method->value === "relative" ) { + $hsl['l'] -= $hsl['l'] * $amount->value / 100; + } else { + $hsl['l'] -= $amount->value / 100; + } $hsl['l'] = self::clamp( $hsl['l'] ); return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] ); } - public function fadein( $color = null, $amount = null ) { + public function fadein( $color = null, $amount = null, $method = null ) { if ( !$color instanceof Less_Tree_Color ) { throw new Less_Exception_Compiler( 'The first argument to fadein must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) ); } @@ -353,12 +371,18 @@ public function fadein( $color = null, $amount = null ) { } $hsl = $color->toHSL(); - $hsl['a'] += $amount->value / 100; + + if ( isset( $method ) && $method->value === "relative" ) { + $hsl['a'] += $hsl['a'] * $amount->value / 100; + } else { + $hsl['a'] += $amount->value / 100; + } + $hsl['a'] = self::clamp( $hsl['a'] ); return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] ); } - public function fadeout( $color = null, $amount = null ) { + public function fadeout( $color = null, $amount = null, $method = null ) { if ( !$color instanceof Less_Tree_Color ) { throw new Less_Exception_Compiler( 'The first argument to fadeout must be a color' . ( $color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '' ) ); } @@ -367,7 +391,13 @@ public function fadeout( $color = null, $amount = null ) { } $hsl = $color->toHSL(); - $hsl['a'] -= $amount->value / 100; + + if ( isset( $method ) && $method->value === "relative" ) { + $hsl['a'] -= $hsl['a'] * $amount->value / 100; + } else { + $hsl['a'] -= $amount->value / 100; + } + $hsl['a'] = self::clamp( $hsl['a'] ); return $this->hsla( $hsl['h'], $hsl['s'], $hsl['l'], $hsl['a'] ); } @@ -514,10 +544,16 @@ public function replace( $string, $pattern, $replacement, $flags = null ) { if ( $flags && $flags->value ) { $expr .= self::replace_flags( $flags->value ); } + $replacement = ( $replacement instanceof Less_Tree_Quoted ) ? + $replacement->value : $replacement->toCSS(); - $result = preg_replace( $expr, $replacement->value, $result ); + $result = preg_replace( $expr, $replacement, $result, 1 ); - if ( property_exists( $string, 'quote' ) ) { + if ( $flags && $flags->value && preg_match( '/g/', $flags->value ) ) { + $result = preg_replace( $expr, $replacement, $result ); + } + + if ( $string instanceof Less_Tree_Quoted ) { return new Less_Tree_Quoted( $string->quote, $result, $string->escaped ); } return new Less_Tree_Quoted( '', $result ); @@ -533,14 +569,19 @@ public function _percent( $string, ...$args ) { foreach ( $args as $arg ) { if ( preg_match( '/%[sda]/i', $result, $token ) ) { $token = $token[0]; - $value = stristr( $token, 's' ) ? $arg->value : $arg->toCSS(); + $value = ( ( $arg instanceof Less_Tree_Quoted ) && + stristr( $token, 's' ) ? $arg->value : $arg->toCSS() ); + $value = preg_match( '/[A-Z]$/', $token ) ? urlencode( $value ) : $value; $result = preg_replace( '/%[sda]/i', $value, $result, 1 ); } } $result = str_replace( '%%', '%', $result ); - return new Less_Tree_Quoted( $string->quote, $result, $string->escaped ); + if ( $string instanceof Less_Tree_Quoted ) { + return new Less_Tree_Quoted( $string->quote, $result, $string->escaped ); + } + return new Less_Tree_Quoted( '', $result ); } public function unit( $val, $unit = null ) { @@ -781,6 +822,10 @@ public function color( $c ) { throw new Less_Exception_Compiler( "argument must be a color keyword or 3/6 digit hex e.g. #FFF" ); } + public function isruleset( $n ) { + return new Less_Tree_Keyword( $n instanceof Less_Tree_DetachedRuleset ? 'true' : 'false' ); + } + public function iscolor( $n ) { return new Less_Tree_Keyword( $n instanceof Less_Tree_Color ? 'true' : 'false' ); } diff --git a/lib/Less/Parser.php b/lib/Less/Parser.php index 621c6091..f6d98098 100644 --- a/lib/Less/Parser.php +++ b/lib/Less/Parser.php @@ -1241,7 +1241,7 @@ private function parseEntitiesDimension() { return; } - $value = $this->MatchReg( '/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/' ); + $value = $this->MatchReg( '/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/i' ); if ( $value ) { if ( isset( $value[2] ) ) { return new Less_Tree_Dimension( $value[1], $value[2] ); diff --git a/lib/Less/Tree/Call.php b/lib/Less/Tree/Call.php index ab3d3a7c..0581d5da 100644 --- a/lib/Less/Tree/Call.php +++ b/lib/Less/Tree/Call.php @@ -46,6 +46,17 @@ public function compile( $env = null ) { $args[] = $a->compile( $env ); } + foreach ( $args as $key => $arg ) { + if ( $arg instanceof Less_Tree_Expression ) { + $arg->throwAwayComments(); + + if ( count( $arg->value ) === 1 ) { + $subNode = $arg->value[0]; + array_splice( $args, $key, 1, [ $subNode ] ); + } + } + } + Less_Environment::$mathOn = $currentMathContext; $nameLC = strtolower( $this->name ); diff --git a/lib/Less/Tree/Quoted.php b/lib/Less/Tree/Quoted.php index e9d199e2..2b67e868 100644 --- a/lib/Less/Tree/Quoted.php +++ b/lib/Less/Tree/Quoted.php @@ -13,7 +13,7 @@ class Less_Tree_Quoted extends Less_Tree implements Less_Tree_HasValueProperty { /** * @param string $str */ - public function __construct( $str, $content = '', $escaped = false, $index = false, $currentFileInfo = null ) { + public function __construct( $str, $content = '', $escaped = true, $index = false, $currentFileInfo = null ) { $this->escaped = $escaped; $this->value = $content; if ( $str ) { diff --git a/lib/Less/Tree/Unit.php b/lib/Less/Tree/Unit.php index c4e8fcf1..983e4546 100644 --- a/lib/Less/Tree/Unit.php +++ b/lib/Less/Tree/Unit.php @@ -50,7 +50,7 @@ public function compare( $other ) { } public function is( $unitString ) { - return $this->toString() === $unitString; + return strtoupper( $this->toString() ) === strtoupper( $unitString ); } public function isLength() { diff --git a/test/Fixtures/bug-reports/css/210.css b/test/Fixtures/bug-reports/css/210.css index 4409eed7..cc0b757a 100644 --- a/test/Fixtures/bug-reports/css/210.css +++ b/test/Fixtures/bug-reports/css/210.css @@ -4,14 +4,14 @@ body .color-channels { --saturation: 50%; --lightness: 40%; --hsvhue: 210; - --hsvsaturation: 67%; + --hsvsaturation: 66.66666667%; --hsvvalue: 60%; --red: 51; --green: 102; --blue: 153; --alpha: 0.9; - --luma: 11%; - --luminance: 33%; + --luma: 11.25581169%; + --luminance: 33.4728%; } body .color-definitions .rgb { color: #336699; @@ -29,10 +29,10 @@ body .color-definitions .hsla { color: rgba(51, 102, 153, 0.9); } body .color-definitions .hsv { - color: #326699; + color: #336699; } body .color-definitions .hsva { - color: rgba(50, 102, 153, 0.9); + color: rgba(51, 102, 153, 0.9); } body .css-filters .saturate { color: saturate(200%); diff --git a/test/Fixtures/lessjs-2.5.3/expected/functions.css b/test/Fixtures/lessjs-2.5.3/expected/functions.css index ab5df5bb..0b9b344c 100644 --- a/test/Fixtures/lessjs-2.5.3/expected/functions.css +++ b/test/Fixtures/lessjs-2.5.3/expected/functions.css @@ -27,7 +27,7 @@ luma-green: 71.52%; luma-blue: 7.22%; luma-yellow: 92.78%; - luma-cyan: 78.74%; + luma-cyan: 78.73999999999999%; luma-differs-from-luminance: 23.89833349%; luminance-white: 100%; luminance-black: 0%; @@ -96,12 +96,12 @@ pi: 3.14159265; mod: 2m; abs: 4%; - tan: 0.90040404; - sin: 0.17364818; + tan: 0.90040415; + sin: 0.17364819; cos: 0.84385396; atan: 0.1rad; atan: 34deg; - atan: 45deg; + atan: 44.9999964deg; pow: 64px; pow: 64; pow: 27; diff --git a/test/phpunit/FixturesTest.php b/test/phpunit/FixturesTest.php index 6e727b69..704bbbee 100644 --- a/test/phpunit/FixturesTest.php +++ b/test/phpunit/FixturesTest.php @@ -25,9 +25,6 @@ class phpunit_FixturesTest extends phpunit_bootstrap { // If T352866 is fixed, this is should also be resolved 'variables' => true, - // Temporary disabled; Bug logged here T353289 - 'functions' => true, - // Temporary disabled; Bug logged here T352859 'selectors' => true, @@ -122,6 +119,9 @@ public function testFixture( $cssFile, $lessFile ) { // Check with standard parser $parser = new Less_Parser(); try { + $parser->registerFunction( '_color', [ __CLASS__, 'FnColor' ] ); + $parser->registerFunction( 'add', [ __CLASS__, 'FnAdd' ] ); + $parser->registerFunction( 'increment', [ __CLASS__, 'FnIncrement' ] ); $parser->parseFile( $lessFile ); $css = $parser->getCss(); } catch ( Less_Exception_Parser $e ) { @@ -131,7 +131,10 @@ public function testFixture( $cssFile, $lessFile ) { $this->assertSame( $expectedCSS, $css, "Standard compiler" ); // Check with cache - $options = [ 'cache_dir' => $this->cache_dir ]; + $options = [ 'cache_dir' => $this->cache_dir, + 'functions' => [ '_color' => [ __CLASS__, 'FnColor' ], + 'add' => [ __CLASS__, 'FnAdd' ], + 'increment' => [ __CLASS__, 'FnIncrement' ] ] ]; $files = [ $lessFile => '' ]; try { $cacheFile = Less_Cache::Regen( $files, $options ); @@ -152,4 +155,19 @@ public function testFixture( $cssFile, $lessFile ) { $css = trim( $css ); $this->assertEquals( $expectedCSS, $css, "Using cache" ); } + + public static function FnColor( $str ) { + if ( $str->value === "evil red" ) { + return new Less_Tree_Color( "600" ); + } + } + + public static function FnAdd( $a, $b ) { + return new Less_Tree_Dimension( $a->value + $b->value ); + } + + public static function FnIncrement( $a ) { + return new Less_Tree_Dimension( $a->value + 1 ); + } + }