Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.41% covered (success)
90.41%
66 / 73
58.33% covered (warning)
58.33%
7 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
Less_Tree_Media
90.41% covered (success)
90.41%
66 / 73
58.33% covered (warning)
58.33%
7 / 12
30.79
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 accept
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 genCSS
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 compile
80.00% covered (warning)
80.00%
12 / 15
0.00% covered (danger)
0.00%
0 / 1
4.13
 variable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 find
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 emptySelectors
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 markReferenced
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 compileTop
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 compileNested
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
8
 permute
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
7.02
 bubbleSelectors
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
1<?php
2declare( strict_types = 1 );
3
4/**
5 * @private
6 */
7class Less_Tree_Media extends Less_Tree {
8
9    /** @var Less_Tree_Value */
10    public $features;
11    /** @var Less_Tree_Ruleset[] */
12    public $rules;
13    /** @var int|null */
14    public $index;
15    /** @var array|null */
16    public $currentFileInfo;
17    /** @var bool|null */
18    public $isReferenced;
19
20    public function __construct( $value = [], $features = [], $index = null, $currentFileInfo = null ) {
21        $this->index = $index;
22        $this->currentFileInfo = $currentFileInfo;
23
24        $selectors = $this->emptySelectors();
25
26        $this->features = new Less_Tree_Value( $features );
27
28        $this->rules = [ new Less_Tree_Ruleset( $selectors, $value ) ];
29        $this->rules[0]->allowImports = true;
30    }
31
32    public function accept( $visitor ) {
33        $this->features = $visitor->visitObj( $this->features );
34        $this->rules = $visitor->visitArray( $this->rules );
35    }
36
37    /**
38     * @see Less_Tree::genCSS
39     */
40    public function genCSS( $output ) {
41        $output->add( '@media ', $this->currentFileInfo, $this->index );
42        $this->features->genCSS( $output );
43        Less_Tree::outputRuleset( $output, $this->rules );
44    }
45
46    /**
47     * @param Less_Environment $env
48     * @return self|Less_Tree_Ruleset
49     * @see less-2.5.3.js#Media.prototype.eval
50     */
51    public function compile( $env ) {
52        $media = new self( [], [], $this->index, $this->currentFileInfo );
53
54        $mathBypass = false;
55        if ( !$env->mathOn ) {
56            $mathBypass = true;
57            $env->mathOn = true;
58        }
59
60        $media->features = $this->features->compile( $env );
61
62        if ( $mathBypass ) {
63            $env->mathOn = false;
64        }
65
66        $env->mediaPath[] = $media;
67        $env->mediaBlocks[] = $media;
68
69        array_unshift( $env->frames, $this->rules[0] );
70        $media->rules = [ $this->rules[0]->compile( $env ) ];
71        array_shift( $env->frames );
72
73        array_pop( $env->mediaPath );
74
75        return !$env->mediaPath ? $media->compileTop( $env ) : $media->compileNested( $env );
76    }
77
78    public function variable( $name ) {
79        return $this->rules[0]->variable( $name );
80    }
81
82    public function find( $selector ) {
83        return $this->rules[0]->find( $selector, $this );
84    }
85
86    public function emptySelectors() {
87        $el = new Less_Tree_Element( '', '&', $this->index, $this->currentFileInfo );
88        $sels = [ new Less_Tree_Selector( [ $el ], [], null, $this->index, $this->currentFileInfo ) ];
89        $sels[0]->mediaEmpty = true;
90        return $sels;
91    }
92
93    public function markReferenced() {
94        $this->rules[0]->markReferenced();
95        $this->isReferenced = true;
96        Less_Tree::ReferencedArray( $this->rules[0]->rules );
97    }
98
99    // evaltop
100    public function compileTop( $env ) {
101        $result = $this;
102
103        if ( count( $env->mediaBlocks ) > 1 ) {
104            $selectors = $this->emptySelectors();
105            $result = new Less_Tree_Ruleset( $selectors, $env->mediaBlocks );
106            $result->multiMedia = true;
107        }
108
109        $env->mediaBlocks = [];
110        $env->mediaPath = [];
111
112        return $result;
113    }
114
115    /**
116     * @param Less_Environment $env
117     * @return Less_Tree_Ruleset
118     */
119    public function compileNested( $env ) {
120        $path = array_merge( $env->mediaPath, [ $this ] );
121        '@phan-var self[] $path';
122
123        // Extract the media-query conditions separated with `,` (OR).
124        foreach ( $path as $key => $p ) {
125            $value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
126            $path[$key] = is_array( $value ) ? $value : [ $value ];
127        }
128        '@phan-var array<array<Less_Tree>> $path';
129
130        // Trace all permutations to generate the resulting media-query.
131        //
132        // (a, b and c) with nested (d, e) ->
133        //    a and d
134        //    a and e
135        //    b and c and d
136        //    b and c and e
137
138        $permuted = $this->permute( $path );
139        '@phan-var (Less_Tree|string)[][] $permuted';
140        $expressions = [];
141        foreach ( $permuted as $path ) {
142
143            for ( $i = 0, $len = count( $path ); $i < $len; $i++ ) {
144                $path[$i] = $path[$i] instanceof Less_Tree ? $path[$i] : new Less_Tree_Anonymous( $path[$i] );
145            }
146
147            for ( $i = count( $path ) - 1; $i > 0; $i-- ) {
148                array_splice( $path, $i, 0, [ new Less_Tree_Anonymous( 'and' ) ] );
149            }
150
151            $expressions[] = new Less_Tree_Expression( $path );
152        }
153        $this->features = new Less_Tree_Value( $expressions );
154
155        // Fake a tree-node that doesn't output anything.
156        return new Less_Tree_Ruleset( [], [] );
157    }
158
159    public function permute( $arr ) {
160        if ( !$arr ) {
161            return [];
162        }
163
164        if ( count( $arr ) == 1 ) {
165            return $arr[0];
166        }
167
168        $result = [];
169        $rest = $this->permute( array_slice( $arr, 1 ) );
170        foreach ( $rest as $r ) {
171            foreach ( $arr[0] as $a ) {
172                $result[] = array_merge(
173                    is_array( $a ) ? $a : [ $a ],
174                    is_array( $r ) ? $r : [ $r ]
175                );
176            }
177        }
178
179        return $result;
180    }
181
182    public function bubbleSelectors( $selectors ) {
183        if ( !$selectors ) {
184            return;
185        }
186
187        $this->rules = [ new Less_Tree_Ruleset( $selectors, [ $this->rules[0] ] ) ];
188    }
189
190}