PluginProbe
Elementor Website Builder – more than just a page builder / 3.1.2
Elementor Website Builder – more than just a page builder v3.1.2
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / includes / stylesheet.php

stylesheet.php in Elementor Website Builder – more than just a page builder 3.1.2, at includes/stylesheet.php

443 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit; // Exit if accessed directly.
6 }
7
8 /**
9 * Elementor stylesheet.
10 *
11 * Elementor stylesheet handler class responsible for setting up CSS rules and
12 * properties, and all the CSS `@media` rule with supported viewport width.
13 *
14 * @since 1.0.0
15 */
16 class Stylesheet {
17
18 /**
19 * CSS Rules.
20 *
21 * Holds the list of CSS rules.
22 *
23 * @since 1.0.0
24 * @access private
25 *
26 * @var array A list of CSS rules.
27 */
28 private $rules = [];
29
30 /**
31 * Devices.
32 *
33 * Holds the list of devices.
34 *
35 * @since 1.0.0
36 * @access private
37 *
38 * @var array A list of devices.
39 */
40 private $devices = [];
41
42 /**
43 * Raw CSS.
44 *
45 * Holds the raw CSS.
46 *
47 * @since 1.0.0
48 * @access private
49 *
50 * @var array The raw CSS.
51 */
52 private $raw = [];
53
54 /**
55 * Parse CSS rules.
56 *
57 * Goes over the list of CSS rules and generates the final CSS.
58 *
59 * @since 1.0.0
60 * @access public
61 * @static
62 *
63 * @param array $rules CSS rules.
64 *
65 * @return string Parsed rules.
66 */
67 public static function parse_rules( array $rules ) {
68 $parsed_rules = '';
69
70 foreach ( $rules as $selector => $properties ) {
71 $selector_content = self::parse_properties( $properties );
72
73 if ( $selector_content ) {
74 $parsed_rules .= $selector . '{' . $selector_content . '}';
75 }
76 }
77
78 return $parsed_rules;
79 }
80
81 /**
82 * Parse CSS properties.
83 *
84 * Goes over the selector properties and generates the CSS of the selector.
85 *
86 * @since 1.0.0
87 * @access public
88 * @static
89 *
90 * @param array $properties CSS properties.
91 *
92 * @return string Parsed properties.
93 */
94 public static function parse_properties( array $properties ) {
95 $parsed_properties = '';
96
97 foreach ( $properties as $property_key => $property_value ) {
98 if ( '' !== $property_value ) {
99 $parsed_properties .= $property_key . ':' . $property_value . ';';
100 }
101 }
102
103 return $parsed_properties;
104 }
105
106 /**
107 * Add device.
108 *
109 * Add a new device to the devices list.
110 *
111 * @since 1.0.0
112 * @access public
113 *
114 * @param string $device_name Device name.
115 * @param string $device_max_point Device maximum point.
116 *
117 * @return Stylesheet The current stylesheet class instance.
118 */
119 public function add_device( $device_name, $device_max_point ) {
120 $this->devices[ $device_name ] = $device_max_point;
121
122 asort( $this->devices );
123
124 return $this;
125 }
126
127 /**
128 * Add rules.
129 *
130 * Add a new CSS rule to the rules list.
131 *
132 * @since 1.0.0
133 * @access public
134 *
135 * @param string $selector CSS selector.
136 * @param array|string $style_rules Optional. Style rules. Default is `null`.
137 * @param array $query Optional. Media query. Default is `null`.
138 *
139 * @return Stylesheet The current stylesheet class instance.
140 */
141 public function add_rules( $selector, $style_rules = null, array $query = null ) {
142 $query_hash = 'all';
143
144 if ( $query ) {
145 $query_hash = $this->query_to_hash( $query );
146 }
147
148 if ( ! isset( $this->rules[ $query_hash ] ) ) {
149 $this->add_query_hash( $query_hash );
150 }
151
152 if ( null === $style_rules ) {
153 preg_match_all( '/([^\s].+?(?=\{))\{((?s:.)+?(?=}))}/', $selector, $parsed_rules );
154
155 foreach ( $parsed_rules[1] as $index => $selector ) {
156 $this->add_rules( $selector, $parsed_rules[2][ $index ], $query );
157 }
158
159 return $this;
160 }
161
162 if ( ! isset( $this->rules[ $query_hash ][ $selector ] ) ) {
163 $this->rules[ $query_hash ][ $selector ] = [];
164 }
165
166 if ( is_string( $style_rules ) ) {
167 $style_rules = array_filter( explode( ';', trim( $style_rules ) ) );
168
169 $ordered_rules = [];
170
171 foreach ( $style_rules as $rule ) {
172 $property = explode( ':', $rule, 2 );
173
174 if ( count( $property ) < 2 ) {
175 return $this;
176 }
177
178 $ordered_rules[ trim( $property[0] ) ] = trim( $property[1], ' ;' );
179 }
180
181 $style_rules = $ordered_rules;
182 }
183
184 $this->rules[ $query_hash ][ $selector ] = array_merge( $this->rules[ $query_hash ][ $selector ], $style_rules );
185
186 return $this;
187 }
188
189 /**
190 * Add raw CSS.
191 *
192 * Add a raw CSS rule.
193 *
194 * @since 1.0.8
195 * @access public
196 *
197 * @param string $css The raw CSS.
198 * @param string $device Optional. The device. Default is empty.
199 *
200 * @return Stylesheet The current stylesheet class instance.
201 */
202 public function add_raw_css( $css, $device = '' ) {
203 if ( ! isset( $this->raw[ $device ] ) ) {
204 $this->raw[ $device ] = [];
205 }
206
207 $this->raw[ $device ][] = trim( $css );
208
209 return $this;
210 }
211
212 /**
213 * Get CSS rules.
214 *
215 * Retrieve the CSS rules.
216 *
217 * @since 1.0.5
218 * @access public
219 *
220 * @param string $device Optional. The device. Default is empty.
221 * @param string $selector Optional. CSS selector. Default is empty.
222 * @param string $property Optional. CSS property. Default is empty.
223 *
224 * @return null|array CSS rules, or `null` if not rules found.
225 */
226 public function get_rules( $device = null, $selector = null, $property = null ) {
227 if ( ! $device ) {
228 return $this->rules;
229 }
230
231 if ( $property ) {
232 return isset( $this->rules[ $device ][ $selector ][ $property ] ) ? $this->rules[ $device ][ $selector ][ $property ] : null;
233 }
234
235 if ( $selector ) {
236 return isset( $this->rules[ $device ][ $selector ] ) ? $this->rules[ $device ][ $selector ] : null;
237 }
238
239 return isset( $this->rules[ $device ] ) ? $this->rules[ $device ] : null;
240 }
241
242 /**
243 * To string.
244 *
245 * This magic method responsible for parsing the rules into one CSS string.
246 *
247 * @since 1.0.0
248 * @access public
249 *
250 * @return string CSS style.
251 */
252 public function __toString() {
253 $style_text = '';
254
255 foreach ( $this->rules as $query_hash => $rule ) {
256 $device_text = self::parse_rules( $rule );
257
258 if ( 'all' !== $query_hash ) {
259 $device_text = $this->get_query_hash_style_format( $query_hash ) . '{' . $device_text . '}';
260 }
261
262 $style_text .= $device_text;
263 }
264
265 foreach ( $this->raw as $device_name => $raw ) {
266 $raw = implode( "\n", $raw );
267
268 if ( $raw && isset( $this->devices[ $device_name ] ) ) {
269 $raw = '@media(max-width: ' . $this->devices[ $device_name ] . 'px){' . $raw . '}';
270 }
271
272 $style_text .= $raw;
273 }
274
275 return $style_text;
276 }
277
278 /**
279 * Get device maximum value.
280 *
281 * Retrieve the maximum size of any given device.
282 *
283 * @since 1.2.0
284 * @access private
285 *
286 * @throws \RangeException If max value for this device is out of range.
287 *
288 * @param string $device_name Device name.
289 *
290 * @return int
291 */
292 private function get_device_max_value( $device_name ) {
293 $devices_names = array_keys( $this->devices );
294
295 $device_name_index = array_search( $device_name, $devices_names );
296
297 $next_index = $device_name_index + 1;
298
299 if ( $next_index >= count( $devices_names ) ) {
300 throw new \RangeException( 'Max value for this device is out of range.' );
301 }
302
303 return $this->devices[ $devices_names[ $next_index ] ] - 1;
304 }
305
306 /**
307 * Query to hash.
308 *
309 * Turns the media query into a hashed string that represents the query
310 * endpoint in the rules list.
311 *
312 * @since 1.2.0
313 * @access private
314 *
315 * @param array $query CSS media query.
316 *
317 * @return string Hashed string of the query.
318 */
319 private function query_to_hash( array $query ) {
320 $hash = [];
321
322 foreach ( $query as $endpoint => $value ) {
323 $hash[] = $endpoint . '_' . $value;
324 }
325
326 return implode( '-', $hash );
327 }
328
329 /**
330 * Hash to query.
331 *
332 * Turns the hashed string to an array that contains the data of the query
333 * endpoint.
334 *
335 * @since 1.2.0
336 * @access private
337 *
338 * @param string $hash Hashed string of the query.
339 *
340 * @return array Media query data.
341 */
342 private function hash_to_query( $hash ) {
343 $query = [];
344
345 $hash = array_filter( explode( '-', $hash ) );
346
347 foreach ( $hash as $single_query ) {
348 $query_parts = explode( '_', $single_query );
349
350 $end_point = $query_parts[0];
351
352 $device_name = $query_parts[1];
353
354 $query[ $end_point ] = 'max' === $end_point ? $this->get_device_max_value( $device_name ) : $this->devices[ $device_name ];
355 }
356
357 return $query;
358 }
359
360 /**
361 * Add query hash.
362 *
363 * Register new endpoint query and sort the rules the way they should be
364 * displayed in the final stylesheet based on the device and the viewport
365 * width.
366 *
367 * @since 1.2.0
368 * @access private
369 *
370 * @param string $query_hash Hashed string of the query.
371 */
372 private function add_query_hash( $query_hash ) {
373 $this->rules[ $query_hash ] = [];
374
375 uksort(
376 $this->rules, function( $a, $b ) {
377 if ( 'all' === $a ) {
378 return -1;
379 }
380
381 if ( 'all' === $b ) {
382 return 1;
383 }
384
385 $a_query = $this->hash_to_query( $a );
386
387 $b_query = $this->hash_to_query( $b );
388
389 if ( isset( $a_query['min'] ) xor isset( $b_query['min'] ) ) {
390 return 1;
391 }
392
393 if ( isset( $a_query['min'] ) ) {
394 $range = $a_query['min'] - $b_query['min'];
395
396 if ( $range ) {
397 return $range;
398 }
399
400 $a_has_max = isset( $a_query['max'] );
401
402 if ( $a_has_max xor isset( $b_query['max'] ) ) {
403 return $a_has_max ? 1 : -1;
404 }
405
406 if ( ! $a_has_max ) {
407 return 0;
408 }
409 }
410
411 return $b_query['max'] - $a_query['max'];
412 }
413 );
414 }
415
416 /**
417 * Get query hash style format.
418 *
419 * Retrieve formated media query rule with the endpoint width settings.
420 *
421 * The method returns the CSS `@media` rule and supported viewport width in
422 * pixels. It can also handel multiple width endpoints.
423 *
424 * @since 1.2.0
425 * @access private
426 *
427 * @param string $query_hash The hash of the query.
428 *
429 * @return string CSS media query.
430 */
431 private function get_query_hash_style_format( $query_hash ) {
432 $query = $this->hash_to_query( $query_hash );
433
434 $style_format = [];
435
436 foreach ( $query as $end_point => $value ) {
437 $style_format[] = '(' . $end_point . '-width:' . $value . 'px)';
438 }
439
440 return '@media' . implode( ' and ', $style_format );
441 }
442 }
443