if ( empty( $element_data['class'] ) ) {
// Prevent duplication link options data entries created by global modules
if ( in_array( $element_data['class'], $data_classes ) ) {
$data_classes[] = $element_data['class'];
* Get list of concatenated & minified script and their possible alternative name
function et_builder_get_minified_scripts() {
$minified_scripts = array(
'fitvids', // possible alternative name
'jquery-fitvids', // possible alternative name
'jquery-waypoints', // possible alternative name
'jquery-magnific-popup', // possible alternative name
'jquery-hashchange', // possible alternative name
'jquery-easypiechart', // possible alternative name
'et-builder-modules-global-functions-script',
'et-jquery-touch-mobile',
'et-builder-modules-script',
return apply_filters( 'et_builder_get_minified_scripts', $minified_scripts );
* Get list of concatenated & minified styles (sans style.css)
function et_builder_get_minified_styles() {
$minified_styles = array(
'et-shortcodes-responsive-css',
return apply_filters( 'et_builder_get_minified_styles', $minified_styles );
* Re-enqueue listed concatenated & minified scripts (and their possible alternative name) used empty string
* to keep its dependency in order but avoiding WordPress to print the script to avoid the same file printed twice
* Case in point: salvattore that is being called via builder module's render() method
function et_builder_dequeue_minified_scripts() {
if ( ! et_load_unminified_scripts() && ! is_admin() ) {
* Builder script handle name
$builder_script_handle = apply_filters( 'et_builder_modules_script_handle', 'et-builder-modules-script' );
foreach ( et_builder_get_minified_scripts() as $script ) {
// Get script's localized data before the script is dequeued
$script_data = wp_scripts()->get_data( $script, 'data' );
// If to-be dequeued script has localized data, get builder script's data and concatenated both to ensure compatibility
// Concatenating is needed because script's localize data is saved as string (encoded array concatenated into variable name)
if ( $script_data && '' !== trim( $script_data ) ) {
// If builder script handle localized data returns false/empty, $script_data still need to be added
$concatenated_scripts_data = implode(
wp_scripts()->get_data( $builder_script_handle, 'data' ),
// Add concatenated localized data to builder script handle
wp_scripts()->add_data( $builder_script_handle, 'data', $concatenated_scripts_data );
// If dequeued script has inline script, get it then re-add it to builder script handle using appropriate position
$inline_script_positions = array( 'before', 'after' );
foreach ( $inline_script_positions as $inline_script_position ) {
$inline_script = wp_scripts()->get_data( $script, $inline_script_position );
// Inline script is saved as array. add_inline_script() method will handle it appending process
if ( is_array( $inline_script ) && ! empty( $inline_script ) ) {
wp_scripts()->add_inline_script( $builder_script_handle, implode( ' ', $inline_script ), $inline_script_position );
wp_dequeue_script( $script );
wp_deregister_script( $script );
wp_register_script( $script, '', array(), ET_BUILDER_VERSION, true );
add_action( 'wp_print_scripts', 'et_builder_dequeue_minified_scripts', 99999999 ); // <head>
add_action( 'wp_print_footer_scripts', 'et_builder_dequeue_minified_scripts', 9 ); // <footer>
function et_builder_dequeue_minifieds_styles() {
if ( ! et_load_unminified_styles() && ! is_admin() ) {
// Get builder minified + combined style handle
$builder_optimized_style_name = apply_filters( 'et_builder_optimized_style_handle', '' );
foreach ( et_builder_get_minified_styles() as $style ) {
// If dequeued style has inline style, get it then re-add it to minified + combiled style handle
// Inline style only has 'after' position
$inline_style = wp_styles()->get_data( $style, 'after' );
// Inline style is saved as array. add_inline_style() method will handle it appending process
if ( is_array( $inline_style ) && ! empty( $inline_style ) ) {
wp_styles()->add_inline_style( $builder_optimized_style_name, implode( ' ', $inline_style ), 'after' );
wp_dequeue_style( $style );
wp_deregister_style( $style );
wp_register_style( $style, '', array(), ET_BUILDER_VERSION );
// Child theme might manually enqueues parent themes' style.css. When combine + minify CSS file is enabled, this isn't an issue.
// However, when combine + minify CSS is disabled, child theme should load style.dev.css (source) instead of style.css (minified).
// Child theme might not considering this, which causes minified file + other source files are printed. To fix it, deregister any
// style handle that contains parent theme's style.css URL, then re-queue new one with the same name handle + URL to parent theme's style.dev.css
// This should be done in theme only. Divi-Builder plugin doesn't need this.
if ( ! et_is_builder_plugin_active() && is_child_theme() ) {
$template_directory_uri = preg_quote( get_template_directory_uri(), '/' );
$optimized_style_src = '/^(' . $template_directory_uri . '\/style)(-cpt)?(\.css)$/';
$unoptimized_style_src = '$1$2.dev$3';
et_core_replace_enqueued_style( $optimized_style_src, $unoptimized_style_src, true );
add_action( 'wp_print_styles', 'et_builder_dequeue_minifieds_styles', 99999999 ); // <head>
* Determine whether current theme supports Waypoints or not
function et_is_ignore_waypoints() {
// WPBakery Visual Composer plugin conflicts with waypoints
if ( class_exists( 'Vc_Manager' ) ) {
// always return false if not in divi plugin
if ( ! et_is_builder_plugin_active() ) {
$theme_data = wp_get_theme();
if ( empty( $theme_data ) ) {
// list of themes without Waypoints support
$no_waypoints_themes = array(
$no_waypoints_themes = apply_filters( 'et_pb_no_waypoints_themes', $no_waypoints_themes );
// return true if current theme doesn't support Waypoints
if ( in_array( $theme_data->Name, $no_waypoints_themes, true ) ) {
* Determine whether current page has enqueued theme's style.css or not
* This is mainly used on preview screen to decide to enqueue theme's style nor not
function et_builder_has_theme_style_enqueued() {
if ( ! empty( $wp_styles->queue ) ) {
$theme_style_uri = get_stylesheet_uri();
foreach ( $wp_styles->queue as $handle ) {
if ( isset( $wp_styles->registered[ $handle ]->src ) && $theme_style_uri === $wp_styles->registered[ $handle ]->src ) {
* Added specific body classes for builder related situation
* This enables theme to adjust its case independently
function et_builder_body_classes( $classes ) {
if ( is_et_pb_preview() ) {
$classes[] = 'et-pb-preview';
// Minified JS identifier class name
if ( ! et_load_unminified_scripts() ) {
$classes[] = 'et_minified_js';
// Minified CSS identifier class name
if ( ! et_load_unminified_styles() ) {
$classes[] = 'et_minified_css';
$post_id = et_core_page_resource_get_the_ID();
$post_type = get_post_type( $post_id );
// Add layout classes when on library page
if ( et_core_is_fb_enabled() && 'et_pb_layout' === $post_type ) {
$layout_type = et_fb_get_layout_type( $post_id );
$layout_scope = et_fb_get_layout_term_slug( $post_id, 'scope' );
$classes[] = "et_pb_library_page-${layout_type}";
$classes[] = "et_pb_library_page-${layout_scope}";
add_filter( 'body_class', 'et_builder_body_classes' );
if ( ! function_exists( 'et_builder_add_main_elements' ) ) :
function et_builder_add_main_elements() {
if ( ET_BUILDER_CACHE_MODULES ) {
ET_Builder_Element::init_cache();
require_once ET_BUILDER_DIR . 'main-structure-elements.php';
require_once ET_BUILDER_DIR . 'main-modules.php';
do_action( 'et_builder_ready' );
if ( ! function_exists( 'et_builder_load_framework' ) ) :
function et_builder_load_framework() {
require_once ET_BUILDER_DIR . 'functions.php';
require_once ET_BUILDER_DIR . 'compat/woocommerce.php';
require_once ET_BUILDER_DIR . 'class-et-global-settings.php';
require_once ET_BUILDER_DIR . 'feature/BlockEditorIntegration.php';
global $pagenow, $et_current_memory_limit;
if ( ! empty( $pagenow ) && in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) ) {
$et_current_memory_limit = et_core_get_memory_limit();
* Filters builder modules loading hook. Load builder files on front-end and on specific admin pages only by default.
* @param string Hook name.
$action_hook = apply_filters( 'et_builder_modules_load_hook', is_admin() ? 'wp_loaded' : 'wp' );
if ( et_builder_should_load_framework() ) {
require_once ET_BUILDER_DIR . 'class-et-builder-value.php';
require_once ET_BUILDER_DIR . 'class-et-builder-element.php';
require_once ET_BUILDER_DIR . 'class-et-builder-plugin-compat-base.php';
require_once ET_BUILDER_DIR . 'class-et-builder-plugin-compat-loader.php';
require_once ET_BUILDER_DIR . 'ab-testing.php';
require_once ET_BUILDER_DIR . 'class-et-builder-settings.php';
$builder_settings_loaded = true;
do_action( 'et_builder_framework_loaded' );
add_action( $action_hook, 'et_builder_init_global_settings', apply_filters( 'et_pb_load_global_settings_priority', 9 ) );
add_action( $action_hook, 'et_builder_add_main_elements', apply_filters( 'et_pb_load_main_elements_priority', 10 ) );
} elseif ( is_admin() ) {
require_once ET_BUILDER_DIR . 'class-et-builder-plugin-compat-base.php';
require_once ET_BUILDER_DIR . 'class-et-builder-plugin-compat-loader.php';
require_once ET_BUILDER_DIR . 'class-et-builder-settings.php';
$builder_settings_loaded = true;
if ( isset( $builder_settings_loaded ) ) {
add_action( 'init', 'et_builder_settings_init', 100 );
add_action( $action_hook, 'et_builder_load_frontend_builder' );
if ( isset( $_GET['et_bfb'] ) && is_user_logged_in() ) {
add_filter( 'wpe_heartbeat_allowed_pages', 'et_bfb_wpe_heartbeat_allowed_pages' );
* Checking whether current page is BFB page based on its query string only; Suitable for basic
* early check BEFORE $wp_query global is generated in case builder need to alter query
* configuration. This is needed because BFB layout is basically loaded in front-end
function et_bfb_maybe_bfb_url() {
$has_bfb_query_string = ! empty( $_GET['et_fb'] ) && ! empty( $_GET['et_bfb'] );
$has_vb_permission = et_pb_is_allowed( 'use_visual_builder' );
// This check assumes that $wp_query isn't ready (to be used before query is parsed) so any
// query based check such as is_single(), etc don't exist yet. Thus BFB URL might valid if:
// 3. has `et_fb` & `et_bfb` query string
// 4. has visual builder permission
return ! is_admin() && is_user_logged_in() && $has_bfb_query_string && $has_vb_permission;
* Get verified query string value for et_bfb_make_post_type_queryable()
* @param string $param_name
* @return string|number|bool
function et_bfb_get_make_queryable_param( $param_name ) {
$param = isset( $_GET[ "et_{$param_name}" ] ) ? $_GET[ "et_{$param_name}" ] : false;
$param_nonce = isset( $_GET[ "et_{$param_name}_nonce" ] ) ? $_GET[ "et_{$param_name}_nonce" ] : false;
$verified_param = $param && $param_nonce && wp_verify_nonce(
"et_{$param_name}_{$param}"
return $verified_param ? $param : false;
* Set builder's registered post type's publicly_queryable property to true (if needed) so publicly
* hidden post type can render BFB page on backend edit screen
* @see WP->parse_request() on how request is parsed
function et_bfb_make_post_type_queryable() {
// Valid query isn't available at this point so builder will guess whether current request is
// BFB based on available value; Stop if this might not be BFB url
if ( ! et_bfb_maybe_bfb_url() ) {
$get_post_id = absint( et_bfb_get_make_queryable_param( 'post_id' ) );
$get_post_type = sanitize_text_field( et_bfb_get_make_queryable_param( 'post_type' ) );
// Stop if no valid post id / post type for make queryable found on query string
if ( ! $get_post_id || ! $get_post_type ) {
$post_type_object = get_post_type_object( $get_post_type );
// Stop if requested post type doesn't exist
if ( is_null( $post_type_object ) ) {
$unqueryable_post_type = et_builder_get_third_party_unqueryable_post_types();
$is_post_type_unqueryable = in_array( $get_post_type, $unqueryable_post_type );
// CPT's version of edit_post is always available on cap->edit_post regardless CPT's meta_map_cap
// or capability_type setting are set or not. If meta_map_cap is set to true, WordPress
// automatically translates it into edit_post. Otherwise, CPT version of edit_post is sent as
// it is and it is plugin / post type registrant's responsibility to add the capability to role
// and map it into primitive capabilities on map_meta_cap()
$capability = isset( $post_type_object->cap->edit_post ) ? $post_type_object->cap->edit_post : 'edit_post';
$can_edit_this_post = current_user_can( $capability, $get_post_id );
// Flip publicly_queryable of current request so BFB layout page can be rendered.
// Note: post_type existence have been verified on is_null( $post_type_object ) check above
if ( $is_post_type_unqueryable && $can_edit_this_post ) {
$wp_post_types[ $get_post_type ]->publicly_queryable = true;
add_action( 'init', 'et_bfb_make_post_type_queryable' );
* Modify rewrite rule's redirect of current BFB request if its post type's `publicly_queryable`
* is set to false and its `query_var` is NOT set to `false`. When this situation happens, current
* BFB page cannot be rendered because rewrite rule's redirect value doesn't have `post_type`
* param which makes page query gets incorrect page value
function et_bfb_make_cpt_rewrite_rule_queryable( $value ) {
// Get verified make queryable post_type param from query string
$unqueryable_post_type = et_bfb_get_make_queryable_param( 'post_type' );
// Make sure that value is array, current request might be BFB, and verified post_type from
// query string exist. Note: need to use early return otherwise the rest need multiple stack
if ( ! is_array( $value ) || ! et_bfb_maybe_bfb_url() || ! $unqueryable_post_type ) {
$rewrite_regex = $unqueryable_post_type . '/([^/]+)(?:/([0-9]+))?/?$';
$rewrite_redirect = isset( $value[ $rewrite_regex ] ) ? $value[ $rewrite_regex ] : false;
$has_post_type_substr = $rewrite_redirect && strpos( $rewrite_redirect, '?post_type=' ) !== false;
$post_type_object = get_post_type_object( $unqueryable_post_type );
// If current page's post type object `query_var` isn't falsey and no `post_type=` substring is
// found on current page's post type rewrite rule redirect value, modify the rewrite rule
// redirect value so it can picks up current post type when query is parsed
if ( $post_type_object->query_var && ! $has_post_type_substr ) {
$value[ $rewrite_regex ] = 'index.php?post_type=' . $unqueryable_post_type . '&name=$matches[1]&page=$matches[2]';
add_filter( 'option_rewrite_rules', 'et_bfb_make_cpt_rewrite_rule_queryable' );
if ( ! function_exists( 'et_bfb_wpe_heartbeat_allowed_pages' ) ) :
function et_bfb_wpe_heartbeat_allowed_pages( $pages ) {
function et_builder_load_frontend_builder() {
global $et_current_memory_limit;
$et_current_memory_limit = et_core_get_memory_limit();
if ( $et_current_memory_limit < 256 ) {
@ini_set( 'memory_limit', '256M' );
require_once ET_BUILDER_DIR . 'frontend-builder/init.php';
if ( ! function_exists( 'et_pb_get_google_api_key' ) ) :
function et_pb_get_google_api_key() {
$google_api_option = get_option( 'et_google_api_settings' );
$google_api_key = isset( $google_api_option['api_key'] ) ? $google_api_option['api_key'] : '';
if ( ! function_exists( 'et_pb_enqueue_google_maps_script' ) ) :
function et_pb_enqueue_google_maps_script() {
$google_api_option = get_option( 'et_google_api_settings' );
$google_maps_script_enqueue = ! $google_api_option || ! isset( $google_api_option['enqueue_google_maps_script'] ) || ( isset( $google_api_option['enqueue_google_maps_script'] ) && 'on' === $google_api_option['enqueue_google_maps_script'] ) ? true : false;
'et_pb_enqueue_google_maps_script',
$google_maps_script_enqueue
* Add pseudo-action via the_content to hook filter/action at the end of main content
* @param string content string
* @return string content string