From 282847e6d3c4328138f4d6a056b759d38af68e9a Mon Sep 17 00:00:00 2001 From: Timo Tijhof Date: Thu, 25 Sep 2014 17:11:58 -0700 Subject: [PATCH] Use CSSJanus from upstream (v1.1.0) Remove the local copy and include upstream instead. Remove local unit tests as it's now a proper upstream lib that is tested by uptream. Release: https://github.com/cssjanus/php-cssjanus/releases/tag/v1.1.0 Changes: Features * Support "/*!" syntax for @noflip. * Flip border-style. Bug fixes * Improve flipping of background-position. * Restrict four_notation_quantity to margin, padding and border-width. * Flip two values in border-radius rule (in addition to four values). Change-Id: Ieb6e179d6163f99a9c98fd99c2277d25135871fe --- RELEASE-NOTES-1.24 | 1 + includes/libs/CSSJanus.php | 251 ++++---- tests/phpunit/includes/libs/CSSJanusTest.php | 633 ------------------- 3 files changed, 139 insertions(+), 746 deletions(-) delete mode 100644 tests/phpunit/includes/libs/CSSJanusTest.php diff --git a/RELEASE-NOTES-1.24 b/RELEASE-NOTES-1.24 index 43e0ae51f3..9171814376 100644 --- a/RELEASE-NOTES-1.24 +++ b/RELEASE-NOTES-1.24 @@ -192,6 +192,7 @@ production. settings. * (bug 69418) A MultiConfig implementation was added that supports fallback to multiple Config instances. +* Update CSSJanus to v1.1.0. === Bug fixes in 1.24 === * (bug 50572) MediaWiki:Blockip should support gender diff --git a/includes/libs/CSSJanus.php b/includes/libs/CSSJanus.php index 4cfc9b7b71..e96baec11d 100644 --- a/includes/libs/CSSJanus.php +++ b/includes/libs/CSSJanus.php @@ -55,7 +55,7 @@ class CSSJanus { 'lookahead_not_letter' => '(?![a-zA-Z])', 'lookbehind_not_letter' => '(? '[^\}]*?', - 'noflip_annotation' => '\/\*\s*@noflip\s*\*\/', + 'noflip_annotation' => '\/\*\!?\s*@noflip\s*\*\/', 'noflip_single' => null, 'noflip_class' => null, 'comment' => '/\/\*[^*]*\*+([^\/*][^*]*\*+)*\//', @@ -83,7 +83,7 @@ class CSSJanus { * Build patterns we can't define above because they depend on other patterns. */ private static function buildPatterns() { - if ( !is_null( self::$patterns['escape'] ) ) { + if (!is_null(self::$patterns['escape'])) { // Patterns have already been built return; } @@ -113,17 +113,17 @@ class CSSJanus { $patterns['rtl_in_url'] = "/{$patterns['lookbehind_not_letter']}(rtl){$patterns['lookahead_for_closing_paren']}/i"; $patterns['cursor_east'] = "/{$patterns['lookbehind_not_letter']}([ns]?)e-resize/"; $patterns['cursor_west'] = "/{$patterns['lookbehind_not_letter']}([ns]?)w-resize/"; - $patterns['four_notation_quantity'] = "/(:\s*){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s*[;}])/i"; - $patterns['four_notation_color'] = "/(-color\s*:\s*){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s*[;}])/i"; - $patterns['border_radius'] = "/(border-radius\s*:\s*){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s*[;}])/i"; + $patterns['four_notation_quantity_props'] = "((?:margin|padding|border-width)\s*:\s*)"; + $patterns['four_notation_quantity'] = "/{$patterns['four_notation_quantity_props']}{$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s*[;}])/i"; + $patterns['four_notation_color'] = "/((?:-color|border-style)\s*:\s*){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s*[;}])/i"; + $patterns['border_radius'] = "/(border-radius\s*:\s*)([^;}]*)/"; $patterns['box_shadow'] = "/(box-shadow\s*:\s*(?:inset\s*)?){$patterns['possibly_negative_quantity']}/i"; $patterns['text_shadow1'] = "/(text-shadow\s*:\s*){$patterns['color']}(\s*){$patterns['possibly_negative_quantity']}/i"; $patterns['text_shadow2'] = "/(text-shadow\s*:\s*){$patterns['possibly_negative_quantity']}/i"; - // The two regexes below are parenthesized differently then in the original implementation to make the - // callback's job more straightforward - $patterns['bg_horizontal_percentage'] = "/(background(?:-position)?\s*:\s*[^%]*?)(-?{$patterns['num']})(%\s*(?:{$patterns['quantity']}|{$patterns['ident']}))/"; - $patterns['bg_horizontal_percentage_x'] = "/(background-position-x\s*:\s*)(-?{$patterns['num']})(%)/"; + $patterns['bg_horizontal_percentage'] = "/(background(?:-position)?\s*:\s*(?:[^:;}\s]+\s+)*?)({$patterns['quantity']})/i"; + $patterns['bg_horizontal_percentage_x'] = "/(background-position-x\s*:\s*)(-?{$patterns['num']}%)/i"; // @codingStandardsIgnoreEnd + } /** @@ -133,46 +133,46 @@ class CSSJanus { * @param $swapLeftRightInURL Boolean: If true, swap 'left' and 'right' in URLs * @return string Transformed stylesheet */ - public static function transform( $css, $swapLtrRtlInURL = false, $swapLeftRightInURL = false ) { + public static function transform($css, $swapLtrRtlInURL = false, $swapLeftRightInURL = false) { // We wrap tokens in ` , not ~ like the original implementation does. // This was done because ` is not a legal character in CSS and can only // occur in URLs, where we escape it to %60 before inserting our tokens. - $css = str_replace( '`', '%60', $css ); + $css = str_replace('`', '%60', $css); self::buildPatterns(); // Tokenize single line rules with /* @noflip */ - $noFlipSingle = new CSSJanusTokenizer( self::$patterns['noflip_single'], '`NOFLIP_SINGLE`' ); - $css = $noFlipSingle->tokenize( $css ); + $noFlipSingle = new CSSJanusTokenizer(self::$patterns['noflip_single'], '`NOFLIP_SINGLE`'); + $css = $noFlipSingle->tokenize($css); // Tokenize class rules with /* @noflip */ - $noFlipClass = new CSSJanusTokenizer( self::$patterns['noflip_class'], '`NOFLIP_CLASS`' ); - $css = $noFlipClass->tokenize( $css ); + $noFlipClass = new CSSJanusTokenizer(self::$patterns['noflip_class'], '`NOFLIP_CLASS`'); + $css = $noFlipClass->tokenize($css); // Tokenize comments - $comments = new CSSJanusTokenizer( self::$patterns['comment'], '`C`' ); - $css = $comments->tokenize( $css ); + $comments = new CSSJanusTokenizer(self::$patterns['comment'], '`C`'); + $css = $comments->tokenize($css); // LTR->RTL fixes start here - $css = self::fixDirection( $css ); - if ( $swapLtrRtlInURL ) { - $css = self::fixLtrRtlInURL( $css ); + $css = self::fixDirection($css); + if ($swapLtrRtlInURL) { + $css = self::fixLtrRtlInURL($css); } - if ( $swapLeftRightInURL ) { - $css = self::fixLeftRightInURL( $css ); + if ($swapLeftRightInURL) { + $css = self::fixLeftRightInURL($css); } - $css = self::fixLeftAndRight( $css ); - $css = self::fixCursorProperties( $css ); - $css = self::fixFourPartNotation( $css ); - $css = self::fixBorderRadius( $css ); - $css = self::fixBackgroundPosition( $css ); - $css = self::fixShadows( $css ); + $css = self::fixLeftAndRight($css); + $css = self::fixCursorProperties($css); + $css = self::fixFourPartNotation($css); + $css = self::fixBorderRadius($css); + $css = self::fixBackgroundPosition($css); + $css = self::fixShadows($css); // Detokenize stuff we tokenized before - $css = $comments->detokenize( $css ); - $css = $noFlipClass->detokenize( $css ); - $css = $noFlipSingle->detokenize( $css ); + $css = $comments->detokenize($css); + $css = $noFlipClass->detokenize($css); + $css = $noFlipSingle->detokenize($css); return $css; } @@ -184,16 +184,19 @@ class CSSJanus { * and misses "body\n{\ndirection:ltr;\n}". This function does not have * these problems. * - * See http://code.google.com/p/cssjanus/issues/detail?id=15 and - * TODO: URL + * See https://code.google.com/p/cssjanus/issues/detail?id=15 + * * @param $css string * @return string */ - private static function fixDirection( $css ) { - $css = preg_replace( self::$patterns['direction_ltr'], - '$1' . self::$patterns['tmpToken'], $css ); - $css = preg_replace( self::$patterns['direction_rtl'], '$1ltr', $css ); - $css = str_replace( self::$patterns['tmpToken'], 'rtl', $css ); + private static function fixDirection($css) { + $css = preg_replace( + self::$patterns['direction_ltr'], + '$1' . self::$patterns['tmpToken'], + $css + ); + $css = preg_replace(self::$patterns['direction_rtl'], '$1ltr', $css); + $css = str_replace(self::$patterns['tmpToken'], 'rtl', $css); return $css; } @@ -203,10 +206,10 @@ class CSSJanus { * @param $css string * @return string */ - private static function fixLtrRtlInURL( $css ) { - $css = preg_replace( self::$patterns['ltr_in_url'], self::$patterns['tmpToken'], $css ); - $css = preg_replace( self::$patterns['rtl_in_url'], 'ltr', $css ); - $css = str_replace( self::$patterns['tmpToken'], 'rtl', $css ); + private static function fixLtrRtlInURL($css) { + $css = preg_replace(self::$patterns['ltr_in_url'], self::$patterns['tmpToken'], $css); + $css = preg_replace(self::$patterns['rtl_in_url'], 'ltr', $css); + $css = str_replace(self::$patterns['tmpToken'], 'rtl', $css); return $css; } @@ -216,10 +219,10 @@ class CSSJanus { * @param $css string * @return string */ - private static function fixLeftRightInURL( $css ) { - $css = preg_replace( self::$patterns['left_in_url'], self::$patterns['tmpToken'], $css ); - $css = preg_replace( self::$patterns['right_in_url'], 'left', $css ); - $css = str_replace( self::$patterns['tmpToken'], 'right', $css ); + private static function fixLeftRightInURL($css) { + $css = preg_replace(self::$patterns['left_in_url'], self::$patterns['tmpToken'], $css); + $css = preg_replace(self::$patterns['right_in_url'], 'left', $css); + $css = str_replace(self::$patterns['tmpToken'], 'right', $css); return $css; } @@ -229,10 +232,10 @@ class CSSJanus { * @param $css string * @return string */ - private static function fixLeftAndRight( $css ) { - $css = preg_replace( self::$patterns['left'], self::$patterns['tmpToken'], $css ); - $css = preg_replace( self::$patterns['right'], 'left', $css ); - $css = str_replace( self::$patterns['tmpToken'], 'right', $css ); + private static function fixLeftAndRight($css) { + $css = preg_replace(self::$patterns['left'], self::$patterns['tmpToken'], $css); + $css = preg_replace(self::$patterns['right'], 'left', $css); + $css = str_replace(self::$patterns['tmpToken'], 'right', $css); return $css; } @@ -242,11 +245,14 @@ class CSSJanus { * @param $css string * @return string */ - private static function fixCursorProperties( $css ) { - $css = preg_replace( self::$patterns['cursor_east'], - '$1' . self::$patterns['tmpToken'], $css ); - $css = preg_replace( self::$patterns['cursor_west'], '$1e-resize', $css ); - $css = str_replace( self::$patterns['tmpToken'], 'w-resize', $css ); + private static function fixCursorProperties($css) { + $css = preg_replace( + self::$patterns['cursor_east'], + '$1' . self::$patterns['tmpToken'], + $css + ); + $css = preg_replace(self::$patterns['cursor_west'], '$1e-resize', $css); + $css = str_replace(self::$patterns['tmpToken'], 'w-resize', $css); return $css; } @@ -259,28 +265,38 @@ class CSSJanus { * the bug where whitespace is not preserved when flipping four-part rules * and four-part color rules with multiple whitespace characters between * colors are not recognized. - * See http://code.google.com/p/cssjanus/issues/detail?id=16 + * See https://code.google.com/p/cssjanus/issues/detail?id=16 * @param $css string * @return string */ - private static function fixFourPartNotation( $css ) { - $css = preg_replace( self::$patterns['four_notation_quantity'], '$1$2$3$8$5$6$7$4$9', $css ); - $css = preg_replace( self::$patterns['four_notation_color'], '$1$2$3$8$5$6$7$4$9', $css ); + private static function fixFourPartNotation($css) { + $css = preg_replace(self::$patterns['four_notation_quantity'], '$1$2$3$8$5$6$7$4$9', $css); + $css = preg_replace(self::$patterns['four_notation_color'], '$1$2$3$8$5$6$7$4$9', $css); return $css; } /** - * Swaps appropriate corners in four-part border-radius rules. - * Needs to undo the effect of fixFourPartNotation() on those rules, too. + * Swaps appropriate corners in border-radius values. * * @param $css string * @return string */ - private static function fixBorderRadius( $css ) { - // Undo four_notation_quantity - $css = preg_replace( self::$patterns['border_radius'], '$1$2$3$8$5$6$7$4$9', $css ); - // Do the real thing - $css = preg_replace( self::$patterns['border_radius'], '$1$4$3$2$5$8$7$6$9', $css ); + private static function fixBorderRadius($css) { + $css = preg_replace_callback(self::$patterns['border_radius'], function ($matches) { + $pre = $matches[1]; + $values = $matches[2]; + $numValues = count(preg_split('/\s+/', trim($values))); + switch ($numValues) { + case 4: + $values = preg_replace('/^(\S+)(\s*)(\S+)(\s*)(\S+)(\s*)(\S+)/', '$3$2$1$4$7$6$5', $values); + break; + case 3: + case 2: + $values = preg_replace('/^(\S+)(\s*)(\S+)/', '$3$2$1', $values); + break; + } + return $pre . $values; + }, $css); return $css; } @@ -291,42 +307,31 @@ class CSSJanus { * @param $css string * @return string */ - private static function fixShadows( $css ) { + private static function fixShadows($css) { // Flips the sign of a CSS value, possibly with a unit. // (We can't just negate the value with unary minus due to the units.) - $flipSign = function ( $cssValue ) { + $flipSign = function ($cssValue) { // Don't mangle zeroes - if ( floatval( $cssValue ) === 0.0 ) { + if (floatval($cssValue) === 0.0) { return $cssValue; - } elseif ( $cssValue[0] === '-' ) { - return substr( $cssValue, 1 ); + } elseif ($cssValue[0] === '-') { + return substr($cssValue, 1); } else { return "-" . $cssValue; } }; - $css = preg_replace_callback( - self::$patterns['box_shadow'], function ( $matches ) use ( $flipSign ) { - return $matches[1] . $flipSign( $matches[2] ); - }, - $css - ); + $css = preg_replace_callback(self::$patterns['box_shadow'], function ($matches) use ($flipSign) { + return $matches[1] . $flipSign($matches[2]); + }, $css); - $css = preg_replace_callback( - self::$patterns['text_shadow1'], - function ( $matches ) use ( $flipSign ) { - return $matches[1] . $matches[2] . $matches[3] . $flipSign( $matches[4] ); - }, - $css - ); + $css = preg_replace_callback(self::$patterns['text_shadow1'], function ($matches) use ($flipSign) { + return $matches[1] . $matches[2] . $matches[3] . $flipSign($matches[4]); + }, $css); - $css = preg_replace_callback( - self::$patterns['text_shadow2'], - function ( $matches ) use ( $flipSign ) { - return $matches[1] . $flipSign( $matches[2] ); - }, - $css - ); + $css = preg_replace_callback(self::$patterns['text_shadow2'], function ($matches) use ($flipSign) { + return $matches[1] . $flipSign($matches[2]); + }, $css); return $css; } @@ -336,16 +341,22 @@ class CSSJanus { * @param $css string * @return string */ - private static function fixBackgroundPosition( $css ) { - $replaced = preg_replace_callback( self::$patterns['bg_horizontal_percentage'], - array( 'self', 'calculateNewBackgroundPosition' ), $css ); - if ( $replaced !== null ) { - // Check for null; sometimes preg_replace_callback() returns null here for some weird reason + private static function fixBackgroundPosition($css) { + $replaced = preg_replace_callback( + self::$patterns['bg_horizontal_percentage'], + array('self', 'calculateNewBackgroundPosition'), + $css + ); + if ($replaced !== null) { + // preg_replace_callback() sometimes returns null $css = $replaced; } - $replaced = preg_replace_callback( self::$patterns['bg_horizontal_percentage_x'], - array( 'self', 'calculateNewBackgroundPosition' ), $css ); - if ( $replaced !== null ) { + $replaced = preg_replace_callback( + self::$patterns['bg_horizontal_percentage_x'], + array('self', 'calculateNewBackgroundPosition'), + $css + ); + if ($replaced !== null) { $css = $replaced; } @@ -353,12 +364,22 @@ class CSSJanus { } /** - * Callback for calculateNewBackgroundPosition() + * Callback for fixBackgroundPosition() * @param $matches array * @return string */ - private static function calculateNewBackgroundPosition( $matches ) { - return $matches[1] . ( 100 - $matches[2] ) . $matches[3]; + private static function calculateNewBackgroundPosition($matches) { + $value = $matches[2]; + if (substr($value, -1) === '%') { + $idx = strpos($value, '.'); + if ($idx !== false) { + $len = strlen($value) - $idx - 2; + $value = number_format(100 - $value, $len) . '%'; + } else { + $value = (100 - $value) . '%'; + } + } + return $matches[1] . $value; } } @@ -368,7 +389,8 @@ class CSSJanus { * @author Roan Kattouw */ class CSSJanusTokenizer { - private $regex, $token; + private $regex; + private $token; private $originals; /** @@ -376,7 +398,7 @@ class CSSJanusTokenizer { * @param string $regex Regular expression whose matches to replace by a token. * @param string $token Token */ - public function __construct( $regex, $token ) { + public function __construct($regex, $token) { $this->regex = $regex; $this->token = $token; $this->originals = array(); @@ -388,15 +410,15 @@ class CSSJanusTokenizer { * @param string $str to tokenize * @return string Tokenized string */ - public function tokenize( $str ) { - return preg_replace_callback( $this->regex, array( $this, 'tokenizeCallback' ), $str ); + public function tokenize($str) { + return preg_replace_callback($this->regex, array($this, 'tokenizeCallback'), $str); } /** * @param $matches array * @return string */ - private function tokenizeCallback( $matches ) { + private function tokenizeCallback($matches) { $this->originals[] = $matches[0]; return $this->token; } @@ -407,21 +429,24 @@ class CSSJanusTokenizer { * @param string $str previously run through tokenize() * @return string Original string */ - public function detokenize( $str ) { + public function detokenize($str) { // PHP has no function to replace only the first occurrence or to // replace occurrences of the same string with different values, // so we use preg_replace_callback() even though we don't really need a regex - return preg_replace_callback( '/' . preg_quote( $this->token, '/' ) . '/', - array( $this, 'detokenizeCallback' ), $str ); + return preg_replace_callback( + '/' . preg_quote($this->token, '/') . '/', + array($this, 'detokenizeCallback'), + $str + ); } /** * @param $matches * @return mixed */ - private function detokenizeCallback( $matches ) { - $retval = current( $this->originals ); - next( $this->originals ); + private function detokenizeCallback($matches) { + $retval = current($this->originals); + next($this->originals); return $retval; } diff --git a/tests/phpunit/includes/libs/CSSJanusTest.php b/tests/phpunit/includes/libs/CSSJanusTest.php deleted file mode 100644 index 8d0224cd20..0000000000 --- a/tests/phpunit/includes/libs/CSSJanusTest.php +++ /dev/null @@ -1,633 +0,0 @@ -assertEquals( $transformedA, $cssB, 'Test A-B transformation' ); - - $transformedB = CSSJanus::transform( $cssB ); - $this->assertEquals( $transformedB, $cssA, 'Test B-A transformation' ); - } else { - // If no B version is provided, it means - // the output should equal the input (modulo @noflip annotations). - $transformedA = CSSJanus::transform( $cssA ); - $this->assertEquals( $transformedA, $cssA, 'Nothing was flipped' ); - } - } - - /** - * @dataProvider provideTransformAdvancedCases - */ - public function testTransformAdvanced( $code, $expectedOutput, $options = array() ) { - $swapLtrRtlInURL = isset( $options['swapLtrRtlInURL'] ) ? - $options['swapLtrRtlInURL'] : false; - $swapLeftRightInURL = isset( $options['swapLeftRightInURL'] ) ? - $options['swapLeftRightInURL'] : false; - - $flipped = CSSJanus::transform( $code, $swapLtrRtlInURL, $swapLeftRightInURL ); - - $this->assertEquals( $expectedOutput, $flipped, - 'Test flipping, options: url-ltr-rtl=' . ( $swapLtrRtlInURL ? 'true' : 'false' ) - . ' url-left-right=' . ( $swapLeftRightInURL ? 'true' : 'false' ) - ); - } - - /** - * @dataProvider provideTransformBrokenCases - * @group Broken - */ - public function testTransformBroken( $code, $expectedOutput ) { - $flipped = CSSJanus::transform( $code ); - - $this->assertEquals( $expectedOutput, $flipped, 'Test flipping' ); - } - - /** - * These transform cases are tested *in both directions* - * No need to declare a principle twice in both directions here. - */ - public static function provideTransformCases() { - return array( - // Property keys - array( - '.foo { left: 0; }', - '.foo { right: 0; }' - ), - // Guard against partial keys - // (CSS currently doesn't have flippable properties - // that contain the direction as part of the key without - // dash separation) - array( - '.foo { alright: 0; }' - ), - array( - '.foo { balleft: 0; }' - ), - - // Dashed property keys - array( - '.foo { padding-left: 0; }', - '.foo { padding-right: 0; }' - ), - array( - '.foo { margin-left: 0; }', - '.foo { margin-right: 0; }' - ), - array( - '.foo { border-left: 0; }', - '.foo { border-right: 0; }' - ), - - // Double-dashed property keys - array( - '.foo { border-left-color: red; }', - '.foo { border-right-color: red; }' - ), - array( - // Includes unknown properties? - '.foo { x-left-y: 0; }', - '.foo { x-right-y: 0; }' - ), - - // Multi-value properties - array( - '.foo { padding: 0; }' - ), - array( - '.foo { padding: 0 1px; }' - ), - array( - '.foo { padding: 0 1px 2px; }' - ), - array( - '.foo { padding: 0 1px 2px 3px; }', - '.foo { padding: 0 3px 2px 1px; }' - ), - - // Shorthand / Four notation - array( - '.foo { padding: .25em 15px 0pt 0ex; }', - '.foo { padding: .25em 0ex 0pt 15px; }' - ), - array( - '.foo { margin: 1px -4px 3px 2px; }', - '.foo { margin: 1px 2px 3px -4px; }' - ), - array( - '.foo { padding: 0 15px .25em 0; }', - '.foo { padding: 0 0 .25em 15px; }' - ), - array( - '.foo { padding: 1px 4.1grad 3px 2%; }', - '.foo { padding: 1px 2% 3px 4.1grad; }' - ), - array( - '.foo { padding: 1px 2px 3px auto; }', - '.foo { padding: 1px auto 3px 2px; }' - ), - array( - '.foo { padding: 1px inherit 3px auto; }', - '.foo { padding: 1px auto 3px inherit; }' - ), - // border-radius assigns different meanings to the values - array( - '.foo { border-radius: .25em 15px 0pt 0ex; }', - '.foo { border-radius: 15px .25em 0ex 0pt; }' - ), - array( - '.foo { border-radius: 0px 0px 5px 5px; }', - ), - // Ensure the rule doesn't break other stuff - array( - '.foo { x-unknown: a b c d; }' - ), - array( - '.foo barpx 0 2% { opacity: 0; }' - ), - array( - '#settings td p strong' - ), - array( - // Color names - '.foo { border-color: red green blue white }', - '.foo { border-color: red white blue green }', - ), - array( - // Color name, hexdecimal, RGB & RGBA - '.foo { border-color: red #f00 rgb(255, 0, 0) rgba(255, 0, 0, 0.5) }', - '.foo { border-color: red rgba(255, 0, 0, 0.5) rgb(255, 0, 0) #f00 }', - ), - array( - // Color name, hexdecimal, HSL & HSLA - '.foo { border-color: red #f00 hsl(0, 100%, 50%) hsla(0, 100%, 50%, 0.5) }', - '.foo { border-color: red hsla(0, 100%, 50%, 0.5) hsl(0, 100%, 50%) #f00 }', - ), - array( - // Do not mangle 5 or more values - '.foo { -x-unknown: 1 2 3 4 5; }' - ), - array( - '.foo { -x-unknown: 1 2 3 4 5 6; }' - ), - - // Shorthand / Three notation - array( - '.foo { margin: 1em 0 .25em; }' - ), - array( - '.foo { margin:-1.5em 0 -.75em; }' - ), - - // Shorthand / Two notation - array( - '.foo { padding: 1px 2px; }' - ), - - // Shorthand / One notation - array( - '.foo { padding: 1px; }' - ), - - // text-shadow and box-shadow - array( - '.foo { box-shadow: -6px 3px 8px 5px rgba(0, 0, 0, 0.25); }', - '.foo { box-shadow: 6px 3px 8px 5px rgba(0, 0, 0, 0.25); }', - ), - array( - '.foo { box-shadow: inset -6px 3px 8px 5px rgba(0, 0, 0, 0.25); }', - '.foo { box-shadow: inset 6px 3px 8px 5px rgba(0, 0, 0, 0.25); }', - ), - array( - '.foo { text-shadow: orange 2px 0; }', - '.foo { text-shadow: orange -2px 0; }', - ), - array( - '.foo { text-shadow: 2px 0 orange; }', - '.foo { text-shadow: -2px 0 orange; }', - ), - array( - // Don't mangle zeroes - '.foo { text-shadow: orange 0 2px; }' - ), - array( - // Make sure floats are not considered zero - '.foo { box-shadow: inset .5em 0 0 white; }', - '.foo { box-shadow: inset -.5em 0 0 white; }', - ), - - // Direction - // Note: This differs from the Python implementation, - // see also CSSJanus::fixDirection for more info. - array( - '.foo { direction: ltr; }', - '.foo { direction: rtl; }' - ), - array( - '.foo { direction: rtl; }', - '.foo { direction: ltr; }' - ), - array( - 'input { direction: ltr; }', - 'input { direction: rtl; }' - ), - array( - 'input { direction: rtl; }', - 'input { direction: ltr; }' - ), - array( - 'body { direction: ltr; }', - 'body { direction: rtl; }' - ), - array( - '.foo, body, input { direction: ltr; }', - '.foo, body, input { direction: rtl; }' - ), - array( - 'body { padding: 10px; direction: ltr; }', - 'body { padding: 10px; direction: rtl; }' - ), - array( - 'body { direction: ltr } .myClass { direction: ltr }', - 'body { direction: rtl } .myClass { direction: rtl }' - ), - - // Left/right values - array( - '.foo { float: left; }', - '.foo { float: right; }' - ), - array( - '.foo { text-align: left; }', - '.foo { text-align: right; }' - ), - array( - '.foo { -x-unknown: left; }', - '.foo { -x-unknown: right; }' - ), - // Guard against selectors that look flippable - array( - '.column-left { width: 0; }' - ), - array( - 'a.left { width: 0; }' - ), - array( - 'a.leftification { width: 0; }' - ), - array( - 'a.ltr { width: 0; }' - ), - array( - #
- '.a-ltr.png { width: 0; }' - ), - array( - # - 'foo-ltr[attr="x"] { width: 0; }' - ), - array( - 'div.left > span.right+span.left { width: 0; }' - ), - array( - '.thisclass .left .myclass { width: 0; }' - ), - array( - '.thisclass .left .myclass #myid { width: 0; }' - ), - - // Cursor values (east/west) - array( - '.foo { cursor: e-resize; }', - '.foo { cursor: w-resize; }' - ), - array( - '.foo { cursor: se-resize; }', - '.foo { cursor: sw-resize; }' - ), - array( - '.foo { cursor: ne-resize; }', - '.foo { cursor: nw-resize; }' - ), - - // Background - array( - '.foo { background-position: top left; }', - '.foo { background-position: top right; }' - ), - array( - '.foo { background: url(/foo/bar.png) top left; }', - '.foo { background: url(/foo/bar.png) top right; }' - ), - array( - '.foo { background: url(/foo/bar.png) top left no-repeat; }', - '.foo { background: url(/foo/bar.png) top right no-repeat; }' - ), - array( - '.foo { background: url(/foo/bar.png) no-repeat top left; }', - '.foo { background: url(/foo/bar.png) no-repeat top right; }' - ), - array( - '.foo { background: #fff url(/foo/bar.png) no-repeat top left; }', - '.foo { background: #fff url(/foo/bar.png) no-repeat top right; }' - ), - array( - '.foo { background-position: 100% 40%; }', - '.foo { background-position: 0% 40%; }' - ), - array( - '.foo { background-position: 23% 0; }', - '.foo { background-position: 77% 0; }' - ), - array( - '.foo { background-position: 23% auto; }', - '.foo { background-position: 77% auto; }' - ), - array( - '.foo { background-position-x: 23%; }', - '.foo { background-position-x: 77%; }' - ), - array( - '.foo { background-position-y: 23%; }', - '.foo { background-position-y: 23%; }' - ), - array( - '.foo { background:url(../foo.png) no-repeat 75% 50%; }', - '.foo { background:url(../foo.png) no-repeat 25% 50%; }' - ), - array( - '.foo { background: 10% 20% } .bar { background: 40% 30% }', - '.foo { background: 90% 20% } .bar { background: 60% 30% }' - ), - - // Multiple rules - array( - 'body { direction: rtl; float: right; } .foo { direction: ltr; float: right; }', - 'body { direction: ltr; float: left; } .foo { direction: rtl; float: left; }', - ), - - // Duplicate properties - array( - '.foo { float: left; float: right; float: left; }', - '.foo { float: right; float: left; float: right; }', - ), - - // Preserve comments - array( - '/* left /* right */left: 10px', - '/* left /* right */right: 10px' - ), - array( - '/*left*//*left*/left: 10px', - '/*left*//*left*/right: 10px' - ), - array( - '/* Going right is cool */ .foo { width: 0 }', - ), - array( - "/* padding-right 1 2 3 4 */\n#test { width: 0}\n/*right*/" - ), - array( - "/** Two line comment\n * left\n \*/\n#test {width: 0}" - ), - - // @noflip annotation - array( - // before selector (single) - '/* @noflip */ div { float: left; }' - ), - array( - // before selector (multiple) - '/* @noflip */ div, .notme { float: left; }' - ), - array( - // inside selector - 'div, /* @noflip */ .foo { float: left; }' - ), - array( - // after selector - 'div, .notme /* @noflip */ { float: left; }' - ), - array( - // before multiple rules - '/* @noflip */ div { float: left; } .foo { float: left; }', - '/* @noflip */ div { float: left; } .foo { float: right; }' - ), - array( - // support parentheses in selector - '/* @noflip */ .test:not(:first) { margin-right: -0.25em; margin-left: 0.25em; }', - '/* @noflip */ .test:not(:first) { margin-right: -0.25em; margin-left: 0.25em; }' - ), - array( - // after multiple rules - '.foo { float: left; } /* @noflip */ div { float: left; }', - '.foo { float: right; } /* @noflip */ div { float: left; }' - ), - array( - // before multiple properties - 'div { /* @noflip */ float: left; text-align: left; }', - 'div { /* @noflip */ float: left; text-align: right; }' - ), - array( - // after multiple properties - 'div { float: left; /* @noflip */ text-align: left; }', - 'div { float: right; /* @noflip */ text-align: left; }' - ), - array( - // before a *= attribute selector with multiple properties - '/* @noflip */ div.foo[bar*=baz] { float:left; clear: left; }' - ), - array( - // before a ^= attribute selector with multiple properties - '/* @noflip */ div.foo[bar^=baz] { float:left; clear: left; }' - ), - array( - // before a ~= attribute selector with multiple properties - '/* @noflip */ div.foo[bar~=baz] { float:left; clear: left; }' - ), - array( - // before a = attribute selector with multiple properties - '/* @noflip */ div.foo[bar=baz] { float:left; clear: left; }' - ), - array( - // before a quoted attribute selector with multiple properties - '/* @noflip */ div.foo[bar=\'baz{quux\'] { float:left; clear: left; }' - ), - - // Guard against css3 stuff - array( - 'background-image: -moz-linear-gradient(#326cc1, #234e8c);' - ), - array( - 'background-image: -webkit-gradient(linear, 100% 0%, 0% 0%, from(#666666), to(#ffffff));' - ), - - // CSS syntax / white-space variations - // spaces, no spaces, tabs, new lines, omitting semi-colons - array( - ".foo { left: 0; }", - ".foo { right: 0; }" - ), - array( - ".foo{ left: 0; }", - ".foo{ right: 0; }" - ), - array( - ".foo{ left: 0 }", - ".foo{ right: 0 }" - ), - array( - ".foo{left:0 }", - ".foo{right:0 }" - ), - array( - ".foo{left:0}", - ".foo{right:0}" - ), - array( - ".foo { left : 0 ; }", - ".foo { right : 0 ; }" - ), - array( - ".foo\n { left : 0 ; }", - ".foo\n { right : 0 ; }" - ), - array( - ".foo\n { \nleft : 0 ; }", - ".foo\n { \nright : 0 ; }" - ), - array( - ".foo\n { \n left : 0 ; }", - ".foo\n { \n right : 0 ; }" - ), - array( - ".foo\n { \n left\n : 0; }", - ".foo\n { \n right\n : 0; }" - ), - array( - ".foo \n { \n left\n : 0; }", - ".foo \n { \n right\n : 0; }" - ), - array( - ".foo\n{\nleft\n:\n0;}", - ".foo\n{\nright\n:\n0;}" - ), - array( - ".foo\n.bar {\n\tleft: 0;\n}", - ".foo\n.bar {\n\tright: 0;\n}" - ), - array( - ".foo\t{\tleft\t:\t0;}", - ".foo\t{\tright\t:\t0;}" - ), - - // Guard against partial keys - array( - '.foo { leftxx: 0; }', - '.foo { leftxx: 0; }' - ), - array( - '.foo { rightxx: 0; }', - '.foo { rightxx: 0; }' - ), - ); - } - - /** - * These cases are tested in one way only (format: actual, expected, msg). - * If both ways can be tested, either put both versions in here or move - * it to provideTransformCases(). - */ - public static function provideTransformAdvancedCases() { - $bgPairs = array( - # [ - _ . ] <-> [ left right ltr rtl ] - 'foo.jpg' => 'foo.jpg', - 'left.jpg' => 'right.jpg', - 'ltr.jpg' => 'rtl.jpg', - - 'foo-left.png' => 'foo-right.png', - 'foo_left.png' => 'foo_right.png', - 'foo.left.png' => 'foo.right.png', - - 'foo-ltr.png' => 'foo-rtl.png', - 'foo_ltr.png' => 'foo_rtl.png', - 'foo.ltr.png' => 'foo.rtl.png', - - 'left-foo.png' => 'right-foo.png', - 'left_foo.png' => 'right_foo.png', - 'left.foo.png' => 'right.foo.png', - - 'ltr-foo.png' => 'rtl-foo.png', - 'ltr_foo.png' => 'rtl_foo.png', - 'ltr.foo.png' => 'rtl.foo.png', - - 'foo-ltr-left.gif' => 'foo-rtl-right.gif', - 'foo_ltr_left.gif' => 'foo_rtl_right.gif', - 'foo.ltr.left.gif' => 'foo.rtl.right.gif', - 'foo-ltr_left.gif' => 'foo-rtl_right.gif', - 'foo_ltr.left.gif' => 'foo_rtl.right.gif', - ); - $provider = array(); - foreach ( $bgPairs as $left => $right ) { - # By default '-rtl' and '-left' etc. are not touched, - # Only when the appropiate parameter is set. - $provider[] = array( - ".foo { background: url(images/$left); }", - ".foo { background: url(images/$left); }" - ); - $provider[] = array( - ".foo { background: url(images/$right); }", - ".foo { background: url(images/$right); }" - ); - $provider[] = array( - ".foo { background: url(images/$left); }", - ".foo { background: url(images/$right); }", - array( - 'swapLtrRtlInURL' => true, - 'swapLeftRightInURL' => true, - ) - ); - $provider[] = array( - ".foo { background: url(images/$right); }", - ".foo { background: url(images/$left); }", - array( - 'swapLtrRtlInURL' => true, - 'swapLeftRightInURL' => true, - ) - ); - } - - return $provider; - } - - /** - * Cases that are currently failing, but - * should be looked at in the future as enhancements and/or bug fix - */ - public static function provideTransformBrokenCases() { - return array( - // Guard against selectors that look flippable - array( - # - 'foo-left-x[attr="x"] { width: 0; }', - 'foo-left-x[attr="x"] { width: 0; }' - ), - array( - #
- '.foo[data-left="x"] { width: 0; }', - '.foo[data-left="x"] { width: 0; }' - ), - ); - } -} -- 2.20.1