693 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			693 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * Extract Critical CSS
 | 
						|
 *
 | 
						|
 * @package Builder
 | 
						|
 */
 | 
						|
 | 
						|
if ( ! defined( 'ABSPATH' ) ) {
 | 
						|
	exit; // Exit if accessed directly.
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Extract Critical CSS
 | 
						|
 */
 | 
						|
class ET_Builder_Critical_CSS {
 | 
						|
	// Include in Critical CSS the Required Assets (those which don't depends on Content).
 | 
						|
	// To force some of the Required Assets in the BTF, check `maybe_defer_global_asset` method.
 | 
						|
	const INCLUDE_REQUIRED = true;
 | 
						|
	// Used to estimate height for percentage based units like `vh`,`em`, etc.
 | 
						|
	const VIEWPORT_HEIGHT = 1000;
 | 
						|
	const FONT_HEIGHT     = 16;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Is Critical CSS Threshold Height.
 | 
						|
	 *
 | 
						|
	 * @var int
 | 
						|
	 */
 | 
						|
	protected $_above_the_fold_height;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Root element.
 | 
						|
	 *
 | 
						|
	 * @var stdClass
 | 
						|
	 */
 | 
						|
	protected $_root;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Modules.
 | 
						|
	 *
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	protected $_modules = [];
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Modules.
 | 
						|
	 *
 | 
						|
	 * @var stdClass
 | 
						|
	 */
 | 
						|
	protected $_content;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Above The Fold Sections.
 | 
						|
	 *
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	protected $_atf_sections = [];
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Builder Style Manager.
 | 
						|
	 *
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	protected $_builder_styles = [];
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Instance of `ET_Builder_Critical_CSS`.
 | 
						|
	 *
 | 
						|
	 * @var ET_Builder_Critical_CSS
 | 
						|
	 */
 | 
						|
	private static $_instance;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * ET_Builder_Critical_CSS constructor.
 | 
						|
	 */
 | 
						|
	public function __construct() {
 | 
						|
		global $shortname;
 | 
						|
 | 
						|
		if ( et_is_builder_plugin_active() ) {
 | 
						|
			$options                   = get_option( 'et_pb_builder_options', array() );
 | 
						|
			$critical_threshold_height = isset( $options['performance_main_critical_threshold_height'] ) ? $options['performance_main_critical_threshold_height'] : 'Medium';
 | 
						|
		} else {
 | 
						|
			$critical_threshold_height = et_get_option( $shortname . '_critical_threshold_height', 'Medium' );
 | 
						|
		}
 | 
						|
 | 
						|
		if ( 'High' === $critical_threshold_height ) {
 | 
						|
			$this->_above_the_fold_height = 1500;
 | 
						|
		} elseif ( 'Medium' === $critical_threshold_height ) {
 | 
						|
			$this->_above_the_fold_height = 1000;
 | 
						|
		} else {
 | 
						|
			$this->_above_the_fold_height = 500;
 | 
						|
		}
 | 
						|
 | 
						|
		add_filter( 'et_builder_critical_css_enabled', '__return_true' );
 | 
						|
		// Dynamic CSS content shortcode modules.
 | 
						|
		add_filter( 'et_dynamic_assets_modules_atf', [ $this, 'dynamic_assets_modules_atf' ], 10, 2 );
 | 
						|
		// Detect when renderining Above The Fold sections.
 | 
						|
		add_filter( 'pre_do_shortcode_tag', [ $this, 'check_section_start' ], 99, 4 );
 | 
						|
		add_filter( 'do_shortcode_tag', [ $this, 'check_section_end' ], 99, 2 );
 | 
						|
 | 
						|
		// Analyze Builder style manager.
 | 
						|
		add_filter( 'et_builder_module_style_manager', [ $this, 'enable_builder' ] );
 | 
						|
 | 
						|
		// Dynamic CSS content shortcode.
 | 
						|
		add_filter( 'et_global_assets_list', [ $this, 'maybe_defer_global_asset' ], 99 );
 | 
						|
 | 
						|
		if ( self::INCLUDE_REQUIRED ) {
 | 
						|
			add_filter( 'et_dynamic_assets_atf_includes_required', '__return_true' );
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Defer some global assets if threshold is met.
 | 
						|
	 *
 | 
						|
	 * @param array $assets assets to defer.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return array $assets assets to be deferred.
 | 
						|
	 */
 | 
						|
	public function maybe_defer_global_asset( $assets ) {
 | 
						|
		$defer = [
 | 
						|
			'et_divi_footer',
 | 
						|
			'et_divi_gutters_footer',
 | 
						|
			'et_divi_comments',
 | 
						|
		];
 | 
						|
 | 
						|
		foreach ( $defer as $key ) {
 | 
						|
			if ( isset( $assets[ $key ] ) ) {
 | 
						|
				$assets[ $key ]['maybe_defer'] = true;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return $assets;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Force a PageResource to write its content on file, even when empty
 | 
						|
	 *
 | 
						|
	 * @param bool  $force Default value.
 | 
						|
	 * @param array $resource Critical/Deferred PageResources.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return bool
 | 
						|
	 */
 | 
						|
	public function force_resource_write( $force, $resource ) {
 | 
						|
		$styles = $this->_builder_styles;
 | 
						|
		if ( empty( $styles ) ) {
 | 
						|
			return $force;
 | 
						|
		}
 | 
						|
 | 
						|
		$forced_slugs = [
 | 
						|
			$styles['deferred']->slug,
 | 
						|
			$styles['manager']->slug,
 | 
						|
		];
 | 
						|
 | 
						|
		return in_array( $resource->slug, $forced_slugs, true ) ? true : $force;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Analyze Builder style manager.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @param array $styles Style Managers.
 | 
						|
	 *
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function enable_builder( $styles ) {
 | 
						|
		$this->_builder_styles = $styles;
 | 
						|
 | 
						|
		// There are cases where external assets generation might be disabled at runtime,
 | 
						|
		// ensure Critical CSS and Dynamic Assets use the same logic to avoid side effects.
 | 
						|
		if ( ! et_should_generate_dynamic_assets() ) {
 | 
						|
			$this->disable();
 | 
						|
			return $styles;
 | 
						|
		}
 | 
						|
 | 
						|
		add_filter( 'et_core_page_resource_force_write', [ $this, 'force_resource_write' ], 10, 2 );
 | 
						|
		add_filter( 'et_core_page_resource_tag', [ $this, 'builder_style_tag' ], 10, 5 );
 | 
						|
		if ( et_builder_is_mod_pagespeed_enabled() ) {
 | 
						|
			// PageSpeed filters out `preload` links so we gotta use `prefetch` but
 | 
						|
			// Safari doesn't support the latter....
 | 
						|
			add_action( 'wp_body_open', [ $this, 'add_safari_prefetch_workaround' ], 1 );
 | 
						|
		}
 | 
						|
 | 
						|
		return $styles;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Prints deferred Critical CSS stlyesheet.
 | 
						|
	 *
 | 
						|
	 * @param string $tag stylesheet template.
 | 
						|
	 * @param string $slug stylesheet slug.
 | 
						|
	 * @param string $scheme stylesheet URL.
 | 
						|
	 * @param string $onload stylesheet onload attribute.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return string
 | 
						|
	 */
 | 
						|
	public function builder_style_tag( $tag, $slug, $scheme, $onload ) {
 | 
						|
		$deferred = $this->_builder_styles['deferred'];
 | 
						|
		$inlined  = $this->_builder_styles['manager'];
 | 
						|
 | 
						|
		// reason: Stylsheet needs to be printed on demand.
 | 
						|
		// phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
 | 
						|
		// reason: Snake case requires refactor of PageResource.php.
 | 
						|
		// phpcs:disable ET.Sniffs.ValidVariableName.UsedPropertyNotSnakeCase
 | 
						|
		switch ( $slug ) {
 | 
						|
			case $deferred->slug:
 | 
						|
				// Don't enqueue empty resources.
 | 
						|
				if ( 0 === et_()->WPFS()->size( $deferred->path ) ) {
 | 
						|
					return '';
 | 
						|
				}
 | 
						|
 | 
						|
				// Use 'prefetch' when Mod PageSpeed is detected because it removes 'preload' links.
 | 
						|
				$rel = et_builder_is_mod_pagespeed_enabled() ? 'prefetch' : 'preload';
 | 
						|
 | 
						|
				/**
 | 
						|
				 * Filter deferred styles rel attribute.
 | 
						|
				 *
 | 
						|
				 * Mod PageSpeed removes 'preload' links and we attempt to fix that by trying to detect if
 | 
						|
				 * the 'x-mod-pagespeed' (Apache) or 'x-page-speed' (Nginx) header is present and if it is,
 | 
						|
				 * replace 'preload' with 'prefetch'. However, in some cases, like when the request goes through
 | 
						|
				 * a CDN first, we are unable to detect the header. This hook can be used to change the 'rel'
 | 
						|
				 * attribute to use 'prefetch' when our et_builder_is_mod_pagespeed_enabled() function fails
 | 
						|
				 * to detect Mod PageSpeed.
 | 
						|
				 *
 | 
						|
				 * With that out of the way, the only reason I wrote this detailed description is to make Fabio proud.
 | 
						|
				 *
 | 
						|
				 * @since 4.11.3
 | 
						|
				 *
 | 
						|
				 * @param string $rel
 | 
						|
				 */
 | 
						|
				$rel = apply_filters( 'et_deferred_styles_rel', $rel );
 | 
						|
 | 
						|
				// Defer the stylesheet.
 | 
						|
				$template = '<link rel="%4$s" as="style" id="%1$s" href="%2$s" onload="this.onload=null;this.rel=\'stylesheet\';%3$s" />';
 | 
						|
 | 
						|
				return sprintf( $template, $slug, $scheme, $onload, $rel );
 | 
						|
			case $inlined->slug:
 | 
						|
				// Inline the stylesheet.
 | 
						|
				$template = "<style id=\"et-critical-inline-css\">%1\$s</style>\n";
 | 
						|
				$content  = et_()->WPFS()->get_contents( $inlined->path );
 | 
						|
				return sprintf( $template, $content );
 | 
						|
		}
 | 
						|
		// phpcs:enable
 | 
						|
 | 
						|
		return $tag;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Safari doesn't support `prefetch`......
 | 
						|
	 *
 | 
						|
	 * @since 4.10.7
 | 
						|
	 *
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function add_safari_prefetch_workaround() {
 | 
						|
		// .... so we turn it into `preload` using JS.
 | 
						|
		?>
 | 
						|
		<script type="application/javascript">
 | 
						|
			(function() {
 | 
						|
				var relList = document.createElement('link').relList;
 | 
						|
				if (!!(relList && relList.supports && relList.supports('prefetch'))) {
 | 
						|
					// Browser supports `prefetch`, no workaround needed.
 | 
						|
					return;
 | 
						|
				}
 | 
						|
 | 
						|
				var links = document.getElementsByTagName('link');
 | 
						|
				for (var i = 0; i < links.length; i++) {
 | 
						|
					var link = links[i];
 | 
						|
					if ('prefetch' === link.rel) {
 | 
						|
						link.rel = 'preload';
 | 
						|
					}
 | 
						|
				}
 | 
						|
			})();
 | 
						|
		</script>
 | 
						|
		<?php
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Add `set_style` filter when rendering an ATF section.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @param false|string $value Short-circuit return value. Either false or the value to replace the shortcode with.
 | 
						|
	 * @param string       $tag   Shortcode name.
 | 
						|
	 * @param array|string $attr  Shortcode attributes array or empty string.
 | 
						|
	 * @param array        $m     Regular expression match array.
 | 
						|
	 *
 | 
						|
	 * @return false|string
 | 
						|
	 */
 | 
						|
	public function check_section_start( $value, $tag, $attr, $m ) {
 | 
						|
		if ( 'et_pb_section' !== $tag ) {
 | 
						|
			return $value;
 | 
						|
		}
 | 
						|
 | 
						|
		$attrs  = $m[3];
 | 
						|
		$action = 'et_builder_set_style';
 | 
						|
		$filter = [ $this, 'set_style' ];
 | 
						|
		$active = has_filter( $action, $filter );
 | 
						|
 | 
						|
		if ( ! empty( $this->_atf_sections[ $attrs ] ) ) {
 | 
						|
			$this->_atf_sections[ $attrs ]--;
 | 
						|
 | 
						|
			if ( ! $active ) {
 | 
						|
				add_filter( $action, [ $this, 'set_style' ], 10 );
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return $value;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Remove `set_style` filter after rendering an ATF section.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @param string $output Shortcode output.
 | 
						|
	 * @param string $tag    Shortcode name.
 | 
						|
	 *
 | 
						|
	 * @return string
 | 
						|
	 */
 | 
						|
	public function check_section_end( $output, $tag ) {
 | 
						|
		static $section = 0;
 | 
						|
 | 
						|
		if ( 'et_pb_section' !== $tag ) {
 | 
						|
			return $output;
 | 
						|
		}
 | 
						|
 | 
						|
		$action = 'et_builder_set_style';
 | 
						|
		$filter = [ $this, 'set_style' ];
 | 
						|
 | 
						|
		if ( has_filter( $action, $filter ) ) {
 | 
						|
			remove_filter( $action, $filter, 10 );
 | 
						|
		}
 | 
						|
 | 
						|
		return $output;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Filter used to analize content coming from Dynamic Access Class.
 | 
						|
	 *
 | 
						|
	 * @param array  $value Default shortcodes list (empty).
 | 
						|
	 * @param string $content TB/Post Content.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return array List of ATF shortcodes.
 | 
						|
	 */
 | 
						|
	public function dynamic_assets_modules_atf( $value, $content = '' ) {
 | 
						|
		if ( empty( $content ) ) {
 | 
						|
			return $value;
 | 
						|
		}
 | 
						|
 | 
						|
		$modules = $this->extract( $content );
 | 
						|
 | 
						|
		// Dynamic CSS content shortcode.
 | 
						|
		add_filter( 'et_dynamic_assets_content', [ $this, 'dynamic_assets_content' ] );
 | 
						|
 | 
						|
		return $modules;
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns splitted (ATF/BFT) Content.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return stdClass
 | 
						|
	 */
 | 
						|
	public function dynamic_assets_content() {
 | 
						|
		return $this->_content;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * While the filter is applied, any rendered style will be considered critical.
 | 
						|
	 *
 | 
						|
	 * @param array $style Style.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function set_style( $style ) {
 | 
						|
		$style['critical'] = true;
 | 
						|
		return $style;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Parse Content into shortcodes.
 | 
						|
	 *
 | 
						|
	 * @param string $content TB/Post Content.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return array|boolean
 | 
						|
	 */
 | 
						|
	public static function parse_shortcode( $content ) {
 | 
						|
		static $regex;
 | 
						|
 | 
						|
		if ( false === strpos( $content, '[' ) ) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( empty( $regex ) ) {
 | 
						|
			$regex = '/' . get_shortcode_regex() . '/';
 | 
						|
 | 
						|
			// Add missing child shortcodes (because dynamically added).
 | 
						|
			$existing   = 'et_pb_pricing_tables';
 | 
						|
			$shortcodes = [
 | 
						|
				$existing,
 | 
						|
				'et_pb_pricing_item',
 | 
						|
			];
 | 
						|
 | 
						|
			$regex = str_replace( $existing, join( '|', $shortcodes ), $regex );
 | 
						|
		}
 | 
						|
 | 
						|
		preg_match_all( $regex, $content, $matches, PREG_SET_ORDER );
 | 
						|
 | 
						|
		return $matches;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Estimates height to split Content in ATF/BTF.
 | 
						|
	 *
 | 
						|
	 * @param string $content TB/Post Content.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return array List of ATF shortcodes.
 | 
						|
	 */
 | 
						|
	public function extract( $content ) {
 | 
						|
		// Create root object when needed.
 | 
						|
		if ( empty( $this->_root ) ) {
 | 
						|
			$this->_root = (object) [
 | 
						|
				'tag'    => 'root',
 | 
						|
				'height' => 0,
 | 
						|
			];
 | 
						|
		}
 | 
						|
 | 
						|
		if ( $this->_root->height >= $this->_above_the_fold_height ) {
 | 
						|
			// Do nothing when root already exists and its height >= treshold.
 | 
						|
			return [];
 | 
						|
		}
 | 
						|
 | 
						|
		$shortcodes = self::parse_shortcode( $content );
 | 
						|
 | 
						|
		if ( ! is_array( $shortcodes ) ) {
 | 
						|
			return [];
 | 
						|
		}
 | 
						|
 | 
						|
		$shortcodes        = array_reverse( $shortcodes );
 | 
						|
		$is_above_the_fold = true;
 | 
						|
		$root              = $this->_root;
 | 
						|
		$root->count       = count( $shortcodes );
 | 
						|
		$stack             = [ $root ];
 | 
						|
		$parent            = end( $stack );
 | 
						|
		$tags              = [];
 | 
						|
		$atf_content       = '';
 | 
						|
		$btf_content       = '';
 | 
						|
 | 
						|
		$structure_slugs = [
 | 
						|
			'et_pb_section',
 | 
						|
			'et_pb_row',
 | 
						|
			'et_pb_row_inner',
 | 
						|
			'et_pb_column',
 | 
						|
			'et_pb_column_inner',
 | 
						|
		];
 | 
						|
 | 
						|
		$section           = '';
 | 
						|
		$section_shortcode = '';
 | 
						|
 | 
						|
		// phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
 | 
						|
		while ( $is_above_the_fold && $shortcode = array_pop( $shortcodes ) ) {
 | 
						|
			list( $raw,, $tag, $attrs,, $content ) = $shortcode;
 | 
						|
 | 
						|
			$tags[]   = $tag;
 | 
						|
			$children = self::parse_shortcode( $content );
 | 
						|
			$element  = (object) [
 | 
						|
				'tag'      => $tag,
 | 
						|
				'children' => [],
 | 
						|
				'height'   => 0,
 | 
						|
				'margin'   => 0,
 | 
						|
				'padding'  => 0,
 | 
						|
				'attrs'    => [],
 | 
						|
			];
 | 
						|
 | 
						|
			switch ( $tag ) {
 | 
						|
				case 'et_pb_pricing_table':
 | 
						|
					$lines   = array_filter( explode( "\n", str_replace( array( '<p>', '</p>', '<br />' ), "\n", $content ) ) );
 | 
						|
					$content = '';
 | 
						|
 | 
						|
					foreach ( $lines as $line ) {
 | 
						|
						$content .= sprintf( '[et_pb_pricing_item]%s[/et_pb_pricing_item]', trim( $line ) );
 | 
						|
					}
 | 
						|
 | 
						|
					$children = self::parse_shortcode( $content );
 | 
						|
					break;
 | 
						|
				case 'et_pb_section':
 | 
						|
					$section           = $attrs;
 | 
						|
					$section_shortcode = $raw;
 | 
						|
					break;
 | 
						|
			}
 | 
						|
 | 
						|
			$props = shortcode_parse_atts( $attrs );
 | 
						|
 | 
						|
			if ( isset( $props['custom_margin'] ) ) {
 | 
						|
				$margin          = self::get_margin_padding_height( $props['custom_margin'] );
 | 
						|
				$element->margin = $margin;
 | 
						|
				if ( $margin > 0 ) {
 | 
						|
					$element->height += $margin;
 | 
						|
					$element->attrs[] = 'margin:' . $props['custom_margin'] . "-> $margin";
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if ( isset( $props['custom_padding'] ) ) {
 | 
						|
				$padding          = self::get_margin_padding_height( $props['custom_padding'] );
 | 
						|
				$element->padding = $padding;
 | 
						|
				if ( $padding > 0 ) {
 | 
						|
					$element->height += $padding;
 | 
						|
					$element->attrs[] = 'padding:' . $props['custom_padding'] . "-> $padding";
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if ( false !== $children ) {
 | 
						|
				// Non empty structure element.
 | 
						|
				$element->count = count( $children );
 | 
						|
				$stack[]        = $element;
 | 
						|
				$shortcodes     = array_merge( $shortcodes, array_reverse( $children ) );
 | 
						|
			} else {
 | 
						|
				// Only add default content height for modules, not empty structure.
 | 
						|
				if ( ! in_array( $tag, $structure_slugs, true ) ) {
 | 
						|
					$element->height += 100;
 | 
						|
				}
 | 
						|
				do {
 | 
						|
					$parent = end( $stack );
 | 
						|
 | 
						|
					switch ( $element->tag ) {
 | 
						|
						case 'et_pb_column':
 | 
						|
						case 'et_pb_column_inner':
 | 
						|
							// Do nothing.
 | 
						|
							break;
 | 
						|
						case 'et_pb_row':
 | 
						|
						case 'et_pb_row_inner':
 | 
						|
							// Row height is determined by its tallest column.
 | 
						|
							$max = 0;
 | 
						|
 | 
						|
							foreach ( $element->children as $column ) {
 | 
						|
								$max = max( $max, $column->height );
 | 
						|
							}
 | 
						|
 | 
						|
							$element->height += $max;
 | 
						|
							$parent->height  += $element->height;
 | 
						|
							break;
 | 
						|
						case 'et_pb_section':
 | 
						|
							// Update Above The Fold Sections.
 | 
						|
							if ( isset( $this->_atf_sections[ $section ] ) ) {
 | 
						|
								$this->_atf_sections[ $section ]++;
 | 
						|
							} else {
 | 
						|
								$this->_atf_sections[ $section ] = 1;
 | 
						|
							}
 | 
						|
 | 
						|
							$atf_content  .= $section_shortcode;
 | 
						|
							$root->height += $element->height;
 | 
						|
 | 
						|
							if ( $root->height >= $this->_above_the_fold_height ) {
 | 
						|
								$is_above_the_fold = false;
 | 
						|
							}
 | 
						|
							break;
 | 
						|
						default:
 | 
						|
							$parent->height += $element->height;
 | 
						|
					}
 | 
						|
 | 
						|
					$parent->children[] = $element;
 | 
						|
 | 
						|
					if ( 0 !== --$parent->count ) {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
 | 
						|
					$element = $parent;
 | 
						|
					array_pop( $stack );
 | 
						|
					if ( empty( $stack ) ) {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				} while ( $is_above_the_fold && 0 !== --$parent->count );
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		foreach ( $shortcodes as $shortcode ) {
 | 
						|
			$btf_content .= $shortcode[0];
 | 
						|
		}
 | 
						|
 | 
						|
		$tags           = array_unique( $tags );
 | 
						|
		$this->_modules = array_unique( array_merge( $this->_modules, $tags ) );
 | 
						|
		$this->_content = (object) [
 | 
						|
			'atf' => $atf_content,
 | 
						|
			'btf' => $btf_content,
 | 
						|
		];
 | 
						|
 | 
						|
		return $tags;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Calculate margin and padding.
 | 
						|
	 *
 | 
						|
	 * @param string $value Margin and padding values.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return int margin/padding height value.
 | 
						|
	 */
 | 
						|
	public static function get_margin_padding_height( $value ) {
 | 
						|
		$values = explode( '|', $value );
 | 
						|
 | 
						|
		if ( empty( $values ) ) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		// Only top/bottom values are needed.
 | 
						|
		$values = array_map( 'trim', [ $values[0], $values[2] ] );
 | 
						|
		$total  = 0;
 | 
						|
 | 
						|
		foreach ( $values as $value ) {
 | 
						|
			if ( '' === $value ) {
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			$unit = et_pb_get_value_unit( $value );
 | 
						|
 | 
						|
			// Remove the unit, if present.
 | 
						|
			if ( false !== strpos( $value, $unit ) ) {
 | 
						|
				$value = substr( $value, 0, -strlen( $unit ) );
 | 
						|
			}
 | 
						|
 | 
						|
			$value = (int) $value;
 | 
						|
 | 
						|
			switch ( $unit ) {
 | 
						|
				case 'rem':
 | 
						|
				case 'em':
 | 
						|
					$value *= self::FONT_HEIGHT;
 | 
						|
					break;
 | 
						|
				case 'vh':
 | 
						|
					$value = ( $value * self::VIEWPORT_HEIGHT ) / 100;
 | 
						|
					break;
 | 
						|
				case 'px':
 | 
						|
					break;
 | 
						|
				default:
 | 
						|
					$value = 0;
 | 
						|
			}
 | 
						|
 | 
						|
			$total += $value;
 | 
						|
		}
 | 
						|
 | 
						|
		return $total;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Disable Critical CSS.
 | 
						|
	 *
 | 
						|
	 * @since 4.12.0
 | 
						|
	 *
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function disable() {
 | 
						|
		remove_filter( 'et_builder_critical_css_enabled', '__return_true' );
 | 
						|
		remove_filter( 'et_dynamic_assets_modules_atf', [ $this, 'dynamic_assets_modules_atf' ] );
 | 
						|
		remove_filter( 'pre_do_shortcode_tag', [ $this, 'check_section_start' ] );
 | 
						|
		remove_filter( 'do_shortcode_tag', [ $this, 'check_section_end' ] );
 | 
						|
		remove_filter( 'et_builder_module_style_manager', [ $this, 'enable_builder' ] );
 | 
						|
		remove_filter( 'et_global_assets_list', [ $this, 'maybe_defer_global_asset' ] );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Get the class instance.
 | 
						|
	 *
 | 
						|
	 * @since 4.10.0
 | 
						|
	 *
 | 
						|
	 * @return ET_Builder_Critical_CSS
 | 
						|
	 */
 | 
						|
	public static function instance() {
 | 
						|
		if ( ! self::$_instance ) {
 | 
						|
			self::$_instance = new self();
 | 
						|
		}
 | 
						|
 | 
						|
		return self::$_instance;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
ET_Builder_Critical_CSS::instance();
 |