balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

stylesheet.php (9122B)


      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 	 * Query to hash.
    280 	 *
    281 	 * Turns the media query into a hashed string that represents the query
    282 	 * endpoint in the rules list.
    283 	 *
    284 	 * @since 1.2.0
    285 	 * @access private
    286 	 *
    287 	 * @param array $query CSS media query.
    288 	 *
    289 	 * @return string Hashed string of the query.
    290 	 */
    291 	private function query_to_hash( array $query ) {
    292 		$hash = [];
    293 
    294 		foreach ( $query as $endpoint => $value ) {
    295 			$hash[] = $endpoint . '_' . $value;
    296 		}
    297 
    298 		return implode( '-', $hash );
    299 	}
    300 
    301 	/**
    302 	 * Hash to query.
    303 	 *
    304 	 * Turns the hashed string to an array that contains the data of the query
    305 	 * endpoint.
    306 	 *
    307 	 * @since 1.2.0
    308 	 * @access private
    309 	 *
    310 	 * @param string $hash Hashed string of the query.
    311 	 *
    312 	 * @return array Media query data.
    313 	 */
    314 	private function hash_to_query( $hash ) {
    315 		$query = [];
    316 
    317 		$hash = array_filter( explode( '-', $hash ) );
    318 
    319 		foreach ( $hash as $single_query ) {
    320 			preg_match( '/(min|max)_(.*)/', $single_query, $query_parts );
    321 
    322 			$end_point = $query_parts[1];
    323 
    324 			$device_name = $query_parts[2];
    325 
    326 			$query[ $end_point ] = 'max' === $end_point ? $this->devices[ $device_name ] : Plugin::$instance->breakpoints->get_device_min_breakpoint( $device_name );
    327 		}
    328 
    329 		return $query;
    330 	}
    331 
    332 	/**
    333 	 * Add query hash.
    334 	 *
    335 	 * Register new endpoint query and sort the rules the way they should be
    336 	 * displayed in the final stylesheet based on the device and the viewport
    337 	 * width.
    338 	 *
    339 	 * @since 1.2.0
    340 	 * @access private
    341 	 *
    342 	 * @param string $query_hash Hashed string of the query.
    343 	 */
    344 	private function add_query_hash( $query_hash ) {
    345 		$this->rules[ $query_hash ] = [];
    346 
    347 		uksort(
    348 			$this->rules, function( $a, $b ) {
    349 				if ( 'all' === $a ) {
    350 					return -1;
    351 				}
    352 
    353 				if ( 'all' === $b ) {
    354 					return 1;
    355 				}
    356 
    357 				$a_query = $this->hash_to_query( $a );
    358 
    359 				$b_query = $this->hash_to_query( $b );
    360 
    361 				if ( isset( $a_query['min'] ) xor isset( $b_query['min'] ) ) {
    362 					return 1;
    363 				}
    364 
    365 				if ( isset( $a_query['min'] ) ) {
    366 					$range = $a_query['min'] - $b_query['min'];
    367 
    368 					if ( $range ) {
    369 						return $range;
    370 					}
    371 
    372 					$a_has_max = isset( $a_query['max'] );
    373 
    374 					if ( $a_has_max xor isset( $b_query['max'] ) ) {
    375 						return $a_has_max ? 1 : -1;
    376 					}
    377 
    378 					if ( ! $a_has_max ) {
    379 						return 0;
    380 					}
    381 				}
    382 
    383 				return $b_query['max'] - $a_query['max'];
    384 			}
    385 		);
    386 	}
    387 
    388 	/**
    389 	 * Get query hash style format.
    390 	 *
    391 	 * Retrieve formated media query rule with the endpoint width settings.
    392 	 *
    393 	 * The method returns the CSS `@media` rule and supported viewport width in
    394 	 * pixels. It can also handel multiple width endpoints.
    395 	 *
    396 	 * @since 1.2.0
    397 	 * @access private
    398 	 *
    399 	 * @param string $query_hash The hash of the query.
    400 	 *
    401 	 * @return string CSS media query.
    402 	 */
    403 	private function get_query_hash_style_format( $query_hash ) {
    404 		$query = $this->hash_to_query( $query_hash );
    405 
    406 		$style_format = [];
    407 
    408 		foreach ( $query as $end_point => $value ) {
    409 			$style_format[] = '(' . $end_point . '-width:' . $value . 'px)';
    410 		}
    411 
    412 		return '@media' . implode( ' and ', $style_format );
    413 	}
    414 }