diff --git a/core/includes/common.inc b/core/includes/common.inc index 5d45937..845b38b 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -2082,85 +2082,6 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) { } /** - * Parses a color string. - * - * @param $hex - * A hexadecimal color string to parse. Can be prefixed with '#' and have - * either one or two digits per component. - * @param $alpha - * (Optional) Whether or not to allow an alpha transparency component. - * Defaults to FALSE. - * - * @return - * An array with the keys 'red', 'green', 'blue' and 'alpha' with the - * corresponding integers as values. FALSE if the input is invalid. - * - * @see drupal_rgba_to_hex() - */ -function drupal_hex_to_rgba($hex, $alpha = FALSE) { - // Ignore '#' prefixes. - $hex = ltrim($hex, '#'); - - // Convert shorhands like '#abc' to '#aabbcc'. - if (strlen($hex) <= 4) { - $hex = preg_replace('|([0-9a-z])|i', '\1\1', $hex); - } - - // Parse out the components. - if (!preg_match('/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-7][0-9a-f])?$/i', $hex, $rgba)) { - return FALSE; - } - - // The input is invalid, if alpha is not allowed but given. - if (!$alpha && isset($rgba[4])) { - return FALSE; - } - - // Return the result array. - return array( - 'red' => hexdec($rgba[1]), - 'green' => hexdec($rgba[2]), - 'blue' => hexdec($rgba[3]), - 'alpha' => isset($rgba[4]) ? hexdec($rgba[4]) : 0, - ); -} - -/** - * Converts a color array to a color string. - * - * @param $rgba - * An array with integer values for the keys 'red', 'green', 'blue' and - * 'alpha'. Components that are not given default to 0. - * - * @return - * A lowercase color string like '#00ff00'. - * - * @see drupal_hex_to_rgba() - */ -function drupal_rgba_to_hex($rgba) { - if (!is_array($rgba)) { - return '#000000'; - } - - $rgba += array( - 'red' => 0, - 'green' => 0, - 'blue' => 0, - 'alpha' => 0, - ); - - $result = '#'; - - foreach (array('red', 'green', 'blue', 'alpha') as $component) { - if ($component != 'alpha' || $rgba[$component]) { - $result .= str_pad(dechex($rgba[$component]), 2, '0', STR_PAD_LEFT); - } - } - - return $result; -} - -/** * @} End of "defgroup format". */ diff --git a/core/includes/form.inc b/core/includes/form.inc index 9f952f9..5bdaf90 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -5,6 +5,8 @@ * Functions for form and batch generation and processing. */ +use Drupal\Core\Utility\Color; + /** * @defgroup forms Form builder functions * @{ @@ -4088,7 +4090,7 @@ function form_validate_url(&$element, &$form_state) { /** * Form element validation handler for #type 'color'. */ -function form_validate_color($element, &$form_state) { +function form_validate_color(&$element, &$form_state) { // Empty means black. $value = trim($element['#value']); if ($value === '') { @@ -4096,9 +4098,9 @@ function form_validate_color($element, &$form_state) { } // Try to parse the value. - if ($parsed = drupal_hex_to_rgba($value)) { + if ($parsed = Color::parseHex($value)) { // Set a normalized value. - form_set_value($element, drupal_rgba_to_hex($parsed), $form_state); + form_set_value($element, $parsed->__toString(), $form_state); } else { form_error($element, t('%name must be a valid color.', array('%name' => empty($element['#title']) ? $element['#parents'][0] : $element['#title']))); diff --git a/core/lib/Drupal/Core/Utility/Color.php b/core/lib/Drupal/Core/Utility/Color.php new file mode 100644 index 0000000..472d223 --- /dev/null +++ b/core/lib/Drupal/Core/Utility/Color.php @@ -0,0 +1,179 @@ + 255 || $green > 255 || $blue > 255) { + throw new \InvalidArgumentException('Color components must be between 0 and 255.'); + } + + if ($alpha > 127) { + throw new \InvalidArgumentException('Alpha component must be between 0 and 127.'); + } + + $this->red = (int) $red; + $this->green = (int) $green; + $this->blue = (int) $blue; + $this->alpha = (int) $alpha; + } + + /** + * Implements PHP magic __toString method to convert the color to a string. + * + * @return string + * A hexadecimal representation of the color like '#aabbcc' or '#aabbcc55'. + */ + public function __toString() { + $result = '#'; + + foreach (array('red', 'green', 'blue', 'alpha') as $component) { + if ($component != 'alpha' || $this->{$component}) { + $result .= str_pad(dechex($this->{$component}), 2, '0', STR_PAD_LEFT); + } + } + + return $result; + } + + /** + * Gets the red component of the color. + * + * @return int + * The red component of the color as an integer between 0 and 255. + */ + public function getRed() { + return $this->red; + } + + /** + * Gets the green component of the color. + * + * @return int + * The green component of the color as an integer between 0 and 255. + */ + public function getGreen() { + return $this->green; + } + + /** + * Gets the blue component of the color. + * + * @return int + * The blue component of the color as an integer between 0 and 255. + */ + public function getBlue() { + return $this->blue; + } + + /** + * Gets the alpha component of the color. + * + * @return int + * The alpha component of the color as an integer between 0 and 127, where + * 0 is opaque and 127 is fully transparent. + */ + public function getAlpha() { + return $this->alpha; + } + + /** + * Gets the decimal alpha component of the color. + * + * @return float + * The alpha component of the color as a float between 0 and 1, where 0 is + * fully transparent and 1 is opaque. + */ + public function getDecimalAlpha() { + return 1 - $alpha / 127.0; + } + + /** + * Parses a hexadecimal color string like '#abc' or '#aabbcc'. + * + * @param string $hex + * The hexadecimal colorstring to parse. + * @param bool $allow_alpha + * Optional. Whether or not to allow an alpha component. Defaults to FALSE. + * + * @return false|Drupal\Core\Utility\Color + * The color object representation of the string or FALSE, if the string is + * invalid. + */ + public static function parseHex($hex, $allow_alpha = FALSE) { + $hex = ltrim($hex, '#'); + + // Expand shorthands like 'abc' to 'aabbcc'. + if (strlen($hex) < 4) { + $hex = preg_replace('|([0-9a-f])|i', '\1\1', $hex); + } + + if (!preg_match('/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-7][0-9a-f])?$/i', $hex, $rgba)) { + return FALSE; + } + + if (!$allow_alpha && isset($rgba[4])) { + return FALSE; + } + + return new Color(hexdec($rgba[1]), hexdec($rgba[2]), hexdec($rgba[3]), isset($rgba[4]) ? hexdec($rgba[4]) : 0); + } +} diff --git a/core/modules/system/tests/common.test b/core/modules/system/tests/common.test index e20f874..46b379e 100644 --- a/core/modules/system/tests/common.test +++ b/core/modules/system/tests/common.test @@ -2050,67 +2050,6 @@ class CommonValidNumberStepUnitTestCase extends DrupalUnitTestCase { } /** - * Tests color conversion functions. - */ -class CommonColorConversionTestCase extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'Color conversion', - 'description' => 'Tests color conversion by drupal_hex_to_rgba() and drupal_rgba_to_hex()', - 'group' => 'Common', - ); - } - - /** - * Tests drupal_hex_to_rgba(). - */ - function testDrupalHexToRGBA() { - // Test shorthand conversion without alpha. - $this->assertEqual(drupal_hex_to_rgba('#000'), array('red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 0)); - $this->assertEqual(drupal_hex_to_rgba('#fff'), array('red' => 255, 'green' => 255, 'blue' => 255, 'alpha' => 0)); - $this->assertEqual(drupal_hex_to_rgba('#abc'), array('red' => 170, 'green' => 187, 'blue' => 204, 'alpha' => 0)); - $this->assertEqual(drupal_hex_to_rgba('cba'), array('red' => 204, 'green' => 187, 'blue' => 170, 'alpha' => 0)); - - // Test shorthand conversion with alpha. - $this->assertEqual(drupal_hex_to_rgba('#0007', TRUE), array('red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 119)); - $this->assertEqual(drupal_hex_to_rgba('#7001', TRUE), array('red' => 119, 'green' => 0, 'blue' => 0, 'alpha' => 17)); - $this->assertEqual(drupal_hex_to_rgba('2000', TRUE), array('red' => 34, 'green' => 0, 'blue' => 0, 'alpha' => 0)); - - // Test conversion without alpha. - $this->assertEqual(drupal_hex_to_rgba('#000000'), array('red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 0)); - $this->assertEqual(drupal_hex_to_rgba('#010203'), array('red' => 1, 'green' => 2, 'blue' => 3, 'alpha' => 0)); - - // Test too high alpha values. - $this->assertIdentical(drupal_hex_to_rgba('#0008', TRUE), FALSE); - $this->assertIdentical(drupal_hex_to_rgba('#a00f', TRUE), FALSE); - $this->assertIdentical(drupal_hex_to_rgba('#aa00aaf1', TRUE), FALSE); - - // Test alpha values are invalid if they are not allowed. - $this->assertIdentical(drupal_hex_to_rgba('#1111'), FALSE); - $this->assertIdentical(drupal_hex_to_rgba('#22334455'), FALSE); - $this->assertIdentical(drupal_hex_to_rgba('#0000'), FALSE); - - // Test bogus input. - $this->assertIdentical(drupal_hex_to_rgba('#foo'), FALSE); - $this->assertIdentical(drupal_hex_to_rgba('123456789'), FALSE); - } - - /** - * Tests drupal_rgba_to_hex(). - */ - function testDrupalRGBAToHex() { - // Test conversion without alpha. - $this->assertEqual(drupal_rgba_to_hex(array('red' => 7)), '#070000'); - $this->assertEqual(drupal_rgba_to_hex(array('green' => 255)), '#00ff00'); - $this->assertEqual(drupal_rgba_to_hex(array('red' => 1, 'green' => 2, 'blue' => 3)), '#010203'); - - // Test conversion with alpha. - $this->assertEqual(drupal_rgba_to_hex(array('alpha' => 10)), '#0000000a'); - $this->assertEqual(drupal_rgba_to_hex(array('green' => 15, 'alpha' => 3)), '#000f0003'); - } -} - -/** * Tests writing of data records with drupal_write_record(). */ class CommonDrupalWriteRecordTestCase extends DrupalWebTestCase {