ensure_cache_directory_exists(); $this->init_hooks(); } /** * Ensure cache directory exists. */ public function ensure_cache_directory_exists() { // Create the base cache directory, if not exists already. $cache_dir = et_core_cache_dir()->path; et_()->ensure_directory_exists( $cache_dir ); } /** * Initialize ET_Dynamic_Assets class. */ public static function init() { if ( null === self::$_instance ) { self::$_instance = new ET_Dynamic_Assets(); } return self::$_instance; } /** * Init hooks. */ public function init_hooks() { add_action( 'wp', array( $this, 'initial_setup' ), 999 ); // Enqueue early assets. add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_dynamic_assets' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_dynamic_scripts_early' ) ); // Enqueue scripts and generate assets if late shortcodes or attributes are detected. add_action( 'wp_footer', array( $this, 'process_late_detection_and_output' ) ); add_action( 'wp_footer', array( $this, 'enqueue_dynamic_scripts_late' ) ); // Add script that loads fallback .css during blog module ajax pagination. add_action( 'wp_footer', array( $this, 'maybe_inject_fallback_dynamic_assets' ) ); // If a late file was generated, we grab it in the footer and then inject it into the header. add_action( 'et_dynamic_late_assets_generated', array( $this, 'maybe_inject_late_dynamic_assets' ), 0 ); } /** * Initial setup. */ public function initial_setup() { // Don't do anything if it's not needed. if ( ! $this->should_initiate() ) { return; } global $post; global $shortname; if ( $this->is_taxonomy() ) { $this->_object_id = intval( get_queried_object()->term_id ); } elseif ( ! empty( $post->ID ) ) { $this->_object_id = intval( $post->ID ); } elseif ( is_search() ) { $this->_object_id = -1; } $this->_folder_name = $this->get_folder_name(); // Don't process Dynamic CSS logic if it's not needed or can't be processed. if ( ! $this->is_cachable_request() ) { return; } if ( 'divi' === $shortname ) { $this->_owner = 'divi'; } elseif ( 'extra' === $shortname ) { $this->_owner = 'extra'; } elseif ( et_is_builder_plugin_active() ) { $this->_owner = 'divi-builder'; } $this->_post_id = ! empty( $post ) ? intval( $post->ID ) : -1; $this->_tb_template_ids = $this->get_theme_builder_template_ids(); $content_retriever = \Feature\ContentRetriever\ET_Builder_Content_Retriever::init(); $this->_all_content = $content_retriever->get_entire_page_content( $post ); $this->_cache_dir_path = et_core_cache_dir()->path; $this->_cache_dir_url = et_core_cache_dir()->url; $this->_product_dir = et_is_builder_plugin_active() ? ET_BUILDER_PLUGIN_URI : get_template_directory_uri(); $this->_cpt_suffix = et_builder_should_wrap_styles() && ! et_is_builder_plugin_active() ? '_cpt' : ''; $this->is_rtl = is_rtl(); $this->_rtl_suffix = $this->is_rtl ? '_rtl' : ''; $this->_page_builder_used = is_singular() ? et_pb_is_pagebuilder_used( $this->_post_id ) : false; // Create asset directory, if it does not exist. $ds = DIRECTORY_SEPARATOR; $file_dir = "{$this->_cache_dir_path}{$ds}{$this->_folder_name}{$ds}"; et_()->ensure_directory_exists( $file_dir ); // If cached shortcodes exist, grab them from the post meta. if ( $this->metadata_exists( '_et_dynamic_cached_shortcodes' ) ) { $this->_early_shortcodes = $this->metadata_get( '_et_dynamic_cached_shortcodes' ); } // If cached attributes exist, grab them from the post meta. if ( $this->metadata_exists( '_et_dynamic_cached_attributes' ) ) { $this->_early_attributes = $this->metadata_get( '_et_dynamic_cached_attributes' ); } // If there are no cached shortcodes, parse the post content to retrieve used shortcodes. if ( empty( $this->_early_shortcodes ) ) { $this->_early_shortcodes = $this->get_early_shortcodes( $this->_all_content ); // Cache the found shortcodes in post meta. $this->metadata_set( '_et_dynamic_cached_shortcodes', $this->_early_shortcodes ); } // If dynamic asset files do not exist, generate them. $files = (array) glob( "{$this->_cache_dir_path}/{$this->_folder_name}/et*-dynamic*" ); if ( empty( $files ) ) { $this->generate_dynamic_assets(); } } /** * Check if current page is a taxonomy page. * * @return boolean * @since 4.10.0 */ public function is_taxonomy() { return is_tax() || is_category() || is_tag(); } /** * Get cache directory name for the current page. * * @return string * @since 4.10.0 */ public function get_folder_name() { $folder_name = $this->_object_id; if ( $this->is_taxonomy() ) { $queried = get_queried_object(); $taxonomy = sanitize_key( $queried->taxonomy ); $folder_name = "taxonomy/{$taxonomy}/" . $this->_object_id; } elseif ( is_search() ) { $folder_name = 'search'; } elseif ( is_author() ) { $author_id = intval( get_queried_object_id() ); $folder_name = "author/{$author_id}"; } elseif ( is_archive() ) { $folder_name = 'archive'; } elseif ( is_home() ) { $folder_name = 'home'; } elseif ( is_404() ) { $folder_name = 'notfound'; } return $folder_name; } /** * Get dynamic assets of a page. * * @return array|void * @since 4.10.0 */ public function get_dynamic_assets_files() { if ( ! $this->is_cachable_request() ) { return; } $dynamic_assets_files = array(); $files = (array) glob( "{$this->_cache_dir_path}/{$this->_folder_name}/et*-dynamic*" ); if ( empty( $files ) ) { return array(); } foreach ( $files as $file ) { $file_path = et_()->normalize_path( $file ); $dynamic_assets_files[] = et_()->path( $this->_cache_dir_url, $this->_folder_name, basename( $file_path ) ); } return $dynamic_assets_files; } /** * Check to see if Dynamic Assets ia applicable to current page request. * * @return bool. * @since 4.10.0 */ public function is_cachable_request() { if ( is_null( self::$_is_cachable_request ) ) { self::$_is_cachable_request = true; // Bail if this is not a front-end page request. if ( ! et_should_generate_dynamic_assets() ) { self::$_is_cachable_request = false; return self::$_is_cachable_request; } // Bail if Dynamic CSS is disabled. if ( ! et_use_dynamic_css() ) { self::$_is_cachable_request = false; return self::$_is_cachable_request; } // Bail if the page has no designated cache folder and is not cachable. if ( ! $this->_folder_name ) { self::$_is_cachable_request = false; return self::$_is_cachable_request; } } return self::$_is_cachable_request; } /** * Check to see if we should initiate initial class logic. * * @return bool. * @since 4.10.0 */ public function should_initiate() { // Bail if this is not a front-end or builder page request. if ( ! et_builder_is_frontend_or_builder() ) { return false; } // Bail if Dynamic CSS and Dynamic JS are both disabled. if ( ! et_use_dynamic_css() && et_disable_js_on_demand() ) { return false; } // Bail if feed since CSS isn't needed for RSS/Atom. if ( is_feed() ) { return false; } return true; } /** * Get the handle of current theme's style.css handle. * * @since 4.10.0 */ public function get_style_css_handle() { global $shortname; $child_theme_suffix = is_child_theme() && ! et_is_builder_plugin_active() ? '-parent' : ''; $inline_style_suffix = et_core_is_inline_stylesheet_enabled() ? '-inline' : ''; $product_prefix = $this->_owner . '-style'; $handle = 'divi-builder-style' === $product_prefix . $inline_style_suffix ? $product_prefix : $product_prefix . $child_theme_suffix . $inline_style_suffix; return $handle; } /** * Enqueues the assets needed for the modules that are present on the page. * * @since 4.10.0 */ public function enqueue_dynamic_assets() { $dynamic_assets = $this->get_dynamic_assets_files(); if ( empty( $dynamic_assets ) || ! et_use_dynamic_css() ) { return; } $body = []; $head = []; $base_url = et_core_cache_dir()->url; $base_path = et_core_cache_dir()->path; foreach ( $dynamic_assets as $dynamic_asset ) { // Ignore empty files. $abs_file = str_replace( $base_url, $base_path, $dynamic_asset ); if ( 0 === et_()->WPFS()->size( $abs_file ) ) { continue; } $type = pathinfo( wp_parse_url( $dynamic_asset, PHP_URL_PATH ), PATHINFO_EXTENSION ); $filename = pathinfo( wp_parse_url( $dynamic_asset, PHP_URL_PATH ), PATHINFO_FILENAME ); $filepath = et_()->path( $this->_cache_dir_path, $this->_folder_name, "{$filename}.{$type}" ); // Bust PHP's stats cache for the resource file to ensure we get the latest timestamp. clearstatcache( true, $filepath ); $filetime = filemtime( $filepath ); $version = $filetime ? $filetime : ET_BUILDER_PRODUCT_VERSION; $is_late = false !== strpos( $filename, 'late' ); $is_critical = false !== strpos( $filename, 'critical' ); $is_css = 'css' === $type; $late_slug = true === $is_late ? '-late' : ''; $deps = array( $this->get_style_css_handle() ); $handle = $this->_owner . '-dynamic' . $late_slug; if ( wp_style_is( $handle ) ) { continue; } $in_footer = false !== strpos( $dynamic_asset, 'footer' ); $asset = (object) [ 'type' => $type, 'src' => $dynamic_asset, 'deps' => $deps, 'in_footer' => $is_css ? 'all' : $in_footer, ]; if ( $is_critical ) { $body[ $handle ] = $asset; } else { $head[ $handle ] = $asset; } } // Enqueue inline styles. if ( ! empty( $body ) ) { $this->_enqueued_assets = (object) [ 'head' => $head, 'body' => $body, ]; $cache_dir = et_core_cache_dir(); $path = $cache_dir->path; $url = $cache_dir->url; $styles = ''; $handle = ''; foreach ( $this->_enqueued_assets->body as $handle => $asset ) { $file = str_replace( $url, $path, $asset->src ); $styles .= et_()->WPFS()->get_contents( $file ); } $handle .= '-critical'; // Create empty style which will enqueue no external file but still allow us // to add inline content to it. wp_register_style( $handle, false, array( $this->get_style_css_handle() ), $version ); wp_enqueue_style( $handle ); wp_add_inline_style( $handle, $styles ); add_filter( 'style_loader_tag', [ $this, 'defer_head_style' ], 10, 4 ); } // Enqueue styles. foreach ( $head as $handle => $asset ) { $is_css = 'css' === $asset->type; $enqueue_function = $is_css ? 'wp_enqueue_style' : 'wp_enqueue_script'; $enqueue_function( $handle, $asset->src, $asset->deps, $version, $asset->in_footer ); } } /** * Print deferred styles in the head. * * @since 4.10.0 * * @param string $tag The link tag for the enqueued style. * @param string $handle The style's registered handle. * @param string $href The stylesheet's source URL. * @param string $media The stylesheet's media attribute. * * @return string */ public function defer_head_style( $tag, $handle, $href, $media ) { if ( empty( $this->_enqueued_assets->head[ $handle ] ) ) { // Ignore assets not enqueued by this class. return $tag; } // Use 'prefetch' when Mod PageSpeed is detected because it removes 'preload' links. $rel = et_builder_is_mod_pagespeed_enabled() ? 'prefetch' : 'preload'; /* This filter is documented in /includes/builder/feature/CriticalCSS.php */ $rel = apply_filters( 'et_deferred_styles_rel', $rel ); return sprintf( "\n", $rel, $handle, $href, $media, "this.onload=null;this.rel='stylesheet'" ); } /** * Generates asset files to be combined on the front-end. * * @param array $assets_data Assets Data. * @param string $suffix Additional file name suffix. * * @return void * @since 4.10.0 */ public function generate_dynamic_assets_files( $assets_data = array(), $suffix = '' ) { global $wp_filesystem; if ( ! $this->is_cachable_request() ) { return; } $tb_ids = ''; $current_tb_template_ids = $this->_tb_template_ids; $late_suffix = ''; $file_contents = ''; if ( $this->_need_late_generation ) { $late_suffix = '-late'; } if ( ! empty( $current_tb_template_ids ) ) { foreach ( $current_tb_template_ids as $key => $value ) { $current_tb_template_ids[ $key ] = 'tb-' . $value; } $tb_ids = '-' . implode( '-', $current_tb_template_ids ); } $ds = DIRECTORY_SEPARATOR; $file_dir = "{$this->_cache_dir_path}{$ds}{$this->_folder_name}{$ds}"; $maybe_post_id = is_singular() ? '-' . $this->_post_id : ''; $suffix = empty( $suffix ) ? '' : "-{$suffix}"; $file_name = "et-{$this->_owner}-dynamic{$tb_ids}{$maybe_post_id}{$late_suffix}{$suffix}.css"; $file_path = et_()->normalize_path( "{$file_dir}{$file_name}" ); $lock_file = wp_tempnam( $file_name ); // Iterate over all the asset data to generate dynamic asset files. foreach ( $assets_data as $file_type => $data ) { $file_contents .= implode( "\n", array_unique( $data['content'] ) ); } if ( file_exists( $file_path ) ) { return; } // Try to create a temporary directory which we'll use as a pseudo file lock. if ( file_exists( $lock_file ) ) { $wp_filesystem->put_contents( $lock_file, '' ); // Create the static resource file. $asset_created = $wp_filesystem->put_contents( $file_path, $file_contents, FS_CHMOD_FILE ); if ( ! $asset_created ) { // There's no point in continuing. return; } else { // Remove the temporary file. unlink( $lock_file ); } } } /** * Inject fallback assets when needed. * We don't know what content might appear on blog module pagination. * Fallback .css is injected on these pages. * * @return void * @since 4.10.0 */ public function maybe_inject_fallback_dynamic_assets() { if ( ! $this->is_cachable_request() ) { return; } preg_match_all( '/show_content="\w+"/', $this->_all_content, $show_content_values ); $show_content = et_check_if_particular_value_is_on( $show_content_values[0] ) || $this->_late_show_content; if ( in_array( 'et_pb_blog', $this->_all_shortcodes, true ) && $show_content ) { $assets_path = et_get_dynamic_assets_path( true ); $fallback_file = "{$assets_path}/css/_fallback{$this->_cpt_suffix}{$this->_rtl_suffix}.css"; // Inject the fallback assets into `
`. ?> is_cachable_request() ) { return; } $late_assets = array(); $late_files = (array) glob( "{$this->_cache_dir_path}/{$this->_folder_name}/et-{$this->_owner}-dynamic*late*" ); $style_handle = $this->get_style_css_handle(); $inline_style_suffix = et_core_is_inline_stylesheet_enabled() ? '-inline' : ''; if ( ! empty( $late_files ) ) { foreach ( (array) $late_files as $file ) { $file_path = et_()->normalize_path( $file ); $late_asset_url = esc_url_raw( et_()->path( $this->_cache_dir_url, $this->_folder_name, basename( $file_path ) ) ); $late_asset_size = filesize( $file_path ); if ( $late_asset_size ) { array_push( $late_assets, $late_asset_url ); } } } // Don't inject empty files. if ( ! $late_assets ) { return; } // Inject the late assets into ``. ?> is_cachable_request() ) { return; } $split_global_data = []; if ( $this->_need_late_generation ) { $this->_processed_shortcodes = $this->_missed_shortcodes; $global_assets_list = $this->get_new_array_values( $this->get_late_global_assets_list(), $this->_early_global_asset_list ); } else { $this->_presets_attributes = $this->get_preset_attributes( $this->_all_content ); $this->_processed_shortcodes = $this->_early_shortcodes; $global_assets_list = $this->get_global_assets_list(); /** * Filters the Above The Fold shortcodes. * * @since 4.10.0 * * @param array $shortcodes Above The Fold shortcodes. * @param string $content Theme Builder / Post Content. */ $atf_shortcodes = apply_filters( 'et_dynamic_assets_modules_atf', [], $this->_all_content ); /** * Filters whether Content can be split in Above The Fold / Below The Fold. * * @since 4.10.0 * * @param bool|object $content Builder Post Types. */ $content = apply_filters( 'et_dynamic_assets_content', false ); if ( false !== $content ) { $split_global_data = $this->split_global_assets_data( $content ); } } $shortcode_assets_list = $this->get_shortcode_assets_list(); if ( empty( $split_global_data ) ) { $assets_data = $this->get_assets_data( array_merge( $global_assets_list, $shortcode_assets_list ) ); $this->generate_dynamic_assets_files( $assets_data ); } else { $btf_shortcode_assets_list = $shortcode_assets_list; $atf_shortcode_assets_list = []; foreach ( $atf_shortcodes as $shortcode ) { if ( isset( $shortcode_assets_list[ $shortcode ] ) ) { $atf_shortcode_assets_list[ $shortcode ] = $shortcode_assets_list[ $shortcode ]; unset( $btf_shortcode_assets_list[ $shortcode ] ); } } $atf_assets_data = $this->get_assets_data( array_merge( $split_global_data->atf, $atf_shortcode_assets_list ) ); // Gotta reset this or else `get_assets_data` not going to return the correct set... $this->_processed_files = []; $btf_assets_data = $this->get_assets_data( array_merge( $split_global_data->btf, $btf_shortcode_assets_list ) ); $this->generate_dynamic_assets_files( $atf_assets_data, 'critical' ); $this->generate_dynamic_assets_files( $btf_assets_data ); } } /** * Generate late assets if needed. * * @since 4.10.0 */ public function process_late_detection_and_output() { // Late detection. $this->get_late_shortcodes(); $this->get_late_attributes(); // Late assets determination. if ( $this->_need_late_generation ) { $this->generate_dynamic_assets(); /** * Fires after late detected assets are generated. * * @since 4.10.0 */ do_action( 'et_dynamic_late_assets_generated' ); } } /** * Get shortcode assets data. * * @param array $asset_list Assets list. * * @return array * @since 4.10.0 */ public function get_assets_data( $asset_list = array() ) { global $wp_filesystem; $assets_data = array(); $newly_processed_files = array(); $files_with_url = array( 'signup', 'icons_base', 'icons_base_social', 'icons_all', 'icons_fa_all' ); foreach ( $asset_list as $asset => $asset_data ) { foreach ( $asset_data as $file_type => $files ) { $files = (array) $files; foreach ( $files as $file ) { // Make sure same file's content is not loaded more than once. if ( in_array( $file, $this->_processed_files, true ) ) { continue; } array_push( $newly_processed_files, $file ); $file_content = $wp_filesystem->get_contents( $file ); if ( in_array( basename( $file, '.css' ), $files_with_url, true ) ) { $file_content = preg_replace( '/#dynamic-product-dir/i', $this->_product_dir, $file_content ); } $file_content = trim( $file_content ); if ( empty( $file_content ) ) { continue; } $assets_data[ $file_type ]['assets'][] = $asset; $assets_data[ $file_type ]['content'][] = $file_content; if ( $this->is_rtl ) { $file_rtl = str_replace( ".${file_type}", "-rtl.{$file_type}", $file ); if ( file_exists( $file_rtl ) ) { $file_content_rtl = $wp_filesystem->get_contents( $file_rtl ); $assets_data[ $file_type ]['assets'][] = "{$asset}-rtl"; $assets_data[ $file_type ]['content'][] = $file_content_rtl; } } } } } $this->_processed_files = $this->get_unique_array_values( $this->_processed_files, $newly_processed_files ); return $assets_data; } /** * Gets a list of global asset files. * * @return array * @since 4.10.0 */ public function get_global_assets_list() { if ( ! $this->is_cachable_request() ) { return array(); } $assets_prefix = et_get_dynamic_assets_path(); $dynamic_icons = et_use_dynamic_icons(); $social_icons_deps = array( 'et_pb_social_media_follow', 'et_pb_team_member', ); if ( ! $this->_use_divi_icons || ! $this->_use_fa_icons ) { if ( empty( $this->_presets_attributes ) ) { $this->_presets_attributes = $this->get_preset_attributes( $this->_all_content ); } // Check for icons existence in presets. $icon_fields_in_presets = array_intersect( et_pb_get_font_icon_field_names(), array_keys( $this->_presets_attributes ) ); $maybe_presets_contain_divi_icon = false; $maybe_presets_contain_fa_icon = false; if ( ! empty( $icon_fields_in_presets ) ) { foreach ( $icon_fields_in_presets as $icon_field_in_presets ) { if ( ! $this->_use_divi_icons && et_pb_maybe_divi_font_icon( $this->_presets_attributes[ $icon_field_in_presets ] ) ) { $maybe_presets_contain_divi_icon = true; if ( $this->_use_fa_icons || $maybe_presets_contain_fa_icon ) { break; } else { continue; } } if ( ! $this->_use_fa_icons && et_pb_maybe_fa_font_icon( $this->_presets_attributes[ $icon_field_in_presets ] ) ) { $maybe_presets_contain_fa_icon = true; if ( $this->_use_divi_icons || $maybe_presets_contain_divi_icon ) { break; } } } } $maybe_post_contains_divi_icon = $this->_use_divi_icons || $maybe_presets_contain_divi_icon || ( $this->check_for_dependency( et_pb_get_font_icon_modules(), $this->_processed_shortcodes ) && et_pb_check_if_post_contains_divi_font_icon( $this->_all_content ) ); // Load the icon font needed based on the icons being used. $this->_use_divi_icons = $this->_use_divi_icons || ( 'on' !== $dynamic_icons || $maybe_post_contains_divi_icon || $this->check_if_class_exits( 'et-pb-icon', $this->_all_content ) ); $this->_use_fa_icons = $this->_use_fa_icons || $maybe_presets_contain_fa_icon || ( $this->check_for_dependency( et_pb_get_font_icon_modules(), $this->_processed_shortcodes ) && et_pb_check_if_post_contains_fa_font_icon( $this->_all_content ) ); } if ( ! $this->_use_social_icons ) { $this->_use_social_icons = $this->check_for_dependency( $social_icons_deps, $this->_processed_shortcodes ); if ( $this->_use_social_icons && ! $this->_use_fa_icons ) { $this->_use_fa_icons = et_pb_check_if_post_contains_network_with_fa_icon( $this->_all_content ); } } if ( $this->_use_divi_icons ) { $this->_early_global_asset_list['et_icons_all'] = array( 'css' => "{$assets_prefix}/css/icons_all.css", ); } elseif ( $this->_use_social_icons ) { $this->_early_global_asset_list['et_icons_social'] = array( 'css' => "{$assets_prefix}/css/icons_base_social.css", ); } else { $this->_early_global_asset_list['et_icons_base'] = array( 'css' => "{$assets_prefix}/css/icons_base.css", ); } if ( $this->_use_fa_icons ) { $this->_early_global_asset_list['et_icons_fa'] = array( 'css' => "{$assets_prefix}/css/icons_fa_all.css", ); } // Only include the following assets on post feeds and posts that aren't using the builder. if ( ( is_single() && ! $this->_page_builder_used ) || ( is_home() && ! is_front_page() ) || ! is_singular() ) { $this->_early_global_asset_list['et_post_formats'] = array( 'css' => array( "{$assets_prefix}/css/post_formats{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_base{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", "{$assets_prefix}/css/audio_player{$this->_cpt_suffix}.css", "{$assets_prefix}/css/video_player{$this->_cpt_suffix}.css", "{$assets_prefix}/css/wp_gallery{$this->_cpt_suffix}.css", ), ); } // Load posts styles on posts and post feeds. if ( ! is_page() ) { $this->_early_global_asset_list['et_posts'] = array( 'css' => "{$assets_prefix}/css/posts{$this->_cpt_suffix}.css", ); } if ( $this->is_rtl ) { $this->_early_global_asset_list['et_divi_shared_conditional_rtl'] = array( 'css' => "{$assets_prefix}/css/shared-conditional-style{$this->_cpt_suffix}-rtl.css", ); } // Check for specialty sections. preg_match_all( '/specialty="\w+"/', $this->_all_content, $specialty_values ); $specialty_used = et_check_if_particular_value_is_on( $specialty_values[0] ); // Check for custom gutter widths. preg_match_all( '/gutter_width="\w+"/', $this->_all_content, $matches ); $page_custom_gutter = is_singular() ? [ intval( get_post_meta( $this->_post_id, '_et_pb_gutter_width', true ) ) ] : []; // Add custom gutters in TB templates. if ( ! empty( $this->_tb_template_ids ) ) { foreach ( $this->_tb_template_ids as $template_id ) { $page_custom_gutter[] = intval( get_post_meta( $template_id, '_et_pb_gutter_width', true ) ); } } $customizer_gutter = intval( et_get_option( 'gutter_width', '3' ) ); $this->_default_gutters = array_merge( (array) $page_custom_gutter, (array) $customizer_gutter ); $no_of_gutters = substr_count( $this->_all_content, 'use_custom_gutter' ); $preset_gutter_val = ! empty( $this->_presets_attributes['use_custom_gutter'] ) && 'on' === $this->_presets_attributes['use_custom_gutter'] ? (array) $this->_presets_attributes['gutter_width'] : array(); if ( $no_of_gutters > count( $matches[0] ) && ! in_array( 'gutter_width="3"', $matches[0], true ) ) { array_push( $matches[0], 'gutter_width="3"' ); } // Here we are combining the custom gutters in the page with Default gutters and then keeping only the unique gutters. $gutter_widths = $this->get_unique_array_values( et_get_content_gutter_widths( $matches[0] ), $this->_default_gutters, $preset_gutter_val ); $gutter_length = count( $gutter_widths ); $grid_items_deps = array( 'et_pb_filterable_portfolio', 'et_pb_fullwidth_portfolio', 'et_pb_portfolio', 'et_pb_gallery', 'et_pb_wc_gallery', 'et_pb_blog', 'et_pb_sidebar', 'et_pb_shop', ); $grid_items_used = $this->check_for_dependency( $grid_items_deps, $this->_processed_shortcodes ); if ( ! empty( $gutter_widths ) ) { $this->_early_global_asset_list = array_merge( $this->_early_global_asset_list, $this->get_gutters_asset_list( $gutter_length, $gutter_widths, $specialty_used, $grid_items_used ) ); } // Load WooCommerce css when WooCommerce is active. if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) { $this->_early_global_asset_list['et_divi_woocommerce_modules'] = array( 'css' => array( "{$assets_prefix}/css/woocommerce{$this->_cpt_suffix}.css", "{$assets_prefix}/css/woocommerce_shared{$this->_cpt_suffix}.css", ), ); } // Load PageNavi css when PageNavi is active. if ( is_plugin_active( 'wp-pagenavi/wp-pagenavi.php' ) ) { $this->_early_global_asset_list['et_divi_wp_pagenavi'] = array( 'css' => "{$assets_prefix}/css/wp-page_navi{$this->_cpt_suffix}.css", ); } $show_in_lightbox = $this->check_if_attribute_exits( 'show_in_lightbox', $this->_all_content ); if ( $show_in_lightbox ) { $this->_early_global_asset_list['et_jquery_magnific_popup'] = array( 'css' => "{$assets_prefix}/css/magnific_popup.css", ); } $has_animation_style = $this->check_if_attribute_exits( 'animation_style', $this->_all_content ); // Load animation assets if any module uses animations. if ( $has_animation_style || in_array( 'et_pb_circle_counter', $this->_processed_shortcodes, true ) ) { $this->_early_global_asset_list['animations'] = array( 'css' => "{$assets_prefix}/css/animations{$this->_cpt_suffix}.css", ); } $sticky_used = $this->check_if_attribute_exits( 'sticky_position', $this->_all_content ); if ( $sticky_used ) { $this->_early_global_asset_list['sticky'] = array( 'css' => "{$assets_prefix}/css/sticky_elements{$this->_cpt_suffix}.css", ); } // Collect and pass all needed assets arguments. $assets_args = array( 'assets_prefix' => $assets_prefix, 'dynamic_icons' => $dynamic_icons, 'cpt_suffix' => $this->_cpt_suffix, 'use_all_icons' => $this->_use_divi_icons, 'show_in_lightbox' => $show_in_lightbox, 'has_animation_style' => $has_animation_style, 'sticky_used' => $sticky_used, // Gutter/grid items processed info. 'gutter_widths' => $gutter_widths, 'gutter_length' => $gutter_length, 'specialty_used' => $specialty_used, 'grid_items_used' => $grid_items_used, ); /** * Use this filter to add additional assets to the global asset list. * * @param array $this->_early_global_asset_list Current global assets on the list. * @param array $assets_args Additional assets arguments. * @param ET_Dynamic_Assets $this Instance of ET_Dynamic_Assets class. * * @since 4.10.0 * @since 4.11.0 Pass new parameters $assets_args and $this. 3rd-party plugins can * use it to call some private functions (i.e. gutter assets). */ $this->_early_global_asset_list = apply_filters( 'et_global_assets_list', $this->_early_global_asset_list, $assets_args, $this ); return $this->_early_global_asset_list; } /** * Gets a list of global asset files during late detection. * * @return array * @since 4.10.0 */ public function get_late_global_assets_list() { if ( ! $this->is_cachable_request() ) { return array(); } $assets_prefix = et_get_dynamic_assets_path(); $grid_items_used = ''; if ( $this->_late_custom_icon ) { $this->_late_global_asset_list['et_icons_all'] = array( 'css' => "{$assets_prefix}/css/icons_all.css", ); } elseif ( $this->_late_social_icon ) { $this->_late_global_asset_list['et_icons_social'] = array( 'css' => "{$assets_prefix}/css/icons_base_social.css", ); } if ( $this->_late_fa_icon ) { $this->_late_global_asset_list['et_icons_fa'] = array( 'css' => "{$assets_prefix}/css/icons_fa_all.css", ); } $gutter_length = count( $this->_late_custom_gutters ); $gutter_widths = $this->get_unique_array_values( $this->_late_gutter_width, $this->_default_gutters ); $grid_items_deps = array( 'et_pb_filterable_portfolio', 'et_pb_fullwidth_portfolio', 'et_pb_portfolio', 'et_pb_gallery', 'et_pb_wc_gallery', 'et_pb_blog', 'et_pb_sidebar', 'et_pb_shop', ); $grid_items_used = $this->check_for_dependency( $grid_items_deps, $this->_processed_shortcodes ); if ( ! empty( $gutter_widths ) ) { $this->_late_global_asset_list = array_merge( $this->_late_global_asset_list, $this->get_gutters_asset_list( $gutter_length, $gutter_widths, $this->_late_use_specialty, $grid_items_used ) ); } if ( $this->_late_show_in_lightbox ) { $this->_late_global_asset_list['et_jquery_magnific_popup'] = array( 'css' => "{$assets_prefix}/css/magnific_popup.css", ); } if ( $this->_late_animation_style ) { $this->_late_global_asset_list['animations'] = array( 'css' => "{$assets_prefix}/css/animations{$this->_cpt_suffix}.css", ); } if ( $this->_late_use_sticky ) { $this->_late_global_asset_list['sticky'] = array( 'css' => "{$assets_prefix}/css/sticky_elements{$this->_cpt_suffix}.css", ); } // Collect and pass all needed assets arguments. $assets_args = array( 'assets_prefix' => $assets_prefix, 'dynamic_icons' => et_use_dynamic_icons(), 'cpt_suffix' => $this->_cpt_suffix, 'use_all_icons' => $this->_late_custom_icon, 'show_in_lightbox' => $this->_late_show_in_lightbox, 'has_animation_style' => $this->_late_animation_style, 'sticky_used' => $this->_late_use_sticky, // Gutter/grid items processed info. 'gutter_widths' => $gutter_widths, 'gutter_length' => $gutter_length, 'specialty_used' => $this->_late_use_specialty, 'grid_items_used' => $grid_items_used, ); /** * Use this filter to add additional assets to the late global asset list. * * @param array $this->_late_global_asset_list Current late global assets on the list. * @param array $assets_args Additional assets arguments. * @param ET_Dynamic_Assets $this Instance of ET_Dynamic_Assets class. * * @since 4.10.0 * @since 4.11.0 Pass new parameters $assets_args and $this. 3rd-party plugins can * use it to call some private functions (i.e. gutter assets). */ $this->_late_global_asset_list = apply_filters( 'et_late_global_assets_list', $this->_late_global_asset_list, $assets_args, $this ); return $this->_late_global_asset_list; } /** * Generate gutters CSS file list. * * @param integer $gutter_length number of gutter widths used. * @param array $gutter_widths array of gutter widths used. * @param bool $specialty are specialty sections used. * @param bool $grid_items are grid modules used. * @return array $assets_list of gutter assets * @since 4.10.0 */ public function get_gutters_asset_list( $gutter_length, $gutter_widths, $specialty = false, $grid_items = false ) { $temp_widths = $gutter_widths; $gutter_length = count( $gutter_widths ); $specialty_suffix = $specialty ? '_specialty' : ''; $assets_prefix = et_get_dynamic_assets_path(); $assets_list = array(); // Put default gutter `3` at beginning, otherwise it would mess up the layout. if ( in_array( 3, $temp_widths, true ) ) { $gutter_widths = array_diff( $temp_widths, [ 3 ] ); array_unshift( $gutter_widths, 3 ); } // Replace legacy gutter width values of 0 with 1. $gutter_widths = str_replace( 0, 1, $gutter_widths ); for ( $i = 0; $i < $gutter_length; $i++ ) { $assets_list[ 'et_divi_gutters' . $gutter_widths[ $i ] ] = array( 'css' => "{$assets_prefix}/css/gutters" . $gutter_widths[ $i ] . "{$this->_cpt_suffix}.css", ); $assets_list[ 'et_divi_gutters' . $gutter_widths[ $i ] . "{$specialty_suffix}" ] = array( 'css' => "{$assets_prefix}/css/gutters" . $gutter_widths[ $i ] . "{$specialty_suffix}{$this->_cpt_suffix}.css", ); if ( $grid_items ) { $assets_list[ 'et_divi_gutters' . $gutter_widths[ $i ] . '_grid_items' ] = array( 'css' => "{$assets_prefix}/css/gutters" . $gutter_widths[ $i ] . "_grid_items{$this->_cpt_suffix}.css", ); $assets_list[ 'et_divi_gutters' . $gutter_widths[ $i ] . "{$specialty_suffix}_grid_items" ] = array( 'css' => "{$assets_prefix}/css/gutters" . $gutter_widths[ $i ] . "{$specialty_suffix}_grid_items{$this->_cpt_suffix}.css", ); } } return $assets_list; } /** * Gets a list of asset files and can be useful for getting all Divi module shortcodes. * * @param bool $used_shortcodes if shortcodes are used. * * @return array * @since 4.10.0 */ public function get_shortcode_assets_list( $used_shortcodes = true ) { $assets_prefix = et_get_dynamic_assets_path(); $specialty_suffix = ''; $shortcode_list = array(); preg_match_all( '/specialty="\w+"/', $this->_all_content, $specialty_values ); $specialty_used = et_check_if_particular_value_is_on( $specialty_values[0] ) || $this->_late_use_specialty; if ( $specialty_used ) { $specialty_suffix = '_specialty'; } $assets_list = array( // Structure elements. 'et_pb_section' => array( 'css' => array( "{$assets_prefix}/css/section{$this->_cpt_suffix}.css", "{$assets_prefix}/css/row{$this->_cpt_suffix}.css", // Some fullwidth section modules use the et_pb_row class. ), ), 'et_pb_row' => array( 'css' => "{$assets_prefix}/css/row{$this->_cpt_suffix}.css", ), 'et_pb_column' => array(), 'et_pb_row_inner' => array( 'css' => "{$assets_prefix}/css/row{$this->_cpt_suffix}.css", ), // Module elements. 'et_pb_accordion' => array( 'css' => array( "{$assets_prefix}/css/accordion{$this->_cpt_suffix}.css", "{$assets_prefix}/css/toggle{$this->_cpt_suffix}.css", ), ), 'et_pb_audio' => array( 'css' => array( "{$assets_prefix}/css/audio{$this->_cpt_suffix}.css", "{$assets_prefix}/css/audio_player{$this->_cpt_suffix}.css", ), ), 'et_pb_counter' => array( 'css' => "{$assets_prefix}/css/counter{$this->_cpt_suffix}.css", ), 'et_pb_blog' => array( 'css' => array( "{$assets_prefix}/css/blog{$this->_cpt_suffix}.css", "{$assets_prefix}/css/posts{$this->_cpt_suffix}.css", "{$assets_prefix}/css/post_formats{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", "{$assets_prefix}/css/audio_player{$this->_cpt_suffix}.css", "{$assets_prefix}/css/video_player{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_base{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", "{$assets_prefix}/css/wp_gallery{$this->_cpt_suffix}.css", ), ), 'et_pb_blurb' => array( 'css' => array( "{$assets_prefix}/css/blurb{$this->_cpt_suffix}.css", "{$assets_prefix}/css/legacy_animations{$this->_cpt_suffix}.css", ), ), 'et_pb_button' => array( 'css' => array( "{$assets_prefix}/css/button{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_circle_counter' => array( 'css' => "{$assets_prefix}/css/circle_counter{$this->_cpt_suffix}.css", ), 'et_pb_code' => array( 'css' => "{$assets_prefix}/css/code{$this->_cpt_suffix}.css", ), 'et_pb_comments' => array( 'css' => array( "{$assets_prefix}/css/comments{$this->_cpt_suffix}.css", "{$assets_prefix}/css/comments_shared{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_contact_form' => array( 'css' => array( "{$assets_prefix}/css/contact_form{$this->_cpt_suffix}.css", "{$assets_prefix}/css/forms{$this->_cpt_suffix}.css", "{$assets_prefix}/css/forms{$specialty_suffix}{$this->_cpt_suffix}.css", "{$assets_prefix}/css/fields{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_countdown_timer' => array( 'css' => "{$assets_prefix}/css/countdown_timer{$this->_cpt_suffix}.css", ), 'et_pb_cta' => array( 'css' => "{$assets_prefix}/css/cta{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), 'et_pb_divider' => array( 'css' => "{$assets_prefix}/css/divider{$this->_cpt_suffix}.css", ), 'et_pb_filterable_portfolio' => array( 'css' => array( "{$assets_prefix}/css/filterable_portfolio{$this->_cpt_suffix}.css", "{$assets_prefix}/css/portfolio{$this->_cpt_suffix}.css", "{$assets_prefix}/css/grid_items{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", ), ), 'et_pb_fullwidth_code' => array( 'css' => "{$assets_prefix}/css/fullwidth_code{$this->_cpt_suffix}.css", ), 'et_pb_fullwidth_header' => array( 'css' => "{$assets_prefix}/css/fullwidth_header{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), 'et_pb_fullwidth_image' => array( 'css' => array( "{$assets_prefix}/css/fullwidth_image{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", ), ), 'et_pb_fullwidth_map' => array( 'css' => array( "{$assets_prefix}/css/map{$this->_cpt_suffix}.css", "{$assets_prefix}/css/fullwidth_map{$this->_cpt_suffix}.css", ), ), 'et_pb_fullwidth_menu' => array( 'css' => array( "{$assets_prefix}/css/menus{$this->_cpt_suffix}.css", "{$assets_prefix}/css/fullwidth_menu{$this->_cpt_suffix}.css", "{$assets_prefix}/css/header_animations.css", "{$assets_prefix}/css/header_shared{$this->_cpt_suffix}.css", ), ), 'et_pb_fullwidth_portfolio' => array( 'css' => array( "{$assets_prefix}/css/fullwidth_portfolio{$this->_cpt_suffix}.css", "{$assets_prefix}/css/grid_items{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", ), ), 'et_pb_fullwidth_post_slider' => array( 'css' => array( "{$assets_prefix}/css/post_slider{$this->_cpt_suffix}.css", "{$assets_prefix}/css/fullwidth_post_slider{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_modules{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_base{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", "{$assets_prefix}/css/posts{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_fullwidth_post_title' => array( 'css' => array( "{$assets_prefix}/css/post_title{$this->_cpt_suffix}.css", "{$assets_prefix}/css/fullwidth_post_title{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_fullwidth_slider' => array( 'css' => array( "{$assets_prefix}/css/fullwidth_slider{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_modules{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_base{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_gallery' => array( 'css' => array( "{$assets_prefix}/css/gallery{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", "{$assets_prefix}/css/grid_items{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_base{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", "{$assets_prefix}/css/magnific_popup.css", ), ), 'gallery' => array( 'css' => array( "{$assets_prefix}/css/wp_gallery{$this->_cpt_suffix}.css", "{$assets_prefix}/css/magnific_popup.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", ), ), 'et_pb_image' => array( 'css' => array( "{$assets_prefix}/css/image{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", ), ), 'et_pb_login' => array( 'css' => array( "{$assets_prefix}/css/login{$this->_cpt_suffix}.css", "{$assets_prefix}/css/forms{$this->_cpt_suffix}.css", "{$assets_prefix}/css/forms{$specialty_suffix}{$this->_cpt_suffix}.css", "{$assets_prefix}/css/fields{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_map' => array( 'css' => "{$assets_prefix}/css/map{$this->_cpt_suffix}.css", ), 'et_pb_menu' => array( 'css' => array( "{$assets_prefix}/css/menus{$this->_cpt_suffix}.css", "{$assets_prefix}/css/menu{$this->_cpt_suffix}.css", "{$assets_prefix}/css/header_animations.css", "{$assets_prefix}/css/header_shared{$this->_cpt_suffix}.css", ), ), 'et_pb_number_counter' => array( 'css' => "{$assets_prefix}/css/number_counter{$this->_cpt_suffix}.css", ), 'et_pb_portfolio' => array( 'css' => array( "{$assets_prefix}/css/portfolio{$this->_cpt_suffix}.css", "{$assets_prefix}/css/grid_items{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", ), ), 'et_pb_post_slider' => array( 'css' => array( "{$assets_prefix}/css/post_slider{$this->_cpt_suffix}.css", "{$assets_prefix}/css/posts{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_modules{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_base{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_post_nav' => array( 'css' => "{$assets_prefix}/css/post_nav{$this->_cpt_suffix}.css", ), 'et_pb_post_title' => array( 'css' => array( "{$assets_prefix}/css/post_title{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_pricing_tables' => array( 'css' => array( "{$assets_prefix}/css/pricing_tables{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_search' => array( 'css' => "{$assets_prefix}/css/search{$this->_cpt_suffix}.css", ), 'et_pb_shop' => array( 'css' => array( "{$assets_prefix}/css/shop{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", ), ), 'et_pb_sidebar' => array( 'css' => array( "{$assets_prefix}/css/sidebar{$this->_cpt_suffix}.css", "{$assets_prefix}/css/widgets_shared{$this->_cpt_suffix}.css", ), ), 'et_pb_signup' => array( 'css' => array( "{$assets_prefix}/css/signup{$this->_cpt_suffix}.css", "{$assets_prefix}/css/forms{$this->_cpt_suffix}.css", "{$assets_prefix}/css/forms{$specialty_suffix}{$this->_cpt_suffix}.css", "{$assets_prefix}/css/fields{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_slider' => array( 'css' => array( "{$assets_prefix}/css/slider{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_modules{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_base{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_social_media_follow' => array( 'css' => "{$assets_prefix}/css/social_media_follow{$this->_cpt_suffix}.css", ), 'et_pb_tabs' => array( 'css' => "{$assets_prefix}/css/tabs{$this->_cpt_suffix}.css", ), 'et_pb_team_member' => array( 'css' => array( "{$assets_prefix}/css/team_member{$this->_cpt_suffix}.css", "{$assets_prefix}/css/legacy_animations{$this->_cpt_suffix}.css", ), ), 'et_pb_testimonial' => array( 'css' => "{$assets_prefix}/css/testimonial{$this->_cpt_suffix}.css", ), 'et_pb_text' => array( 'css' => "{$assets_prefix}/css/text{$this->_cpt_suffix}.css", ), 'et_pb_toggle' => array( 'css' => "{$assets_prefix}/css/toggle{$this->_cpt_suffix}.css", ), 'et_pb_video' => array( 'css' => array( "{$assets_prefix}/css/video{$this->_cpt_suffix}.css", "{$assets_prefix}/css/video_player{$this->_cpt_suffix}.css", ), ), 'et_pb_video_slider' => array( 'css' => array( "{$assets_prefix}/css/video_slider{$this->_cpt_suffix}.css", "{$assets_prefix}/css/video_player{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_base{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_additional_info' => array( 'css' => array( "{$assets_prefix}/css/woo_additional_info{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_add_to_cart' => array( 'css' => array( "{$assets_prefix}/css/woo_add_to_cart{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_breadcrumb' => array( 'css' => array( "{$assets_prefix}/css/woo_breadcrumb{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_cart_notice' => array( 'css' => array( "{$assets_prefix}/css/woo_cart_notice{$this->_cpt_suffix}.css", "{$assets_prefix}/css/buttons{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_description' => array( 'css' => array( "{$assets_prefix}/css/woo_description{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_gallery' => array( 'css' => array( "{$assets_prefix}/css/gallery{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", "{$assets_prefix}/css/grid_items{$this->_cpt_suffix}.css", "{$assets_prefix}/css/magnific_popup.css", "{$assets_prefix}/css/slider_base{$this->_cpt_suffix}.css", "{$assets_prefix}/css/slider_controls{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_images' => array( 'css' => array( "{$assets_prefix}/css/image{$this->_cpt_suffix}.css", "{$assets_prefix}/css/overlay{$this->_cpt_suffix}.css", "{$assets_prefix}/css/woo_images{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_meta' => array( 'css' => array( "{$assets_prefix}/css/woo_meta{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_price' => array( 'css' => array( "{$assets_prefix}/css/woo_price{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_rating' => array( 'css' => array( "{$assets_prefix}/css/woo_rating{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_related_products' => array( 'css' => array( "{$assets_prefix}/css/woo_related_products_upsells{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_upsells' => array( 'css' => array( "{$assets_prefix}/css/woo_related_products_upsells{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_reviews' => array( 'css' => array( "{$assets_prefix}/css/woo_reviews{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_stock' => array( 'css' => array( "{$assets_prefix}/css/woo_stock{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_tabs' => array( 'css' => array( "{$assets_prefix}/css/tabs{$this->_cpt_suffix}.css", "{$assets_prefix}/css/woo_tabs{$this->_cpt_suffix}.css", ), ), 'et_pb_wc_title' => array( 'css' => array( "{$assets_prefix}/css/woo_title{$this->_cpt_suffix}.css", ), ), 'et_pb_icon' => array( 'css' => "{$assets_prefix}/css/icon{$this->_cpt_suffix}.css", ), ); /** * This filter can be used to force loading of a certain Divi module in case their custom one relies on its styles. * * @since 4.10.0 */ $required_assets = apply_filters( 'et_required_module_assets', array() ); if ( $used_shortcodes ) { foreach ( $assets_list as $asset => $asset_data ) { if ( ! in_array( $asset, $this->_processed_shortcodes, true ) && ! in_array( $asset, $required_assets, true ) ) { unset( $assets_list[ $asset ] ); } } } return $assets_list; } /** * Adds global modules' content (if any) on top of post content so that * that all shortcodes can be properly registered. * * @param string $content The post content. * * @return string * @since 4.10.0 */ public function maybe_add_global_modules_content( $content ) { preg_match_all( '@global_module="(\d+)"@', $content, $matches ); $global_modules = $this->get_unique_array_values( $matches[1], $this->_global_modules ); // If there are no global modules `$matches[1]` would be an empty array. // When a Global module is added, the shortcode is also added in post content. But afterwards if the Global // module is changed, the respective shortcode in post content doesn't change accordingly. // Here We are detecting the changes using the `global_module` attribute. We are appending the *actual* // Global module content at the end, and we need to put the Global module content at beginning, // otherwise the Dynamic Asset mechanism won't be able to detect the changes. if ( ! empty( $global_modules ) ) { foreach ( $global_modules as $global_post_id ) { $global_module = get_post( $global_post_id ); $content = $global_module->post_content . $content; } } return $content; } /** * Early shortcode detection. * Retrieves list of shortcodes from the post and theme builder content. * * @param string $content The post content. * * @return array * * @see strip_shortcodes() for used regex * * @since 4.10.0 */ public function get_early_shortcodes( $content ) { $shortcodes = array_keys( $this->get_shortcode_assets_list( false ) ); $processed_content = $this->maybe_add_global_modules_content( $content ); preg_match_all( '@\[([^<>&/\[\]\x00-\x20=]++)@', $processed_content, $matches ); return array_intersect( $shortcodes, array_unique( $matches[1] ) ); } /** * Get the post IDs of active Theme Builder templates. * * @return array * @since 4.10.0 */ public function get_theme_builder_template_ids() { $tb_layouts = et_theme_builder_get_template_layouts(); $template_ids = array(); // Extract layout ids used in current request. if ( ! empty( $tb_layouts ) ) { if ( $tb_layouts[ ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE ]['override'] ) { if ( ! empty( $tb_layouts[ ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE ]['enabled'] ) ) { $template_ids[] = intval( $tb_layouts[ ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE ]['id'] ); } } if ( $tb_layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ]['override'] ) { if ( ! empty( $tb_layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ]['enabled'] ) ) { $template_ids[] = intval( $tb_layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ]['id'] ); } } if ( $tb_layouts[ ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE ]['override'] ) { if ( ! empty( $tb_layouts[ ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE ]['enabled'] ) ) { $template_ids[] = intval( $tb_layouts[ ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE ]['id'] ); } } } return $template_ids; } /** * Merge multiple arrays and returns an array with unique values. * * @since 4.10.0 */ public function get_unique_array_values() { $merged_array = array(); foreach ( func_get_args() as $array_of_value ) { if ( empty( $array_of_value ) ) { continue; } $merged_array = array_merge( $merged_array, $array_of_value ); } return array_values( array_unique( $merged_array ) ); } /** * Find array values in array_1 that do not exist in array_2. * * @param array $array_1 First array. * @param array $array_2 Second array. * * @since 4.13.0 */ public function get_new_array_values( $array_1, $array_2 ) { $new_array_values = array(); foreach ( $array_1 as $key => $value ) { if ( empty( $array_2[ $key ] ) ) { $new_array_values[ $key ] = $value; } } return $new_array_values; } /** * Shortcode late detection. * Get shortcodes from the feature manager that might have been missed * during early detection. * * @param string $content Post content. * * @since 4.10.0 */ public function get_late_shortcodes( $content = '' ) { $module_use_detection = ET_Builder_Module_Use_Detection::instance(); $late_shortcodes = $module_use_detection->get_modules_used(); $this->_missed_shortcodes = array_diff( $late_shortcodes, $this->_early_shortcodes ); $this->_all_shortcodes = array_merge( $this->_missed_shortcodes, $this->_early_shortcodes ); if ( $this->_missed_shortcodes ) { $this->_need_late_generation = true; $this->metadata_set( '_et_dynamic_cached_shortcodes', $this->_all_shortcodes ); } } /** * Get module attributes used from the feature manager. * * @param array $detected_attributes Detected shortcode attributes. * * @since 4.10.0 */ public function get_late_attributes( $detected_attributes = array() ) { if ( ! $this->_early_attributes ) { $late_attributes = ET_Builder_Module_Use_Detection::instance()->get_module_attr_values_used(); if ( empty( $this->_presets_attributes ) ) { $this->_presets_attributes = $this->get_preset_attributes( $this->_all_content ); } $attributes = array_merge( $late_attributes, $this->_presets_attributes ); $this->metadata_set( '_et_dynamic_cached_attributes', $attributes ); $this->_need_late_generation = true; } if ( $this->_early_attributes ) { $attributes = $this->_early_attributes; $this->_need_late_generation = true; } if ( $attributes ) { foreach ( $attributes as $attribute => $value ) { if ( ! is_array( $value ) ) { $value = (array) $value; } // Determine whether we need to load the FA resources for the font icon options. if ( in_array( $attribute, et_pb_get_font_icon_field_names(), true ) ) { foreach ( $value as $font_icon_value ) { if ( et_pb_maybe_fa_font_icon( $font_icon_value ) ) { $this->_late_fa_icon = true; break; } } foreach ( $value as $font_icon_value ) { if ( et_pb_maybe_divi_font_icon( $font_icon_value ) ) { $this->_late_custom_icon = true; break; } } } switch ( $attribute ) { case 'gutter_width': $this->_late_gutter_width = ! empty( $value ) ? array_map( 'intval', $value ) : array(); break; case 'animation_style': $this->_late_animation_style = ! empty( $value ); break; case 'sticky_position': $this->_late_use_sticky = ! empty( $value ); break; case 'specialty': $this->_late_use_specialty = ! empty( $value ) && in_array( 'on', $value, true ); break; case 'use_custom_gutter': $this->_late_custom_gutters = ! empty( $value ) ? $value : array(); break; case 'show_in_lightbox': $this->_late_show_in_lightbox = ! empty( $value ) && in_array( 'on', $value, true ); break; case 'fullwidth': $this->_late_is_fullwidth = ! empty( $value ) && in_array( 'on', $value, true ); break; case 'show_content': $this->_late_show_content = ! empty( $value ) && in_array( 'on', $value, true ); break; case 'social_network': foreach ( $value as $social_network_value ) { if ( in_array( $social_network_value, et_pb_get_social_net_fa_icons(), true ) ) { $this->_late_fa_icon = true; break; } } foreach ( $value as $social_network_value ) { if ( in_array( $social_network_value, et_pb_get_social_net_divi_icons(), true ) ) { $this->_late_social_icon = true; break; } } break; case 'scroll_vertical_motion_enable': case 'scroll_horizontal_motion_enable': // Intentional fallthrough. case 'scroll_fade_enable': // Intentional fallthrough. case 'scroll_scaling_enable': // Intentional fallthrough. case 'scroll_rotating_enable': // Intentional fallthrough. case 'scroll_blur_enable': // Intentional fallthrough. $this->_late_use_motion_effect = ! empty( $value ); break; default: break; } } } } /** * Check if metadata exists. * * @param string $key Meta key to check against. * * @return boolean * @since 4.10.0 */ public function metadata_exists( $key ) { if ( is_singular() ) { return metadata_exists( 'post', $this->_post_id, $key ); } $folder_name = $this->get_folder_name(); $metadata_manager = ET_Builder_Dynamic_Assets_Feature::instance(); $metadata_cache = $metadata_manager->cache_get( $key, $folder_name ); return ! empty( $metadata_cache ); } /** * Get saved metadata. * * @param string $key Meta key to get data for. * * @return array * @since 4.10.0 */ public function metadata_get( $key ) { if ( is_singular() ) { return metadata_exists( 'post', $this->_post_id, $key ) ? get_post_meta( $this->_post_id, $key, true ) : ''; } $folder_name = $this->get_folder_name(); $metadata_manager = ET_Builder_Dynamic_Assets_Feature::instance(); return $metadata_manager->cache_get( $key, $folder_name ); } /** * Set metadata. * * @param string $key Meta key to set data for. * @param array $value The data to be set. * * @return void * @since 4.10.0 */ public function metadata_set( $key, $value ) { if ( is_singular() ) { update_post_meta( $this->_post_id, $key, $value ); return; } $folder_name = $this->get_folder_name(); $metadata_manager = ET_Builder_Dynamic_Assets_Feature::instance(); $metadata_manager->cache_set( $key, $value, $folder_name ); } /** * Checks if a list of dependencies exist in the content. * * @param array $needles Shortcodes to detect. * @param array $haystack All shortcodes. * * @since 4.10.0 */ public function check_for_dependency( $needles = array(), $haystack = array() ) { $detected = false; foreach ( $needles as $needle ) { if ( in_array( $needle, $haystack, true ) ) { $detected = true; } } return $detected; } /** * Enqueue early dynamic JavaScript files. * * @since 4.10.0 */ public function enqueue_dynamic_scripts_early() { $this->enqueue_dynamic_scripts(); } /** * Enqueue late dynamic JavaScript files. * * @since 4.10.0 */ public function enqueue_dynamic_scripts_late() { $this->enqueue_dynamic_scripts( 'late' ); } /** * Enqueue dynamic JavaScript files. * * @param string $request_type whether early or late request. * * @since 4.10.0 */ public function enqueue_dynamic_scripts( $request_type = 'early' ) { if ( ! et_builder_is_frontend_or_builder() ) { return; } $current_shortcodes = 'late' === $request_type ? $this->_missed_shortcodes : $this->_early_shortcodes; // Handle fitvids script. if ( ! $this->_enqueue_fitvids ) { $fitvids_deps = array( 'et_pb_blog', 'et_pb_slider', 'et_pb_video', 'et_pb_video_slider', 'et_pb_slide_video', 'et_pb_code', 'et_pb_fullwidth_code', 'et_pb_portfolio', 'et_pb_filterable_portfolio', ); $this->_enqueue_fitvids = $this->check_for_dependency( $fitvids_deps, $current_shortcodes ); if ( ( is_single() && ! $this->_page_builder_used ) || ( is_home() && ! is_front_page() ) || ! is_singular() ) { $this->_enqueue_fitvids = true; } if ( $this->_enqueue_fitvids || et_disable_js_on_demand() ) { wp_enqueue_script( 'fitvids', ET_BUILDER_URI . '/feature/dynamic-assets/assets/js/jquery.fitvids.js', array( 'jquery' ), ET_CORE_VERSION, true ); } } // Handle comments script. if ( ! $this->_enqueue_comments ) { $comments_deps = array( 'et_pb_comments', ); $this->_enqueue_comments = $this->check_for_dependency( $comments_deps, $current_shortcodes ); if ( $this->_enqueue_comments && comments_open() || et_disable_js_on_demand() ) { wp_enqueue_script( 'comment-reply' ); } } // Handle jQuery mobile script. if ( ! $this->_enqueue_jquery_mobile ) { $jquery_mobile_deps = array( 'et_pb_portfolio', 'et_pb_slider', 'et_pb_post_slider', 'et_pb_fullwidth_slider', 'et_pb_fullwidth_post_slider', 'et_pb_video_slider', 'et_slide', 'et_tabs', ); $this->_enqueue_jquery_mobile = $this->check_for_dependency( $jquery_mobile_deps, $current_shortcodes ); if ( $this->_enqueue_jquery_mobile || et_disable_js_on_demand() ) { wp_enqueue_script( 'jquery-mobile', ET_BUILDER_URI . '/feature/dynamic-assets/assets/js/jquery.mobile.js', array( 'jquery' ), ET_CORE_VERSION, true ); } } // Handle jQuery hashchange script. if ( ! $this->_enqueue_jquery_hashchange ) { $jquery_hashchange_deps = array( 'et_pb_gallery', 'et_pb_fullwidth_header', 'et_pb_filterable_portfolio', 'et_pb_tabs', ); $this->_enqueue_jquery_hashchange = $this->check_for_dependency( $jquery_hashchange_deps, $current_shortcodes ); if ( $this->_enqueue_jquery_hashchange || et_disable_js_on_demand() ) { wp_enqueue_script( 'hashchange', ET_BUILDER_URI . '/feature/dynamic-assets/assets/js/hashchange.js', array( 'jquery' ), ET_CORE_VERSION, true ); } } // Handle magnific popup script. if ( ! $this->_enqueue_magnific_popup ) { $magnific_popup_deps = array( 'et_pb_gallery', 'gallery', 'et_pb_wc_gallery', ); if ( $this->check_for_dependency( $magnific_popup_deps, $current_shortcodes ) || $this->check_if_attribute_exits( 'show_in_lightbox', $this->_all_content ) || $this->_late_show_in_lightbox ) { $this->_enqueue_magnific_popup = true; } if ( $this->_enqueue_magnific_popup || et_disable_js_on_demand() ) { wp_enqueue_script( 'magnific-popup', ET_BUILDER_URI . '/feature/dynamic-assets/assets/js/magnific-popup.js', array( 'jquery' ), ET_CORE_VERSION, true ); } } // Handle easy pie chart script. if ( ! $this->_enqueue_easypiechart ) { $easypiechart_deps = array( 'et_pb_blog', 'et_pb_circle_counter', 'et_pb_number_counter', ); $this->_enqueue_easypiechart = $this->check_for_dependency( $easypiechart_deps, $current_shortcodes ); if ( $this->_enqueue_easypiechart || et_disable_js_on_demand() ) { wp_enqueue_script( 'easypiechart', ET_BUILDER_URI . '/feature/dynamic-assets/assets/js/easypiechart.js', array( 'jquery' ), ET_CORE_VERSION, true ); } } // Handle salvattore script. if ( ! $this->_enqueue_salvattore ) { $salvattore_deps = array( 'et_pb_blog', 'et_pb_portfolio', 'et_pb_fullwidth_portfolio', 'et_pb_filterable_portfolio', 'et_pb_gallery', ); $this->_enqueue_salvattore = $this->check_for_dependency( $salvattore_deps, $current_shortcodes ); if ( $this->_enqueue_salvattore || et_disable_js_on_demand() ) { wp_enqueue_script( 'salvattore', ET_BUILDER_URI . '/feature/dynamic-assets/assets/js/salvattore.js', array(), ET_CORE_VERSION, true ); } } // Motion Effects and Sticky Options mused be enqueued in wp_footer so that we have all localized data, // so we need to process these during late detection only. if ( 'late' === $request_type ) { // Handle motion effects script. $motion_effects_deps = array( 'scroll_vertical_motion_enable', 'scroll_horizontal_motion_enable', 'scroll_fade_enable', 'scroll_scaling_enable', 'scroll_rotating_enable', 'scroll_blur_enable', ); foreach ( $motion_effects_deps as $motion_effects_dep ) { if ( $this->check_if_attribute_exits( $motion_effects_dep, $this->_all_content ) ) { $this->_enqueue_motion_effecs = true; break; } } if ( $this->_late_use_motion_effect ) { $this->_enqueue_motion_effecs = true; } if ( $this->_enqueue_motion_effecs || et_disable_js_on_demand() ) { wp_enqueue_script( 'et-builder-modules-script-motion', ET_BUILDER_URI . '/feature/dynamic-assets/assets/js/motion-effects.js', array( 'jquery' ), ET_CORE_VERSION, true ); wp_localize_script( 'et-builder-modules-script-motion', 'et_pb_motion_elements', ET_Builder_Element::$_scroll_effects_fields ); } // Handle sticky script. if ( $this->check_if_attribute_exits( 'sticky_position', $this->_all_content ) || $this->_late_use_sticky ) { $this->_enqueue_sticky = true; } if ( $this->_enqueue_sticky || et_disable_js_on_demand() ) { wp_enqueue_script( 'et-builder-modules-script-sticky', ET_BUILDER_URI . '/feature/dynamic-assets/assets/js/sticky-elements.js', array( 'jquery' ), ET_CORE_VERSION, true ); wp_localize_script( 'et-builder-modules-script-sticky', 'et_pb_sticky_elements', ET_Builder_Element::$sticky_elements ); } } } /** * Get attributes from presets used within the $content. * * @param string $content content to look for preset ids in. * * @since 4.10.0 */ public function get_preset_attributes( $content ) { $all_builder_presets = et_get_option( 'builder_global_presets', (object) array(), '', true ); $presets_attributes = array(); foreach ( $all_builder_presets as $module => $module_presets ) { $module_presets = is_array( $module_presets ) ? (object) $module_presets : $module_presets; if ( ! is_object( $module_presets ) ) { continue; } foreach ( $module_presets->presets as $key => $value ) { $presets_attributes = array_merge( $presets_attributes, (array) $value->settings ); } } return $presets_attributes; } /** * Check for available attributes. * * @param string $attribute Attribute to check. * @param string $content to search for attribute in. * * @since 4.10.0 */ public function check_if_attribute_exits( $attribute, $content ) { $preset_attributes = array(); if ( ! empty( $this->_presets_attributes ) ) { $preset_attributes = array_keys( $this->_presets_attributes ); } return preg_match( '/' . $attribute . '=".+"/', $content ) || in_array( $attribute, $preset_attributes, true ); } /** * Check class exists in post content. * * @param string $class class to check. * @param string $content to search for class in. * * @since 4.10.5 */ public function check_if_class_exits( $class, $content ) { return preg_match( '/class=".*' . preg_quote( $class, '/' ) . '/', $content ); } /** * Get custom global asset list. * * @param string $content post content. * * @since 4.10.0 * * @return array */ public function get_custom_global_assets_list( $content ) { // Save the current values of some properties. $all_content = $this->_all_content; $all_shortcodes = $this->_all_shortcodes; if ( '' === $content ) { $this->_all_shortcodes = []; } // Since `get_global_assets_list` has no parameters, the only way to run it on custom content // is to change `_all_content` and `_all_shortcodes`. The current values were previosly saved. // and will be restored right after the method call. $this->_all_content = $content; $list = $this->get_global_assets_list(); $this->_all_content = $all_content; $this->_all_shortcodes = $all_shortcodes; return $list; } /** * Get global assets data. * * @param string $content post content. * * @return array * * @since 4.10.0 */ public function split_global_assets_data( $content ) { /** * Filters whether Required Assets should be considered Above The Fold. * * @since 4.10.0 * * @param bool $include Whether to consider Required Assets Above The Fold or not. */ $atf_includes_required = apply_filters( 'et_dynamic_assets_atf_includes_required', false ); $required = $atf_includes_required ? [] : array_keys( $this->get_custom_global_assets_list( '' ) ); $content_atf = ! empty( $content->atf ) ? $content->atf : ''; $atf = $this->get_custom_global_assets_list( $content_atf ); $all = $this->get_global_assets_list(); $assets = $all; $has_btf = ! empty( $content->btf ); $atf = array_keys( $atf ); $all = array_keys( $all ); $icon_set = false; $icons_sets = array( 'et_icons_base', 'et_icons_social', 'et_icons_all', ); foreach ( $icons_sets as $set ) { if ( in_array( $set, $all, true ) ) { $icon_set = $set; break; } } if ( false !== $icon_set ) { $replace = function ( $value ) use ( $icon_set, $icons_sets ) { return in_array( $value, $icons_sets, true ) ? $icon_set : $value; }; $atf = array_values( array_unique( array_map( $replace, $atf ) ) ); if ( ! empty( $required ) ) { $required = array_values( array_unique( array_map( $replace, $required ) ) ); } } if ( empty( $required ) ) { $atf = array_flip( $atf ); } else { $atf = array_flip( array_diff( $atf, $required ) ); } $atf_assets = []; $btf_assets = []; foreach ( $assets as $key => $asset ) { $has_css = isset( $asset['css'] ); $is_required = isset( $required[ $key ] ); $is_atf = isset( $atf[ $key ] ); $is_atf = $is_atf || ( $atf_includes_required && $is_required ); $force_defer = $has_btf && isset( $asset['maybe_defer'] ); // In order for a (global) asset to be considered Above The Fold: // 1.0 It needs to include a CSS section (some of the assets are JS only). // 2.0 It needs to be used in the ATF Content. // 2.1 Or is a required asset (as in always used, doesn't depends on content) and // required assets are considered ATF (configurable behaviour via WP filter) // 3.0 It needs not be marked as `maybe_defer`, which are basically required assets // that will be deferred if the page has Below The Fold Content. if ( $has_css && $is_atf && ! $force_defer ) { $atf_assets[ $key ]['css'] = $asset['css']; unset( $asset['css'] ); } // Some assets are CSS only (no JS), hence if they considered ATF by the previous code // there will be nothing else to do for them when processing BTF Content. if ( ! empty( $asset ) ) { $btf_assets[ $key ] = $asset; } } return (object) [ 'atf' => $atf_assets, 'btf' => $btf_assets, ]; } } ET_Dynamic_Assets::init();