Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.07% covered (success)
94.07%
111 / 118
76.92% covered (warning)
76.92%
10 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
Less_Tree_Color
94.07% covered (success)
94.07%
111 / 118
76.92% covered (warning)
76.92%
10 / 13
60.75
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
8
 luma
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 genCSS
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toCSS
83.33% covered (warning)
83.33%
15 / 18
0.00% covered (danger)
0.00%
0 / 1
14.91
 operate
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 toRGB
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toHSL
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
7
 toHSV
87.50% covered (warning)
87.50%
21 / 24
0.00% covered (danger)
0.00%
0 / 1
7.10
 toARGB
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 compare
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
6.17
 clamp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toHex
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 fromKeyword
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2/**
3 * @private
4 */
5class Less_Tree_Color extends Less_Tree {
6    public $rgb;
7    public $alpha;
8    public $isTransparentKeyword;
9    public $value;
10
11    public function __construct( $rgb, $a = 1, $isTransparentKeyword = null, $originalForm = null ) {
12        if ( $isTransparentKeyword ) {
13            $this->rgb = $rgb;
14            $this->alpha = $a;
15            $this->isTransparentKeyword = true;
16            return;
17        }
18        if ( isset( $originalForm ) ) {
19            $this->value = $originalForm;
20        }
21
22        $this->rgb = [];
23        if ( is_array( $rgb ) ) {
24            $this->rgb = $rgb;
25        } elseif ( strlen( $rgb ) == 6 ) {
26            foreach ( str_split( $rgb, 2 ) as $c ) {
27                $this->rgb[] = hexdec( $c );
28            }
29        } else {
30            foreach ( str_split( $rgb, 1 ) as $c ) {
31                $this->rgb[] = hexdec( $c . $c );
32            }
33        }
34        $this->alpha = is_numeric( $a ) ? $a : 1;
35    }
36
37    public function luma() {
38        $r = $this->rgb[0] / 255;
39        $g = $this->rgb[1] / 255;
40        $b = $this->rgb[2] / 255;
41
42        $r = ( $r <= 0.03928 ) ? $r / 12.92 : pow( ( ( $r + 0.055 ) / 1.055 ), 2.4 );
43        $g = ( $g <= 0.03928 ) ? $g / 12.92 : pow( ( ( $g + 0.055 ) / 1.055 ), 2.4 );
44        $b = ( $b <= 0.03928 ) ? $b / 12.92 : pow( ( ( $b + 0.055 ) / 1.055 ), 2.4 );
45
46        return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
47    }
48
49    /**
50     * @see Less_Tree::genCSS
51     */
52    public function genCSS( $output ) {
53        $output->add( $this->toCSS() );
54    }
55
56    public function toCSS( $doNotCompress = false ) {
57        $compress = Less_Parser::$options['compress'] && !$doNotCompress;
58        $alpha = $this->fround( $this->alpha );
59        if ( $this->value ) {
60            return $this->value;
61        }
62        //
63        // If we have some transparency, the only way to represent it
64        // is via `rgba`. Otherwise, we use the hex representation,
65        // which has better compatibility with older browsers.
66        // Values are capped between `0` and `255`, rounded and zero-padded.
67        //
68        if ( $alpha < 1 ) {
69            if ( ( $alpha === 0 || $alpha === 0.0 ) && isset( $this->isTransparentKeyword ) && $this->isTransparentKeyword ) {
70                return 'transparent';
71            }
72
73            $values = [];
74            foreach ( $this->rgb as $c ) {
75                $values[] = $this->clamp( round( $c ), 255 );
76            }
77            $values[] = $alpha;
78
79            $glue = ( $compress ? ',' : ', ' );
80            return "rgba(" . implode( $glue, $values ) . ")";
81        } else {
82
83            $color = $this->toRGB();
84
85            if ( $compress ) {
86
87                // Convert color to short format
88                if ( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6] ) {
89                    $color = '#' . $color[1] . $color[3] . $color[5];
90                }
91            }
92
93            return $color;
94        }
95    }
96
97    //
98    // Operations have to be done per-channel, if not,
99    // channels will spill onto each other. Once we have
100    // our result, in the form of an integer triplet,
101    // we create a new Color node to hold the result.
102    //
103
104    /**
105     * @param string $op
106     * @param self $other
107     */
108    public function operate( $op, $other ) {
109        $rgb = [];
110        $alpha = $this->alpha * ( 1 - $other->alpha ) + $other->alpha;
111        for ( $c = 0; $c < 3; $c++ ) {
112            $rgb[$c] = $this->_operate( $op, $this->rgb[$c], $other->rgb[$c] );
113        }
114        return new self( $rgb, $alpha );
115    }
116
117    public function toRGB() {
118        return $this->toHex( $this->rgb );
119    }
120
121    public function toHSL() {
122        $r = $this->rgb[0] / 255;
123        $g = $this->rgb[1] / 255;
124        $b = $this->rgb[2] / 255;
125        $a = $this->alpha;
126
127        $max = max( $r, $g, $b );
128        $min = min( $r, $g, $b );
129        $l = ( $max + $min ) / 2;
130        $d = $max - $min;
131
132        $h = $s = 0;
133        if ( $max !== $min ) {
134            $s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min );
135
136            switch ( $max ) {
137                case $r:
138                    $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 );
139                    break;
140                case $g:
141                    $h = ( $b - $r ) / $d + 2;
142                    break;
143                case $b:
144                    $h = ( $r - $g ) / $d + 4;
145                    break;
146            }
147            $h /= 6;
148        }
149        return [ 'h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a ];
150    }
151
152    // Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
153    public function toHSV() {
154        $r = $this->rgb[0] / 255;
155        $g = $this->rgb[1] / 255;
156        $b = $this->rgb[2] / 255;
157        $a = $this->alpha;
158
159        $max = max( $r, $g, $b );
160        $min = min( $r, $g, $b );
161
162        $v = $max;
163
164        $d = $max - $min;
165        if ( $max === 0 ) {
166            $s = 0;
167        } else {
168            $s = $d / $max;
169        }
170
171        $h = 0;
172        if ( $max !== $min ) {
173            switch ( $max ) {
174                case $r:
175                    $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 );
176                    break;
177                case $g:
178                    $h = ( $b - $r ) / $d + 2;
179                    break;
180                case $b:
181                    $h = ( $r - $g ) / $d + 4;
182                    break;
183            }
184            $h /= 6;
185        }
186        return [ 'h' => $h * 360, 's' => $s, 'v' => $v, 'a' => $a ];
187    }
188
189    public function toARGB() {
190        $argb = array_merge( (array)Less_Parser::round( $this->alpha * 255 ), $this->rgb );
191        return $this->toHex( $argb );
192    }
193
194    /**
195     * @param mixed $x
196     * @return int|null
197     * @see less-2.5.3.js#Color.prototype.compare
198     */
199    public function compare( $x ) {
200        if ( !$x instanceof self ) {
201            return -1;
202        }
203
204        return ( $x->rgb[0] === $this->rgb[0] &&
205            $x->rgb[1] === $this->rgb[1] &&
206            $x->rgb[2] === $this->rgb[2] &&
207            $x->alpha === $this->alpha ) ? 0 : null;
208    }
209
210    /**
211     * @param int|float $val
212     * @param int $max
213     * @return int|float
214     * @see less-2.5.3.js#Color.prototype
215     */
216    private function clamp( $val, $max ) {
217        return min( max( $val, 0 ), $max );
218    }
219
220    public function toHex( $v ) {
221        $ret = '#';
222        foreach ( $v as $c ) {
223            $c = $this->clamp( Less_Parser::round( $c ), 255 );
224            if ( $c < 16 ) {
225                $ret .= '0';
226            }
227            $ret .= dechex( $c );
228        }
229
230        return $ret;
231    }
232
233    /**
234     * @param string $keyword
235     */
236    public static function fromKeyword( $keyword ) {
237        $c = $keyword = strtolower( $keyword );
238
239        if ( Less_Colors::hasOwnProperty( $keyword ) ) {
240            // detect named color
241            $c = new self( substr( Less_Colors::color( $keyword ), 1 ) );
242        }
243
244        if ( $keyword === 'transparent' ) {
245            $c = new self( [ 0, 0, 0 ], 0, true );
246        }
247
248        if ( isset( $c ) && is_object( $c ) ) {
249            $c->value = $keyword;
250            return $c;
251        }
252    }
253
254}