(.*?)<\/script>/mis', array( $this, 'fix_wptexturized_script' ), $content ); } /** * Callback :: Sort fields within tabs by priority. * * @param string $a Comparision field member. * @param string $b Comparision field string. * * @return int|lt */ public static function compare_by_priority( $a, $b ) { $a_priority = ! empty( $a['priority'] ) ? (int) $a['priority'] : self::DEFAULT_PRIORITY; $b_priority = ! empty( $b['priority'] ) ? (int) $b['priority'] : self::DEFAULT_PRIORITY; if ( isset( $a['_order_number'], $b['_order_number'] ) && ( $a_priority === $b_priority ) ) { return $a['_order_number'] - $b['_order_number']; } return $a_priority - $b_priority; } /** * Reorder toggles based on the priority with respect to manually ordered items with no priority * * @param array $toggles_array Toggles to reorder. * * @return array */ public static function et_pb_order_toggles_by_priority( $toggles_array ) { if ( empty( $toggles_array ) ) { return array(); } $high_priority_toggles = array(); $low_priority_toggles = array(); $manually_ordered_toggles = array(); // fill 3 arrays based on priority. foreach ( $toggles_array as $toggle_id => $toggle_data ) { if ( isset( $toggle_data['priority'] ) ) { if ( $toggle_data['priority'] < 10 ) { $high_priority_toggles[ $toggle_id ] = $toggle_data; } else { $low_priority_toggles[ $toggle_id ] = $toggle_data; } } else { // keep the original order of options without priority defined. $manually_ordered_toggles[ $toggle_id ] = $toggle_data; } } // order high and low priority toggles. uasort( $high_priority_toggles, array( 'self', 'compare_by_priority' ) ); uasort( $low_priority_toggles, array( 'self', 'compare_by_priority' ) ); // merge 3 arrays to get the correct order of toggles. return array_merge( $high_priority_toggles, $manually_ordered_toggles, $low_priority_toggles ); } /** * Callback :: Sort modules alphabetically by name. * * @param string $a Comparision member string. * @param string $b Comparision member string. * * @return int|lt */ public static function compare_by_name( $a, $b ) { return strcasecmp( $a->name, $b->name ); } /** * Get total modules count. * * @param string $post_type Post type. * * @return int */ public static function get_modules_count( $post_type ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); $overall_count = count( $parent_modules ) + count( $child_modules ); return $overall_count; } /** * Get modules js array to use in backbone template. * * @param string $post_type Current post type. * * @return string */ public static function get_modules_js_array( $post_type ) { $modules = array(); $parent_modules = self::get_parent_modules( $post_type ); if ( ! empty( $parent_modules ) ) { /** * Sort modules alphabetically by name. */ $sorted_modules = $parent_modules; uasort( $sorted_modules, array( 'self', 'compare_by_name' ) ); foreach ( $sorted_modules as $module ) { /** * Replace single and double quotes with %% and || respectively * to avoid js conflicts */ $module_name = str_replace( array( '"', '"', '"', '"' ), '%%', $module->name ); $module_name = str_replace( array( "'", ''', ''' ), '||', $module_name ); $modules[] = sprintf( '{ "title" : "%1$s", "label" : "%2$s"%3$s}', esc_js( $module_name ), esc_js( $module->slug ), ( isset( $module->fullwidth ) && $module->fullwidth ? ', "fullwidth_only" : "on"' : '' ) ); } } return '[' . implode( ',', $modules ) . ']'; } /** * Get all modules array. * * @param string $post_type Post type. * @param bool $include_child Whether to include childs. * * @return array */ public static function get_modules_array( $post_type = '', $include_child = false ) { $modules = array(); $module_icons = self::get_module_icons(); if ( ! empty( $post_type ) ) { $parent_modules = self::get_parent_modules( $post_type ); if ( $include_child ) { $parent_modules = array_merge( $parent_modules, self::get_child_modules( $post_type ) ); } if ( ! empty( $parent_modules ) ) { $sorted_modules = $parent_modules; } } else { $parent_modules = self::get_parent_modules(); if ( $include_child ) { $parent_modules = array_merge( $parent_modules, self::get_child_modules() ); } if ( ! empty( $parent_modules ) ) { $all_modules = array(); foreach ( $parent_modules as $post_type => $post_type_modules ) { foreach ( $post_type_modules as $module_slug => $module ) { $all_modules[ $module_slug ] = $module; } } $sorted_modules = $all_modules; } } if ( ! empty( $sorted_modules ) ) { /** * Sort modules alphabetically by name. */ uasort( $sorted_modules, array( 'self', 'compare_by_name' ) ); foreach ( $sorted_modules as $module ) { /** * Replace single and double quotes with %% and || respectively * to avoid js conflicts */ $module_name = str_replace( '"', '%%', $module->name ); $module_name = str_replace( "'", '||', $module_name ); $module_name_plural = str_replace( '"', '%%', empty( $module->plural ) ? $module->name : $module->plural ); $module_name_plural = str_replace( "'", '||', $module_name_plural ); $_module = array( 'title' => esc_attr( $module_name ), 'plural' => esc_attr( $module_name_plural ), 'label' => esc_attr( $module->slug ), 'is_parent' => 'child' === $module->type ? 'off' : 'on', 'is_official_module' => $module->_is_official_module, 'use_unique_id' => $module->_use_unique_id, 'vb_support' => isset( $module->vb_support ) ? $module->vb_support : 'off', 'folder_name' => isset( $module->folder_name ) ? $module->folder_name : '', ); if ( isset( $module->fullwidth ) && $module->fullwidth ) { $_module['fullwidth_only'] = 'on'; } // Get module icon character (font-icon). $icon = self::$_->array_get( $module_icons, "{$module->slug}.icon" ); if ( $icon ) { $_module['icon'] = $icon; } // Get module icon svg from fetched svg content. $icon_svg = self::$_->array_get( $module_icons, "{$module->slug}.icon_svg" ); if ( $icon_svg ) { $_module['icon_svg'] = $icon_svg; } $modules[] = $_module; } } return $modules; } /** * Get modules that does not support VB. * * @return array */ public static function get_fb_unsupported_modules() { $parent_modules = self::get_parent_modules(); $unsupported_modules_array = array(); foreach ( $parent_modules as $post_type => $post_type_modules ) { foreach ( $post_type_modules as $module_slug => $module ) { if ( ! isset( $module->vb_support ) || 'off' === $module->vb_support ) { $unsupported_modules_array[] = $module_slug; } } } return array_unique( $unsupported_modules_array ); } /** * Get list of modules that has rich content option * * @since 3.18 * * @return array */ public static function get_has_content_modules() { return self::$has_content_modules; } /** * Returns a regex pattern that includes all parent module slugs. * * @since 3.1 Renamed from `get_parent_shortcodes()` to `get_parent_slugs_regex()` * @since 1.0 * * @param string $post_type Post type. * * @return string */ public static function get_parent_slugs_regex( $post_type = 'page' ) { $slugs = array(); $parent_modules = self::get_parent_modules( $post_type ); if ( ! empty( $parent_modules ) ) { foreach ( $parent_modules as $module ) { $slugs[] = $module->slug; } } return implode( '|', $slugs ); } /** * Returns a regex pattern that includes all child module slugs. * * @since 3.1 Renamed from `get_child_shortcodes()` to `get_child_slugs_regex()` * @since 1.0 * * @param string $post_type Post type. * * @return string */ public static function get_child_slugs_regex( $post_type = 'page' ) { $slugs = array(); $child_modules = self::get_child_modules( $post_type ); if ( ! empty( $child_modules ) ) { foreach ( $child_modules as $slug => $module ) { if ( ! empty( $slug ) ) { $slugs[] = $slug; } } } return implode( '|', $slugs ); } /** * Get child module slugs. * * @param string $post_type Post type. * * @return array */ public static function get_child_slugs( $post_type ) { $child_slugs = array(); $child_modules = self::get_parent_modules( $post_type ); if ( ! empty( $child_modules ) ) { foreach ( $child_modules as $module ) { if ( ! empty( $module->child_slug ) ) { $child_slugs[ $module->slug ] = $module->child_slug; } } } return $child_slugs; } /** * Get row content module slugs. e.x et_pb_code * * @param string $post_type Post type. * * @return string */ public static function get_raw_content_slugs( $post_type ) { $shortcodes = array(); $parent_modules = self::get_parent_modules( $post_type ); if ( ! empty( $parent_modules ) ) { foreach ( $parent_modules as $module ) { if ( isset( $module->use_raw_content ) && $module->use_raw_content ) { $shortcodes[] = $module->slug; } } } $child_modules = self::get_child_modules( $post_type ); if ( ! empty( $child_modules ) ) { foreach ( $child_modules as $module ) { if ( isset( $module->use_raw_content ) && $module->use_raw_content ) { $shortcodes[] = $module->slug; } } } return implode( '|', $shortcodes ); } /** * Get the portion of templates for specified slugs. * * @param string $post_type Post type. * @param array $slugs_array Module slugs. * * @return array|string|void */ public static function get_modules_templates( $post_type, $slugs_array ) { $all_modules = self::get_parent_and_child_modules( $post_type ); $templates_array = array(); if ( empty( $slugs_array ) ) { return; } foreach ( $slugs_array as $slug ) { if ( ! isset( $all_modules[ $slug ] ) ) { return ''; } $module = $all_modules[ $slug ]; $templates_array[] = array( 'slug' => $slug, 'template' => $module->build_microtemplate(), ); } if ( ET_BUILDER_OPTIMIZE_TEMPLATES ) { $templates_array = array( 'templates' => $templates_array, 'unique' => self::$_unique_bb_keys_values, ); } return $templates_array; } /** * Output modules backbone templates. * * @param string $post_type Post type. * @param int $start_from Unused arg. * @param int $amount Unused arg. * * @return array */ public static function output_templates( $post_type = '', $start_from = 0, $amount = 999 ) { $all_modules = self::get_parent_and_child_modules( $post_type ); $modules_names = array_keys( $all_modules ); $output = array(); $output['templates'] = array(); if ( ! empty( $all_modules ) ) { for ( $i = 0; $i < ET_BUILDER_AJAX_TEMPLATES_AMOUNT; $i++ ) { if ( isset( $modules_names[ $i ] ) ) { $module = $all_modules[ $modules_names[ $i ] ]; $output['templates'][ $module->slug ] = self::optimize_bb_chunk( $module->build_microtemplate() ); } else { break; } } } if ( ET_BUILDER_OPTIMIZE_TEMPLATES ) { $output['unique'] = self::$_unique_bb_keys_values; } return $output; } /** * Get structure module slugs. * * @return array */ public static function get_structure_module_slugs() { if ( ! empty( self::$structure_module_slugs ) ) { return self::$structure_module_slugs; } $structure_modules = self::get_structure_modules(); self::$structure_module_slugs = array(); foreach ( $structure_modules as $structural_module ) { self::$structure_module_slugs[] = $structural_module->slug; } /** * Filters structural module slugs. * * @since 4.10.0 * * @param array $structure_module_slugs. */ return apply_filters( 'et_builder_get_structural_module_slugs', self::$structure_module_slugs ); } /** * Get structure modules. * * @return array */ public static function get_structure_modules() { if ( ! empty( self::$structure_modules ) ) { return self::$structure_modules; } $parent_modules = self::get_parent_modules( 'et_pb_layout' ); self::$structure_modules = array(); foreach ( $parent_modules as $parent_module ) { if ( isset( $parent_module->is_structure_element ) && $parent_module->is_structure_element ) { $parent_module->plural = empty( $parent_module->plural ) ? $parent_module->name : $parent_module->plural; self::$structure_modules[] = $parent_module; } } return self::$structure_modules; } /** * Get a filtered list of modules. * * @since 3.10 * * @param string $post_type Leave empty for any. * @param string $type 'parent' or 'child'. Leave empty for any. * * @return ET_Builder_Element[] */ public static function get_modules( $post_type = '', $type = '' ) { $modules = array(); foreach ( self::$modules as $slug => $module ) { if ( '' !== $post_type && ! in_array( $post_type, $module->post_types, true ) ) { continue; } if ( '' !== $type && ! $module->type !== $type ) { continue; } $modules[ $slug ] = $module; } return $modules; } /** * Get modules by fallback post type for disabled post type. * * @param string $type Module type. * * @return ET_Builder_Element[]|mixed */ public static function get_custom_post_type_fallback_modules( $type = 'parent' ) { $modules = 'child' === $type ? self::$child_modules : self::$parent_modules; // Most of the time, page module is expected to be used as disabled post type fallback. if ( isset( $modules['page'] ) ) { return $modules['page']; } // Post module is also expected to be used. if ( isset( $modules['post'] ) ) { return $modules['post']; } // If Divi Builder is disabled for all post types use layout modules as fallback. if ( isset( $modules['et_pb_layout'] ) ) { return $modules['et_pb_layout']; } // If all else fail, use all modules. return self::get_modules(); } /** * Get all parent modules. * * @param string $post_type Current post type. * * @return mixed|void */ public static function get_parent_modules( $post_type = '' ) { if ( ! empty( $post_type ) ) { // We get all modules when post type is not enabled so that posts that have // had their post type support disabled still load all necessary modules. $parent_modules = ! empty( self::$parent_modules[ $post_type ] ) ? self::$parent_modules[ $post_type ] : self::get_custom_post_type_fallback_modules( 'parent' ); } else { $parent_modules = self::$parent_modules; } return apply_filters( 'et_builder_get_parent_modules', $parent_modules, $post_type ); } /** * Get all child modules. * * @param string $post_type Current post type. * * @return mixed|void */ public static function get_child_modules( $post_type = '' ) { if ( ! empty( $post_type ) ) { // We get all modules when post type is not enabled so that posts that have // had their post type support disabled still load all necessary modules. $child_modules = ! empty( self::$child_modules[ $post_type ] ) ? self::$child_modules[ $post_type ] : self::get_custom_post_type_fallback_modules( 'child' ); } else { $child_modules = self::$child_modules; } return apply_filters( 'et_builder_get_child_modules', $child_modules, $post_type ); } /** * Get woocommerce modules. * * @return mixed|void */ public static function get_woocommerce_modules() { return apply_filters( 'et_builder_get_woocommerce_modules', self::$woocommerce_modules ); } /** * Get registered module icons * * @since 3.1 * * @return array */ public static function get_module_icons() { /** * Filters Module Icons displayed in Add Module modals. * * @param array $module_icons Array of all registered module icons. */ $module_icons = apply_filters( 'et_builder_module_icons', self::$module_icons ); // Loop module icons. foreach ( $module_icons as $key => $icons ) { if ( isset( $icons['icon_path'] ) ) { // Get svg content based on given svg's path. $icon_svg = et_()->WPFS()->exists( $icons['icon_path'] ) ? et_()->WPFS()->get_contents( $icons['icon_path'] ) : false; if ( $icon_svg ) { $module_icons[ $key ]['icon_svg'] = $icon_svg; // Remove icon path attribute since it's no longer used. unset( $module_icons[ $key ]['icon_path'] ); } } } return $module_icons; } /** * Get combined array of child and parent modules for provided post_type * * @param string $post_type Post type. * * @since 3.1 * * @return array */ public static function get_parent_and_child_modules( $post_type = '' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); return array_merge( $parent_modules, $child_modules ); } /** * Get a module instance for provided post type by its slug. * * @since 3.10 * * @param string $slug Module slug. * @param string $post_type Current post type. * * @return ET_Builder_Element|null */ public static function get_module( $slug, $post_type = 'post' ) { $modules = self::get_parent_and_child_modules( $post_type ); return self::$_->array_get( $modules, $slug ); } /** * Outputs list of all module help videos array * * @since 3.1 * * @return array */ public static function get_help_videos() { return self::$module_help_videos; } /** * Get list of modules with support of featured image as background. * * @param string $post_type Current post type. * * @return mixed|void */ public static function get_featured_image_background_modules( $post_type = '' ) { $parent_modules = self::get_parent_modules( $post_type ); $featured_image_background_modules = array(); foreach ( $parent_modules as $slug => $parent_module ) { if ( ! empty( $parent_module->featured_image_background ) ) { $featured_image_background_modules[] = $slug; } } /** * Filters list of modules with support of featured image as background. * * @since 3.1 * * @param array[] $featured_image_background_modules List of modules with support of featured image as background. */ return apply_filters( 'et_pb_featured_image_background_modules', $featured_image_background_modules ); } /** * Get field group toggles. * * @param string $post_type Post type. * * @return array */ public static function get_toggles( $post_type ) { static $toggles_array = array(); if ( $toggles_array ) { return $toggles_array; } $modules = self::get_parent_and_child_modules( $post_type ); $custom_modules = array(); foreach ( $modules as $module_slug => $module ) { if ( ! $module->_is_official_module ) { $custom_modules[ $module_slug ] = $module; } foreach ( $module->settings_modal_toggles as $tab_slug => &$tab_data ) { if ( ! isset( $tab_data['toggles'] ) ) { continue; } $tab_data['toggles'] = self::et_pb_order_toggles_by_priority( $tab_data['toggles'] ); } $toggles_array[ $module_slug ] = $module->settings_modal_toggles; } if ( $custom_modules ) { // Add missing toggle definitions for any existing toggles used in custom modules. foreach ( $custom_modules as $module_slug => $module ) { foreach ( $module->get_complete_fields() as $field_name => $field_info ) { $tab_slug = self::$_->array_get( $field_info, 'tab_slug' ); $tab_slug = empty( $tab_slug ) ? 'general' : $tab_slug; $toggle_slug = self::$_->array_get( $field_info, 'toggle_slug' ); if ( ! $toggle_slug || isset( $toggles_array[ $module_slug ][ $tab_slug ]['toggles'][ $toggle_slug ] ) ) { continue; } // Find existing definition. foreach ( $toggles_array as $_module_slug => $tabs ) { foreach ( $tabs as $tab => $toggles ) { if ( isset( $toggles['toggles'][ $toggle_slug ] ) ) { self::$_->array_set( $toggles_array, "{$module_slug}.{$tab_slug}.toggles.{$toggle_slug}", $toggles['toggles'][ $toggle_slug ] ); $toggles_array[ $module_slug ][ $tab_slug ]['toggles'] = self::et_pb_order_toggles_by_priority( $toggles_array[ $module_slug ][ $tab_slug ]['toggles'] ); break 2; } } } // Add missing unregistered toggles to the list. if ( ! isset( $toggles_array[ $module_slug ][ $tab_slug ]['toggles'][ $toggle_slug ] ) ) { if ( ! isset( $toggles_array[ $module_slug ][ $tab_slug ] ) ) { $toggles_array[ $module_slug ][ $tab_slug ] = array( 'toggles' => array( $toggle_slug ) ); } else { $toggles_array[ $module_slug ][ $tab_slug ]['toggles'][] = $toggle_slug; } } } } } return $toggles_array; } /** * Get setting modal tabs. * * @param string $post_type Current post type. * * @return array */ public static function get_tabs( $post_type = '' ) { $official_tabs = array( 'general' => '', 'advanced' => '', 'custom_css' => '', ); $tabs_array = array(); $modules = self::get_parent_and_child_modules( $post_type ); foreach ( $modules as $module_slug => $module ) { if ( '' === $post_type ) { foreach ( $module as $_module_slug => $_module ) { // Backward compatibility with custom tabs registered via `et_builder_main_tabs` filter. $bb_custom_tabs = array_diff_key( $_module->get_main_tabs(), $official_tabs ); $bb_custom_tabs_formatted = array(); // Prepare properly formatted array of tabs data. foreach ( $bb_custom_tabs as $tab_id => $tab_name ) { $bb_custom_tabs_formatted[ $tab_id ] = array( 'name' => $tab_name ); } // Add BB custom tabs to all modules. $tabs_array[ $_module_slug ] = $bb_custom_tabs_formatted; if ( ! isset( $_module->settings_modal_tabs ) ) { continue; } $tabs_array[ $_module_slug ] = array_merge( $tabs_array[ $_module_slug ], $_module->settings_modal_tabs ); } } else { // Backward compatibility with custom tabs registered via `et_builder_main_tabs` filter. $bb_custom_tabs = array_diff_key( $module->get_main_tabs(), $official_tabs ); $bb_custom_tabs_formatted = array(); // Prepare properly formatted array of tabs data. foreach ( $bb_custom_tabs as $tab_id => $tab_name ) { $bb_custom_tabs_formatted[ $tab_id ] = array( 'name' => $tab_name ); } // Add BB custom tabs to all modules. $tabs_array[ $module_slug ] = $bb_custom_tabs_formatted; if ( ! isset( $module->settings_modal_tabs ) ) { continue; } $tabs_array[ $module_slug ] = array_merge( $tabs_array[ $module_slug ], $module->settings_modal_tabs ); } } return $tabs_array; } /** * Get permission options categories. * * @return array */ public static function get_options_categories() { $options_categories = array( 'edit_colors' => array( 'name' => esc_html__( 'Edit Colors', 'et_builder' ), ), 'edit_content' => array( 'name' => esc_html__( 'Edit Content', 'et_builder' ), ), 'edit_fonts' => array( 'name' => esc_html__( 'Edit Fonts', 'et_builder' ), ), 'edit_buttons' => array( 'name' => esc_html__( 'Edit Buttons', 'et_builder' ), ), 'edit_layout' => array( 'name' => esc_html__( 'Edit Layout', 'et_builder' ), ), 'edit_borders' => array( 'name' => esc_html__( 'Edit Borders', 'et_builder' ), ), 'edit_configuration' => array( 'name' => esc_html__( 'Edit Configuration', 'et_builder' ), ), ); $options_categories = array_merge( $options_categories, self::get_custom_options_categories() ); return $options_categories; } /** * Get custom permission option categories. * * @param string $post_type Post type. * * @return array */ public static function get_custom_options_categories( $post_type = '' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); $custom_options_categories = array(); $_modules = array_merge_recursive( $parent_modules, $child_modules ); foreach ( $_modules as $_module_slug => $_module ) { if ( '' === $post_type ) { foreach ( $_module as $__module_slug => $__module ) { if ( ! isset( $__module->options_categories ) ) { continue; } $custom_options_categories = array_merge( $custom_options_categories, $__module->options_categories ); } } else { if ( ! isset( $_module->options_categories ) ) { continue; } $custom_options_categories = array_merge( $custom_options_categories, $_module->options_categories ); } } return $custom_options_categories; } /** * Get all fields. * * @param string $post_type Post type. * * @return array */ public static function get_all_fields( $post_type = '' ) { $_modules = self::get_parent_and_child_modules( $post_type ); $module_fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // skip modules without fb support. if ( ! isset( $_module->vb_support ) || 'off' === $_module->vb_support ) { continue; } $_module->set_fields(); $_module->_add_additional_fields(); $_module->_add_custom_css_fields(); $_module->_maybe_add_defaults(); $_module->_finalize_all_fields(); foreach ( $_module->fields_unprocessed as $field_key => $field ) { // do not add the fields with 'skip' type. These fields used for rendering shortcode on Front End only. if ( isset( $field['type'] ) && 'skip' === $field['type'] ) { continue; } $field['name'] = $field_key; $module_fields[ $_module_slug ][ $field_key ] = $field; } } return $module_fields; } /** * Get general fields of modules. * * @param string $post_type Post type. * @param string $mode Modules Mode - Parent, Child and All. * @param string $module_type Module Slug. * * @return array|mixed */ public static function get_general_fields( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $module_fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } foreach ( $_module->fields_unprocessed as $field_key => $field ) { $is_option_template = self::$option_template->is_option_template_field( $field_key ); // Do not process field template. if ( ! $is_option_template && ( isset( $field['tab_slug'] ) && 'general' !== $field['tab_slug'] ) ) { continue; } // Skip if current option template isn't eligible for `advanced` tab. if ( $is_option_template && ! self::$option_template->is_template_inside_tab( 'general', $field ) ) { continue; } $module_fields[ $_module_slug ][ $field_key ] = $field; } // Some module types must be separated for the Global Presets. // For example we keep all section types as `et_pb_section` however they need different Global Presets. $additional_slugs = self::$global_presets_manager->get_module_additional_slugs( $_module_slug ); foreach ( $additional_slugs as $alias ) { $module_fields[ $alias ] = $module_fields[ $_module_slug ]; } } if ( 'all' !== $module_type ) { return $module_fields[ $module_type ]; } return $module_fields; } /** * Get setting fields from custom tabs. * * @param string $post_type Post type. * @param string $mode Modules Mode - Parent, Child and All. * @param string $module_type Module Slug. * * @return array|mixed */ public static function get_settings_modal_tabs_fields( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $module_fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } foreach ( $_module->fields_unprocessed as $field_key => $field ) { $this_tab_slug = isset( $field['tab_slug'] ) ? $field['tab_slug'] : false; if ( ! $this_tab_slug || in_array( $this_tab_slug, array( 'general', 'advanced', 'custom_css' ), true ) ) { continue; } $field['name'] = $field_key; $module_fields[ $_module_slug ][ $this_tab_slug ][ $field_key ] = $field; } } if ( 'all' !== $module_type ) { return $module_fields[ $module_type ]; } return $module_fields; } /** * Get child module titles. * * @param string $post_type Post type. * * @return array */ public static function get_child_module_titles( $post_type ) { $child_modules = self::get_child_modules( $post_type ); $child_modules_titles = array(); $child_modules_titles_fields = array( 'advanced_setting_title_text', 'child_title_fallback_var', 'child_title_var' ); foreach ( $child_modules as $_module_slug => $_module ) { foreach ( $child_modules_titles_fields as $single_field ) { if ( isset( $_module->$single_field ) ) { $child_modules_titles[ $_module_slug ][ $single_field ] = $_module->$single_field; } } } return $child_modules_titles; } /** * Get advanced fields. * * @param string $post_type Post type. * @param string $mode Whether modules are parent, child or all. * @param string $module_type Module slug. * * @return array|mixed */ public static function get_advanced_fields( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $module_fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } foreach ( $_module->fields_unprocessed as $field_key => $field ) { $is_option_template = self::$option_template->is_option_template_field( $field_key ); // Do not process field template. if ( ! $is_option_template && ( ! isset( $field['tab_slug'] ) || 'advanced' !== $field['tab_slug'] ) ) { continue; } // Skip if current option template isn't eligible for `advanced` tab. if ( $is_option_template && ! self::$option_template->is_template_inside_tab( 'advanced', $field ) ) { continue; } if ( isset( $field['default'] ) ) { $module_fields[ $_module_slug ]['advanced_defaults'][ $field_key ] = $field['default']; } $module_fields[ $_module_slug ][ $field_key ] = $field; } if ( ! empty( $_module->advanced_fields ) ) { $module_fields[ $_module_slug ]['advanced_common'] = $_module->advanced_fields; if ( isset( $_module->advanced_fields['border']['border_styles'] ) ) { $module_fields[ $_module_slug ]['border_styles'] = array_merge( $module_fields[ $_module_slug ]['border_styles'], $_module->advanced_fields['border']['border_styles'] ); } if ( isset( $_module->advanced_fields['border']['border_radii'] ) ) { $module_fileds[ $_module_slug ]['border_radii'] = array_merge( $module_fields[ $_module_slug ]['border_radii'], $_module->advanced_fields['border']['border_radii'] ); } } // Some module types must be separated for the Global Presets. // For example we keep all section types as `et_pb_section` however they need different Global Presets. $additional_slugs = self::$global_presets_manager->get_module_additional_slugs( $_module_slug ); foreach ( $additional_slugs as $alias ) { $module_fields[ $alias ] = $module_fields[ $_module_slug ]; } } if ( 'all' !== $module_type ) { return $module_fields[ $module_type ]; } return $module_fields; } /** * Get custom css fields. * * @param string $post_type Post type. * @param string $mode Whether modules are parent, child or all. * @param string $module_type Module slug. * * @return array|mixed */ public static function get_custom_css_fields( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $module_fields = array(); $custom_css_unwanted_types = array( 'custom_css', 'column_settings_css', 'column_settings_css_fields', 'column_settings_custom_css' ); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } $module_fields[ $_module_slug ] = $_module->custom_css_fields; foreach ( $module_fields[ $_module_slug ] as &$item ) { $item['hover'] = self::$_->array_get( $item, 'hover', 'tabs' ); $item['mobile_options'] = self::$_->array_get( $item, 'mobile_options', true ); $item['sticky'] = self::$_->array_get( $item, 'sticky', true ); } // Automatically added module ID and module class fields to setting modal's CSS tab. if ( ! empty( $_module->fields_unprocessed ) ) { foreach ( $_module->fields_unprocessed as $field_unprocessed_key => $field_unprocessed ) { $has_tab_slug = isset( $field_unprocessed['tab_slug'] ); $is_css_field = $has_tab_slug && 'custom_css' === $field_unprocessed['tab_slug']; $has_type = isset( $field_unprocessed['type'] ); $is_unwanted_css_field = $has_type && in_array( $field_unprocessed['type'], $custom_css_unwanted_types, true ); $is_template_inside_css_tab = is_string( $field_unprocessed ) && self::$option_template->is_template_inside_tab( 'custom_css', $field_unprocessed ); // Option template's template that might be rendered in custom_css tab. if ( ( $is_css_field && ! $is_unwanted_css_field ) || $is_template_inside_css_tab ) { $module_fields[ $_module_slug ][ $field_unprocessed_key ] = $field_unprocessed; } } } // Some module types must be separated for the Global Presets. // For example we keep all section types as `et_pb_section` however they need different Global Presets. $additional_slugs = self::$global_presets_manager->get_module_additional_slugs( $_module_slug ); foreach ( $additional_slugs as $alias ) { $module_fields[ $alias ] = $module_fields[ $_module_slug ]; } } if ( 'all' !== $module_type ) { return $module_fields[ $module_type ]; } return $module_fields; } /** * Get modules i10n. * * @param string $post_type Post type. * @param string $mode Whether it is parent, child or all module. * @param string $module_type Module slug. * * @return array|mixed */ public static function get_modules_i10n( $post_type = '', $mode = 'all', $module_type = 'all' ) { $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } $fields = array(); foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } $fields[ $_module_slug ] = array( 'addNew' => $_module->add_new_child_text(), ); } if ( 'all' !== $module_type ) { return $fields[ $module_type ]; } return $fields; } /** * Get CSS fields transition for module. * * @param string $post_type Post type. * @param string $mode Whether it is parent, child or all module. * @param string $module_type Module slug. * * @return array */ public static function get_modules_transitions( $post_type = '', $mode = 'all', $module_type = 'all' ) { /** * List of `ET_Builder_Element` instances. * * @var ET_Builder_Element[] $_modules */ $parent_modules = self::get_parent_modules( $post_type ); $child_modules = self::get_child_modules( $post_type ); $fields = array(); switch ( $mode ) { case 'parent': $_modules = $parent_modules; break; case 'child': $_modules = $child_modules; break; default: $_modules = array_merge( $parent_modules, $child_modules ); break; } foreach ( $_modules as $_module_slug => $_module ) { // filter modules by slug if needed. if ( 'all' !== $module_type && $module_type !== $_module_slug ) { continue; } $fields[ $_module_slug ] = $_module->get_transition_fields_css_props(); } return $fields; } /** * Get module items configs. * * @param string $post_type Current post type. * * @return array */ public static function get_module_items_configs( $post_type ) { $modules = self::get_parent_and_child_modules( $post_type ); $configs = array(); foreach ( $modules as $slug => $module ) { if ( isset( $module->module_items_config ) ) { $configs[ $slug ] = $module->module_items_config; } } return $configs; } /** * Get combined array of parent and child modules fields. * * @param string $post_type Post type. * @param string $module Parent module slug. * * @return array|bool */ public static function get_module_fields( $post_type, $module ) { $_modules = self::get_parent_and_child_modules( $post_type ); if ( ! empty( $_modules[ $module ] ) ) { return $_modules[ $module ]->fields_unprocessed; } return false; } /** * Get all fields of parent module. * * @param string $post_type Post type. * @param string $module Module slug. * * @return bool */ public static function get_parent_module_fields( $post_type, $module ) { if ( ! empty( self::$parent_modules[ $post_type ][ $module ] ) ) { return self::$parent_modules[ $post_type ][ $module ]->get_complete_fields(); } return false; } /** * Get all child module fields. * * @param string $post_type Post type. * @param string $module Module slug. * * @return bool */ public static function get_child_module_fields( $post_type, $module ) { if ( ! empty( self::$child_modules[ $post_type ][ $module ] ) ) { return self::$child_modules[ $post_type ][ $module ]->get_complete_fields(); } return false; } /** * Get parent module field. * * @param string $post_type Post type. * @param string $module Module slug. * @param string $field Field slug. * * @return bool|mixed */ public static function get_parent_module_field( $post_type, $module, $field ) { $fields = self::get_parent_module_fields( $post_type, $module ); if ( ! empty( $fields[ $field ] ) ) { return $fields[ $field ]; } return false; } /** * Return font icon fields of all modules. * * @param string $post_type Current post type. * * @return array */ public static function get_font_icon_fields( $post_type = '' ) { $_modules = self::get_parent_and_child_modules( $post_type ); $module_fields = array(); foreach ( $_modules as $module_name => $module ) { foreach ( $module->fields_unprocessed as $module_field_name => $module_field ) { if ( isset( $module_field['type'] ) && 'select_icon' === $module_field['type'] ) { $module_fields[ $module_name ][ $module_field_name ] = true; } } } return $module_fields; } /** * Retrieves credits of custom modules for VB * * @param string $post_type Current post type. * * @return array of credits info by module slug */ public static function get_custom_modules_credits( $post_type = '' ) { $result = array(); $modules = self::get_parent_and_child_modules( $post_type ); /** * Loop over the all modules to gather module credits. * * @var $module_slug string * @var $module ET_Builder_Module */ foreach ( $modules as $module_slug => $module ) { // Include custom module credits for displaying them within VB. if ( $module->_is_official_module ) { continue; } else { if ( isset( $module->module_credits ) && is_array( $module->module_credits ) ) { $result[ $module_slug ] = $module->module_credits; } } } return $result; } /** * Return media query key value pairs. * * @param bool $for_js Whether media queries is for js ETBuilderBackend.et_builder_css_media_queries variable. * * @return array|mixed|void */ public static function get_media_quries( $for_js = false ) { $media_queries = array( 'min_width_1405' => '@media only screen and ( min-width: 1405px )', '1100_1405' => '@media only screen and ( min-width: 1100px ) and ( max-width: 1405px)', '981_1405' => '@media only screen and ( min-width: 981px ) and ( max-width: 1405px)', '981_1100' => '@media only screen and ( min-width: 981px ) and ( max-width: 1100px )', 'min_width_981' => '@media only screen and ( min-width: 981px )', 'max_width_980' => '@media only screen and ( max-width: 980px )', '768_980' => '@media only screen and ( min-width: 768px ) and ( max-width: 980px )', 'min_width_768' => '@media only screen and ( min-width: 768px )', 'max_width_767' => '@media only screen and ( max-width: 767px )', 'max_width_479' => '@media only screen and ( max-width: 479px )', ); $media_queries['mobile'] = $media_queries['max_width_767']; $media_queries = apply_filters( 'et_builder_media_queries', $media_queries ); if ( 'for_js' === $for_js ) { $processed_queries = array(); foreach ( $media_queries as $key => $value ) { $processed_queries[] = array( $key, $value ); } } else { $processed_queries = $media_queries; } return $processed_queries; } /** * Set media queries key value pairs. */ public static function set_media_queries() { self::$media_queries = self::get_media_quries(); } /** * Return media query from the media query name. * E.g For max_width_767 media query name, this function return "@media only screen and ( max-width: 767px )". * * @param string $name Media query name e.g max_width_767, max_width_980. * * @return bool|mixed */ public static function get_media_query( $name ) { if ( ! isset( self::$media_queries[ $name ] ) ) { return false; } return self::$media_queries[ $name ]; } /** * Get style key. * * @return int|string */ public static function get_style_key() { if ( self::is_theme_builder_layout() || self::is_wp_editor_template() ) { return self::get_layout_id(); } // Use a generic key in all other cases. // For example, injector plugins that repeat a layout in a loop // need to group that CSS under the same key. return 'post'; } /** * Return style array from {@see self::$internal_modules_styles} or {@see self::$styles}. * * @param bool $internal Whether to return style from internal modules styles. * @param int $key Style key. * * @return array|mixed */ public static function get_style_array( $internal = false, $key = 0 ) { $styles = $internal ? self::$internal_modules_styles : self::$styles; if ( 0 === $key ) { $key = self::get_style_key(); } return isset( $styles[ $key ] ) ? $styles[ $key ] : array(); } /** * Intended to be used for unit testing * * @intendedForTesting */ public static function reset_styles() { self::$internal_modules_styles = array(); self::$styles = array(); self::$media_queries = array(); } /** * Get styles of the current page. * * @see set_advanced_styles() * * @param bool $internal Whether or not module's internal style. * @param int $key Style key. * * @return string */ public static function get_style( $internal = false, $key = 0, $critical = false ) { // use appropriate array depending on which styles we need. $styles_array = self::get_style_array( $internal, $key ); if ( empty( $styles_array ) ) { return ''; } global $et_user_fonts_queue; $output = ''; if ( ! empty( $et_user_fonts_queue ) ) { $output .= et_builder_enqueue_user_fonts( $et_user_fonts_queue ); } $styles_by_media_queries = $styles_array; $styles_count = (int) count( $styles_by_media_queries ); $media_queries_order = array_merge( array( 'general' ), array_values( self::$media_queries ) ); // make sure styles in the array ordered by media query correctly from bigger to smaller screensize. $styles_by_media_queries_sorted = array_merge( array_flip( $media_queries_order ), $styles_by_media_queries ); foreach ( $styles_by_media_queries_sorted as $media_query => $styles ) { // skip wrong values which were added during the array sorting. if ( ! is_array( $styles ) ) { continue; } $media_query_output = ''; $wrap_into_media_query = 'general' !== $media_query; // sort styles by priority. et_()->uasort( $styles, array( 'ET_Builder_Element', 'compare_by_priority' ) ); // merge styles with identical declarations. $merged_declarations = []; foreach ( $styles as $selector => $settings ) { if ( false === $critical && isset( $settings['critical'] ) ) { continue; } elseif ( true === $critical && empty( $settings['critical'] ) ) { continue; } $this_declaration = md5( $settings['declaration'] ); // we want to skip combining anything with psuedo selectors or keyframes. if ( false !== strpos( $selector, ':-' ) || false !== strpos( $selector, '@keyframes' ) ) { // set unique key so that it cant be matched. $unique_key = $this_declaration . '-' . uniqid(); $merged_declarations[ $unique_key ] = [ 'declaration' => $settings['declaration'], 'selector' => $selector, ]; if ( ! empty( $settings['priority'] ) ) { $merged_declarations[ $unique_key ]['priority'] = $settings['priority']; } continue; } if ( empty( $merged_declarations[ $this_declaration ] ) ) { $merged_declarations[ $this_declaration ] = [ 'selector' => '', 'priority' => '', ]; } $new_selector = ! empty( $merged_declarations[ $this_declaration ]['selector'] ) ? $merged_declarations[ $this_declaration ]['selector'] . ', ' . $selector : $selector; $merged_declarations[ $this_declaration ] = [ 'declaration' => $settings['declaration'], 'selector' => $new_selector, ]; if ( ! empty( $settings['priority'] ) ) { $merged_declarations[ $this_declaration ]['priority'] = $settings['priority']; } } // get each rule in a media query. foreach ( $merged_declarations as $settings ) { $media_query_output .= sprintf( '%3$s%4$s%1$s { %2$s }', $settings['selector'], $settings['declaration'], "\n", ( $wrap_into_media_query ? "\t" : '' ) ); } // All css rules that don't use media queries are assigned to the "general" key. // Wrap all non-general settings into media query. if ( $wrap_into_media_query && '' !== $media_query_output ) { $media_query_output = sprintf( '%3$s%3$s%1$s {%2$s%3$s}', $media_query, $media_query_output, "\n" ); } $output .= $media_query_output; } return $output; } /** * Generate video background markup for columns. * * @param array $args Background values. * @param array $conditional_tags Conditional tags. * @param array $current_page Current page info. * * @return bool|mixed */ public static function get_column_video_background( $args = array(), $conditional_tags = array(), $current_page = array() ) { if ( empty( $args ) ) { return false; } $formatted_args = array(); foreach ( $args as $key => $value ) { $key_length = strlen( $key ); $formatted_args[ substr( $key, 0, ( $key_length - 2 ) ) ] = $value; } return self::get_video_background( $formatted_args, $conditional_tags, $current_page ); } /** * Generate video background markup. * * @since 3.23 Add support for responsive settings. * * @param array $args Background values. * @param array $conditional_tags Conditional tags. * @param array $current_page Current page info. * @return mixed Mixed background content generated as video markup. */ public static function get_video_background( $args = array(), $conditional_tags = array(), $current_page = array() ) { $base_name = isset( $args['computed_variables'] ) && isset( $args['computed_variables']['base_name'] ) ? $args['computed_variables']['base_name'] : 'background'; $device = isset( $args['computed_variables'] ) && isset( $args['computed_variables']['device'] ) ? $args['computed_variables']['device'] : 'desktop'; $suffix = ! empty( $device ) && 'desktop' !== $device ? "_{$device}" : ''; $defaults = array( "{$base_name}_video_mp4{$suffix}" => '', "{$base_name}_video_webm{$suffix}" => '', "{$base_name}_video_width{$suffix}" => '', "{$base_name}_video_height{$suffix}" => '', ); $args = wp_parse_args( $args, $defaults ); if ( '' === $args[ "{$base_name}_video_mp4{$suffix}" ] && '' === $args[ "{$base_name}_video_webm{$suffix}" ] ) { return false; } return do_shortcode( sprintf( ' ', ( '' !== $args[ "{$base_name}_video_mp4{$suffix}" ] ? sprintf( '', esc_url( $args[ "{$base_name}_video_mp4{$suffix}" ] ) ) : '' ), ( '' !== $args[ "{$base_name}_video_webm{$suffix}" ] ? sprintf( '', esc_url( $args[ "{$base_name}_video_webm{$suffix}" ] ) ) : '' ), ( '' !== $args[ "{$base_name}_video_width{$suffix}" ] ? sprintf( ' width="%s"', esc_attr( intval( $args[ "{$base_name}_video_width{$suffix}" ] ) ) ) : '' ), ( '' !== $args[ "{$base_name}_video_height{$suffix}" ] ? sprintf( ' height="%s"', esc_attr( intval( $args[ "{$base_name}_video_height{$suffix}" ] ) ) ) : '' ) ) ); } /** * Clean the styles array {@see self::$internal_modules_styles}. * * @param bool $need_internal_styles Set the flag to make sure new styles will be saved to the correct place. */ public static function clean_internal_modules_styles( $need_internal_styles = true ) { // clean the styles array. self::$internal_modules_styles[ self::get_style_key() ] = array(); // set the flag to make sure new styles will be saved to the correct place. self::$prepare_internal_styles = $need_internal_styles; // generate unique number to make sure module classes will be unique if shortcode is generated via ajax. self::$internal_modules_counter = wp_rand( 10000, 99999 ); } /** * Set the field dependencies based on the `show_if` or `show_if_not` key from the * field. * * @param string $slug The module's slug. ie `et_pb_section`. * @param string $field_id The field id. id `background_color`. * @param array $field_info Associative array of the field's data. */ protected static function set_field_dependencies( $slug, $field_id, $field_info ) { // bail if the field_info is not an array. if ( ! is_array( $field_info ) || ! self::$_->array_get( $field_info, 'bb_support', true ) ) { return; } // otherwise we keep going. foreach ( array( 'show_if', 'show_if_not' ) as $dependency_type ) { if ( ! isset( $field_info[ $dependency_type ] ) ) { continue; } if ( ! self::$data_utils->is_assoc_array( $field_info[ $dependency_type ] ) ) { continue; } foreach ( $field_info[ $dependency_type ] as $dependency => $value ) { // dependency -> dependent (eg. et_pb_signup.provider.affects.first_name_field.show_if: mailchimp). $address = self::$_->esc_array( array( $slug, $dependency, 'affects', $field_id, $dependency_type ), 'esc_attr' ); self::$data_utils->array_set( self::$field_dependencies, $address, self::$_->esc_array( $value, 'esc_attr' ) ); // dependent -> dependency (eg. et_pb_signup.first_name_field.show_if.provider: mailchimp). $address = self::$_->esc_array( array( $slug, $field_id, $dependency_type, $dependency ), 'esc_attr' ); self::$data_utils->array_set( self::$field_dependencies, $address, self::$_->esc_array( $value, 'esc_attr' ) ); } } } /** * Get all modules fields dependencies. * * @param string $post_type Post type. * * @return array */ public static function get_field_dependencies( $post_type ) { if ( self::$field_dependencies ) { return self::$field_dependencies; } $all_modules = self::get_parent_and_child_modules( $post_type ); foreach ( $all_modules as $module_slug => $module ) { // Get all the fields. $all_fields = $module->sort_fields( $module->_get_fields() ); foreach ( $all_fields as $field_id => $field_info ) { if ( isset( $field_info['type'] ) && 'composite' === $field_info['type'] ) { foreach ( $field_info['composite_structure'] as $field ) { foreach ( $field['controls'] as $control => $data ) { self::set_field_dependencies( $module_slug, $control, $data ); } } } self::set_field_dependencies( $module_slug, $field_id, $field_info ); } } return self::$field_dependencies; } /** * Set module style. * * @param string $function_name Module slug. * @param array $style Style array. */ public static function set_style( $function_name, $style ) { $selectors = is_array( $style['selector'] ) ? $style['selector'] : array( $style['selector'] ); foreach ( $selectors as $item ) { foreach ( self::$_->sanitize_css_placeholders( $item ) as $selector ) { $selector = apply_filters( "{$function_name}_css_selector", $selector ); $selector = apply_filters( 'all_module_css_selector', $selector, $function_name ); self::_set_style( $function_name, array_merge( $style, array( 'selector' => $selector ) ) ); } } } /** * Check if the style processor allowed to be executed. * Currently, we only use a custom processor from the method inside `ET_Builder_Module_Helper_Style_Processor`, * * NOTE: If there are more processors introduced, this needs to be updated * * @since 4.6.0 * * @param array $processor Style processor. * * @return bool */ protected static function _is_style_processor_allowed( $processor ) { $allow_list = array( 'ET_Builder_Module_Helper_Style_Processor', ); return in_array( et_()->array_get( $processor, '0' ), $allow_list, true ); } /** * Generate responsive + hover + sticky style using the same configuration at once * { * * @type string $mode * @type string $render_slug * @type string $base_attr_name * @type array $attrs * @type string $css_property * @type string $selector * @type bool $is_sticky_module * @type bool|array $important Allowed value ​​is boolean or array of mode, e.g ['sticky', 'hover']. * @type string $additional_css * @type int $priority * @type bool $responsive * @type bool $hover * @type string $hover_selector * @type string $hover_pseudo_selector_location * @type bool $sticky * @type string $sticky_pseudo_selector_location * @type string $utility_arg * } * * NOTE: If there are more mode besides sticky and hover introduced, this needs to be updated. * * @since 4.6.0 * * @param array $args Function arguments. * * @return void */ public function generate_styles( $args = array() ) { $defaults = array( 'mode' => 'sticky', 'render_slug' => '', 'base_attr_name' => '', 'attrs' => $this->props, 'css_property' => '', 'selector' => '%%order_class%%', 'is_sticky_module' => $this->is_sticky_module, 'important' => false, 'additional_css' => '', 'type' => '', 'priority' => '', 'responsive' => true, 'hover' => true, 'hover_selector' => '', 'hover_pseudo_selector_location' => 'order_class', 'sticky' => true, 'sticky_pseudo_selector_location' => 'order_class', 'processor' => false, 'responsive_processor' => false, 'hover_processor' => false, 'sticky_processor' => false, 'processor_declaration_format' => '', 'utility_arg' => '', ); $args = wp_parse_args( $args, $defaults ); $attrs = $args['attrs']; $base_attr_name = $args['base_attr_name']; $selector = $args['selector']; // Responsive Options. if ( $args['responsive'] ) { $responsive = et_pb_responsive_options(); $responsive_values = $responsive->get_property_values( $attrs, $base_attr_name ); $responsive_processor = $args['responsive_processor']; // Custom processor fallback, if there's any. if ( ! $responsive_processor && $args['processor'] ) { $responsive_processor = $args['processor']; } if ( $responsive_processor && self::_is_style_processor_allowed( $responsive_processor ) ) { // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- Need to be able to use a custom processor, the callback function is checked in the _is_style_processor_allowed call_user_func( $responsive_processor, $selector, $responsive_values, $args, 'responsive' ); } else { // Append important tag to responsive's additional css. $responsive_additional_css = '; ' . $args['additional_css']; $responsive_important = is_array( $args['important'] ) ? in_array( 'responsive', $args['important'], true ) : $args['important']; if ( $responsive_important ) { $responsive_additional_css = ' !important;' . $args['additional_css']; } // Responsive Options. $responsive->generate_responsive_css( $responsive_values, $selector, $args['css_property'], $args['render_slug'], $responsive_additional_css, $args['type'], $args['priority'] ); } } // Hover Option. if ( $args['hover'] ) { $hover = et_pb_hover_options(); $hover_value = $hover->get_value( $base_attr_name, $attrs ); $hover_processor = $args['hover_processor']; $hover_important = is_array( $args['important'] ) ? in_array( 'hover', $args['important'], true ) : $args['important']; // Custom processor fallback, if there's any. if ( ! $hover_processor && $args['processor'] ) { $hover_processor = $args['processor']; } // Generate hover selector. if ( '' !== $args['hover_selector'] ) { $hover_selector = $args['hover_selector']; } elseif ( 'order_class' === $args['hover_pseudo_selector_location'] ) { $hover_selector = $hover->add_hover_to_order_class( $selector ); } else { $hover_selector = $hover->add_hover_to_selectors( $selector ); } if ( $hover_processor && self::_is_style_processor_allowed( $hover_processor ) ) { // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- Need to be able to use a custom processor, the callback function is checked in the _is_style_processor_allowed call_user_func( $hover_processor, $hover_selector, $hover_value, $args, 'hover' ); } elseif ( ! empty( $hover_value ) ) { $declaration = $this->generate_declaration( $args['css_property'], $hover_value, $hover_important, $args['additional_css'] ); $el_style = array( 'selector' => $hover_selector, 'declaration' => $declaration, ); self::set_style( $args['render_slug'], $el_style ); } } // Sticky Option. if ( $args['sticky'] ) { $sticky = et_pb_sticky_options(); $sticky_value = $sticky->get_value( $base_attr_name, $attrs ); $sticky_processor = $args['sticky_processor']; $sticky_important = is_array( $args['important'] ) ? in_array( 'sticky', $args['important'], true ) : $args['important']; // Custom processor fallback, if there's any. if ( ! $sticky_processor && $args['processor'] ) { $sticky_processor = $args['processor']; } // If generate_styles() is called multiple times, check for it once then pass // it down as param to skip sticky module check on this method level. $is_sticky_module = null === $args['is_sticky_module'] ? $sticky->is_sticky_module( $attrs ) : $args['is_sticky_module']; // Generate sticky selector. if ( 'order_class' === $args['sticky_pseudo_selector_location'] ) { $sticky_selector = $sticky->add_sticky_to_order_class( $selector, $is_sticky_module ); } else { $sticky_selector = $sticky->add_sticky_to_selectors( $selector, $is_sticky_module ); } if ( $sticky_processor && self::_is_style_processor_allowed( $sticky_processor ) ) { // @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- Need to be able to use a custom processor, the callback function is checked in the _is_style_processor_allowed call_user_func( $sticky_processor, $sticky_selector, $sticky_value, $args, 'sticky' ); } elseif ( ! empty( $sticky_value ) ) { $sticky_declaration = self::generate_declaration( $args['css_property'], $sticky_value, $sticky_important, $args['additional_css'] ); $el_style = array( 'selector' => $sticky_selector, 'declaration' => $sticky_declaration, ); self::set_style( $args['render_slug'], $el_style ); } } } /** * Generate CSS declaration. * * @since 4.6.0 * * @param array|string $css_property CSS Property. * @param string $value Value. * @param bool $important Use important tag. * @param string $additional_css Additional CSS. * * @return string */ public function generate_declaration( $css_property, $value = '', $important = false, $additional_css = '' ) { $important_tag = $important ? ' !important' : ''; $declaration = ''; // Assign value to one or more properties. if ( is_string( $css_property ) ) { $declaration = sprintf( '%1$s: %2$s%3$s;%4$s', esc_attr( $css_property ), esc_attr( $value ), esc_attr( $important_tag ), esc_attr( $additional_css ) ); } elseif ( is_array( $css_property ) ) { foreach ( $css_property as $property ) { $declaration .= sprintf( '%1$s: %2$s%3$s;%4$s', esc_attr( $property ), esc_attr( $value ), esc_attr( $important_tag ), esc_attr( $additional_css ) ); } } return $declaration; } /** * Applies the responsive and hover style for a specified option * * @since 3.25.3 * * @param string $option Setting option. * @param string $selector CSS Selector. * @param string $css_prop CSS property. */ public function generate_responsive_hover_style( $option, $selector, $css_prop ) { $responsive = et_pb_responsive_options(); $hover = et_pb_hover_options(); $values = $responsive->get_property_values( $this->props, $option ); $hover_value = $hover->get_value( $option, $this->props ); $responsive->generate_responsive_css( $values, $selector, $css_prop, $this->slug, '', 'color' ); if ( $hover_value ) { self::set_style( $this->slug, array( 'selector' => $hover->add_hover_to_selectors( $selector ), 'declaration' => "{$css_prop}: $hover_value;", ) ); } } /** * Set module style. * * @param string $function_name Module slug. * @param array $style Style array. */ protected static function _set_style( $function_name, $style ) { static $builder_post_types = null; static $allowed_post_types = null; $declaration = isset( $style['declaration'] ) && ! is_null( $style['declaration'] ) ? rtrim( $style['declaration'] ) : ''; if ( empty( $declaration ) ) { // Do not add empty declarations. return; } if ( null === $builder_post_types ) { $builder_post_types = et_builder_get_builder_post_types(); /** * Filters the Builder Post Types. * * @since 4.10.0 * * @param array $builder_post_types Builder Post Types. */ $allowed_post_types = apply_filters( 'et_builder_set_style_allowed_post_types', $builder_post_types ); } if ( $builder_post_types !== $allowed_post_types ) { $matches = array_intersect( $allowed_post_types, array_keys( self::$_module_slugs_by_post_type ) ); $allowed = false; foreach ( $matches as $post_type ) { if ( ! isset( self::$_module_slugs_by_post_type[ $post_type ] ) ) { continue; } if ( in_array( $function_name, self::$_module_slugs_by_post_type[ $post_type ], true ) ) { $allowed = true; break; } } if ( ! $allowed ) { return; } } global $et_pb_rendering_column_content; // do not process all the styles if FB enabled. Only those for modules without fb support and styles for the internal modules from Blog/Slider. $main_query_post = ET_Post_Stack::get_main_post(); $main_query_post_id = null !== $main_query_post ? $main_query_post->ID : 0; $editing_current_post = et_fb_is_enabled() && self::get_layout_id() === $main_query_post_id; if ( $editing_current_post && ! in_array( $function_name, self::get_fb_unsupported_modules(), true ) && ! $et_pb_rendering_column_content ) { return; } $order_class_name = self::get_module_order_class( $function_name ); $selector = str_replace( '%%order_class%%', ".{$order_class_name}", $style['selector'] ); $selector = str_replace( '%order_class%', ".{$order_class_name}", $selector ); // %%parent_class%% only works if child module's slug is `parent_slug` + _item suffix. If child module slug // use different slug structure, %%parent_class%% should not be used if ( false !== strpos( $selector, '%%parent_class%%' ) ) { $parent_class = str_replace( '_item', '', $function_name ); $selector = str_replace( '%%parent_class%%', ".{$parent_class}", $selector ); } $selector = et_builder_maybe_wrap_css_selectors( $selector, false, ".{$order_class_name}" ); $selector = wp_strip_all_tags( apply_filters( 'et_pb_set_style_selector', $selector, $function_name ) ); // New lines are saved as || in CSS Custom settings, remove them. $declaration = preg_replace( '/(\|\|)/i', '', $declaration ); $media_query = isset( $style['media_query'] ) ? $style['media_query'] : 'general'; $internal = $et_pb_rendering_column_content && self::$prepare_internal_styles; $style_key = self::get_style_key(); $styles = self::get_style_array( $internal ); // prepare styles for internal content. Used in Blog/Slider modules if they contain Divi modules. if ( isset( $styles[ $media_query ][ $selector ]['declaration'] ) ) { $styles[ $media_query ][ $selector ]['declaration'] = sprintf( '%1$s %2$s', $styles[ $media_query ][ $selector ]['declaration'], $declaration ); } else { $styles[ $media_query ][ $selector ]['declaration'] = $declaration; } if ( isset( $style['priority'] ) ) { $styles[ $media_query ][ $selector ]['priority'] = (int) $style['priority']; } $style = $styles[ $media_query ][ $selector ]; /** * Filters the Builder CSS * * @since 4.10.0 * * @param array $style Style Declaration. * @param string $selector Style Selector. */ $style = apply_filters( 'et_builder_set_style', $style, $selector ); $styles[ $media_query ][ $selector ] = $style; if ( $internal ) { self::$internal_modules_styles[ $style_key ] = $styles; } else { self::$styles[ $style_key ] = $styles; } } /** * Return module order class. * * @param string $function_name Module slug. * * @return bool|string */ public static function get_module_order_class( $function_name ) { global $et_pb_rendering_column_content, $et_pb_predefined_module_index; // determine whether we need to get the internal module class or regular. $get_inner_module_class = $et_pb_rendering_column_content; if ( $get_inner_module_class ) { if ( self::_get_index( array( self::INDEX_INNER_MODULE_ORDER, $function_name ) ) === -1 ) { return false; } } else { if ( self::_get_index( array( self::INDEX_MODULE_ORDER, $function_name ) ) === -1 ) { return false; } } if ( isset( $et_pb_predefined_module_index ) && $et_pb_predefined_module_index ) { $shortcode_order_num = $et_pb_predefined_module_index; } else { $shortcode_order_num = $get_inner_module_class ? self::_get_index( array( self::INDEX_INNER_MODULE_ORDER, $function_name ) ) : self::_get_index( array( self::INDEX_MODULE_ORDER, $function_name ) ); } $theme_builder_suffix = self::_get_theme_builder_order_class_suffix(); $wp_editor_suffix = self::_get_wp_editor_order_class_suffix(); // TB should be prioritized over WP Editor. Need to check WP Template editor suffix. if ( empty( $theme_builder_suffix ) && ! empty( $wp_editor_suffix ) ) { $theme_builder_suffix = $wp_editor_suffix; } $order_class_name = sprintf( '%1$s_%2$s%3$s', $function_name, $shortcode_order_num, $theme_builder_suffix ); return $order_class_name; } /** * Set module order class. * * @param string $function_name Module slug. */ public static function set_order_class( $function_name ) { global $et_pb_rendering_column_content; // determine whether we need to update the internal module class or regular. $process_inner_module_class = $et_pb_rendering_column_content; if ( $process_inner_module_class ) { $current_inner_index = self::_get_index( array( self::INDEX_INNER_MODULE_ORDER, $function_name ) ); self::_set_index( array( self::INDEX_INNER_MODULE_ORDER, $function_name ), $current_inner_index > -1 ? $current_inner_index + 1 : self::$internal_modules_counter ); } else { self::_set_index( array( self::INDEX_MODULE_ORDER, $function_name ), self::_get_index( array( self::INDEX_MODULE_ORDER, $function_name ) ) + 1 ); } } /** * Add a modal order class e.g et_pb_section_0, et_pb_section_1. * * @param string $module_class Module class e.g et_pb_section_. * @param string $function_name Module slug. * * @return string */ public static function add_module_order_class( $module_class, $function_name ) { $order_class_name = self::get_module_order_class( $function_name ); return "{$module_class} {$order_class_name}"; } /** * Generate video background markup. * * @since 3.23 Add support for responsive settings. * * @param array $args Background values. * @param string $base_name Background base name. * @return string Video background string value. */ public function video_background( $args = array(), $base_name = 'background' ) { $attr_prefix = "{$base_name}_"; $custom_prefix = 'background' === $base_name ? '' : "{$base_name}_"; $module_attrs = $this->props; // Default background class for each devices. $background_video_class = ''; $background_video_class_tablet = 'et_pb_section_video_bg_tablet'; $background_video_class_phone = 'et_pb_section_video_bg_phone'; $background_video_class_hover = 'et_pb_section_video_bg_hover'; // Hover and Responsive Status. $is_background_hover = et_pb_hover_options()->is_enabled( $base_name, $this->props ); $is_background_mobile = et_pb_responsive_options()->is_responsive_enabled( $this->props, $base_name ); if ( ! empty( $args ) ) { $background_video = self::get_video_background( $args ); $background_video_tablet = ''; $background_video_phone = ''; $background_video_hover = ''; $pause_outside_viewport = self::$_->array_get( $args, "{$attr_prefix}video_pause_outside_viewport", 'off' ); $allow_player_pause = self::$_->array_get( $args, "{$custom_prefix}allow_player_pause", 'off' ); } else { $background_videos = array(); // Desktop. $default_args = array( "{$attr_prefix}video_mp4" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_mp4" ), "{$attr_prefix}video_webm" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_webm" ), "{$attr_prefix}video_width" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_width", '', true ), "{$attr_prefix}video_height" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_height", '', true ), 'computed_variables' => array( 'base_name' => $base_name, ), ); // Collecting background videos. $background_videos['desktop'] = self::get_video_background( $default_args ); $module_attrs[ "video_{$base_name}_values" ] = $background_videos; // Get video and display status. $background_video_status = et_pb_responsive_options()->get_inheritance_background_value( $module_attrs, "video_{$base_name}_values", 'desktop', $base_name, $this->fields_unprocessed ); $background_video = self::$_->array_get( $background_video_status, 'video', '' ); $background_display = self::$_->array_get( $background_video_status, 'display', '' ); // Hover. $background_video_hover = ''; $background_display_hover = ''; if ( $is_background_hover ) { $hover_args = array( "{$attr_prefix}video_mp4__hover" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_mp4__hover" ), "{$attr_prefix}video_webm__hover" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_webm__hover" ), "{$attr_prefix}video_width__hover" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_width__hover", '', true ), "{$attr_prefix}video_height__hover" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_height__hover", '', true ), 'computed_variables' => array( 'base_name' => $base_name, 'device' => '_hover', ), ); // Collecting background videos. $background_videos['hover'] = self::get_video_background( $hover_args ); $module_attrs[ "video_{$base_name}_values" ] = $background_videos; // Get video and display status. $background_video_status_hover = et_pb_responsive_options()->get_inheritance_background_value( $module_attrs, "video_{$base_name}_values", 'hover', $base_name, $this->fields_unprocessed ); $background_video_hover = self::$_->array_get( $background_video_status_hover, 'video', '' ); $background_display_hover = self::$_->array_get( $background_video_status_hover, 'display', '' ); } // Tablet and Phone. $background_video_tablet = ''; $background_display_tablet = ''; $background_video_phone = ''; $background_display_phone = ''; if ( $is_background_mobile ) { $tablet_args = array( "{$attr_prefix}video_mp4_tablet" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_mp4_tablet" ), "{$attr_prefix}video_webm_tablet" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_webm_tablet" ), "{$attr_prefix}video_width_tablet" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_width_tablet", '', true ), "{$attr_prefix}video_height_tablet" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_height_tablet", '', true ), 'computed_variables' => array( 'base_name' => $base_name, 'device' => 'tablet', ), ); $phone_args = array( "{$attr_prefix}video_mp4_phone" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_mp4_phone" ), "{$attr_prefix}video_webm_phone" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_webm_phone" ), "{$attr_prefix}video_width_phone" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_width_phone", '', true ), "{$attr_prefix}video_height_phone" => et_pb_responsive_options()->get_any_value( $this->props, "{$attr_prefix}video_height_phone", '', true ), 'computed_variables' => array( 'base_name' => $base_name, 'device' => 'phone', ), ); // Collecting background videos. $background_videos['tablet'] = self::get_video_background( $tablet_args ); $background_videos['phone'] = self::get_video_background( $phone_args ); $module_attrs[ "video_{$base_name}_values" ] = $background_videos; // Get video and display status. $background_video_status_tablet = et_pb_responsive_options()->get_inheritance_background_value( $module_attrs, "video_{$base_name}_values", 'tablet', $base_name, $this->fields_unprocessed ); $background_video_tablet = self::$_->array_get( $background_video_status_tablet, 'video', '' ); $background_display_tablet = self::$_->array_get( $background_video_status_tablet, 'display', '' ); $background_video_status_phone = et_pb_responsive_options()->get_inheritance_background_value( $module_attrs, "video_{$base_name}_values", 'phone', $base_name, $this->fields_unprocessed ); $background_video_phone = self::$_->array_get( $background_video_status_phone, 'video', '' ); $background_display_phone = self::$_->array_get( $background_video_status_phone, 'display', '' ); } // Set background video and class. Inherit is used to avoid rendering the same video. if ( '' !== $background_display_phone ) { if ( 'hide' === $background_display_phone ) { $background_video_class = 'et_pb_section_video_bg_desktop_tablet'; $background_video_class_tablet = 'et_pb_section_video_bg_tablet_only'; } elseif ( 'inherit' === $background_display_phone ) { $background_video_phone = ''; } } if ( '' !== $background_display_tablet ) { if ( 'hide' === $background_display_tablet ) { $background_video_class = 'et_pb_section_video_bg_desktop_only'; } elseif ( 'inherit' === $background_display_tablet ) { $background_video_tablet = ''; } } if ( '' !== $background_display_hover ) { if ( 'inherit' === $background_display_hover ) { $background_video_class .= ' et_pb_section_video_bg_hover_inherit'; $background_video_hover = ''; } } } $video_background = ''; // Desktop. if ( $background_video ) { // Video on desktop properties. $pause_outside_viewport = self::$_->array_get( $this->props, "{$attr_prefix}video_pause_outside_viewport", '' ); $allow_player_pause = self::$_->array_get( $this->props, "{$custom_prefix}allow_player_pause", 'off' ); $video_background .= sprintf( ' %1$s ', $background_video, $background_video_class, ( 'on' === $allow_player_pause ? ' et_pb_allow_player_pause' : '' ), ( 'off' === $pause_outside_viewport ? ' et_pb_video_play_outside_viewport' : '' ) ); } // Hover. if ( $is_background_hover ) { if ( $background_video_hover ) { // Video on hover properties. $pause_outside_viewport_hover = self::$_->array_get( $this->props, "{$attr_prefix}video_pause_outside_viewport__hover", '' ); $allow_player_pause_hover = self::$_->array_get( $this->props, "{$custom_prefix}allow_player_pause__hover", 'off' ); $video_background .= sprintf( ' %1$s ', $background_video_hover, $background_video_class_hover, ( 'on' === $allow_player_pause_hover ? ' et_pb_allow_player_pause' : '' ), ( 'off' === $pause_outside_viewport_hover ? ' et_pb_video_play_outside_viewport' : '' ) ); } $this->add_classname( 'et_pb_section_video_on_hover' ); } // Tablet. if ( $background_video_tablet && $is_background_mobile ) { // Video on tablet properties. $pause_outside_viewport_tablet = self::$_->array_get( $this->props, "{$attr_prefix}video_pause_outside_viewport_tablet", '' ); $allow_player_pause_tablet = self::$_->array_get( $this->props, "{$custom_prefix}allow_player_pause_tablet", 'off' ); $video_background .= sprintf( ' %1$s ', $background_video_tablet, $background_video_class_tablet, ( 'on' === $allow_player_pause_tablet ? ' et_pb_allow_player_pause' : '' ), ( 'off' === $pause_outside_viewport_tablet ? ' et_pb_video_play_outside_viewport' : '' ) ); } // Phone. if ( $background_video_phone && $is_background_mobile ) { // Video on phone properties. $pause_outside_viewport_phone = self::$_->array_get( $this->props, "{$attr_prefix}video_pause_outside_viewport_phone", '' ); $allow_player_pause_phone = self::$_->array_get( $this->props, "{$custom_prefix}allow_player_pause_phone", 'off' ); $video_background .= sprintf( ' %1$s ', $background_video_phone, $background_video_class_phone, ( 'on' === $allow_player_pause_phone ? ' et_pb_allow_player_pause' : '' ), ( 'off' === $pause_outside_viewport_phone ? ' et_pb_video_play_outside_viewport' : '' ) ); } // Added classname for module wrapper. if ( '' !== $video_background ) { wp_enqueue_style( 'wp-mediaelement' ); wp_enqueue_script( 'wp-mediaelement' ); $this->add_classname( array( 'et_pb_section_video', 'et_pb_preload' ) ); } return $video_background; } /** * Generate parallax image background markup. * * @param string $base_name Background base name. * @param array $props Props (optional). * * @since 4.15.0 Added $props property. * * @return string */ public function get_parallax_image_background( $base_name = 'background', $props = array() ) { $props = empty( $props ) ? $this->props : $props; $attr_prefix = "{$base_name}_"; $custom_prefix = 'background' === $base_name ? '' : "{$base_name}_"; $parallax_processed = array(); $parallax_background = ''; $hover_suffix = et_pb_hover_options()->get_suffix(); $sticky_suffix = et_pb_sticky_options()->get_suffix(); $preview_modes = array( $hover_suffix, $sticky_suffix, '_phone', '_tablet', '' ); // Featured Image as Background. $featured_image = ''; $featured_placement = ''; $featured_image_src = ''; if ( $this->featured_image_background ) { $featured_image = self::$_->array_get( $props, 'featured_image', '' ); $featured_placement = self::$_->array_get( $props, 'featured_placement', '' ); $featured_image_src_obj = wp_get_attachment_image_src( get_post_thumbnail_id( self::_get_main_post_id() ), 'full' ); $featured_image_src = isset( $featured_image_src_obj[0] ) ? $featured_image_src_obj[0] : ''; } // Parallax Gradient. $background_options = et_pb_background_options(); $is_gradient_on = false; foreach ( $preview_modes as $suffix ) { $is_hover = $hover_suffix === $suffix; $is_sticky = $sticky_suffix === $suffix; // A. Bail early if hover or responsive settings disabled on mobile/hover. if ( '' !== $suffix ) { // Ensure responsive settings is enabled on mobile. if ( ! $is_hover && ! $is_sticky && ! et_pb_responsive_options()->is_responsive_enabled( $props, $base_name ) ) { continue; } // Ensure hover settings is enabled. if ( $is_hover && ! et_pb_hover_options()->is_enabled( $base_name, $props ) ) { continue; } // Ensure sticky setting is enabled. if ( $is_sticky && ! et_pb_sticky_options()->is_enabled( $base_name, $props ) ) { continue; } } // Prepare preview mode. $mode = '' !== $suffix ? str_replace( '_', '', $suffix ) : 'desktop'; $mode = $is_hover ? 'hover' : $mode; $mode = $is_sticky ? 'sticky' : $mode; // B.1. Get inherited background value. $use_gradient_options = et_pb_responsive_options()->get_inheritance_background_value( $props, "use_{$base_name}_color_gradient", $mode, $base_name, $this->fields_unprocessed ); $background_image = et_pb_responsive_options()->get_inheritance_background_value( $props, "{$attr_prefix}image", $mode, $base_name, $this->fields_unprocessed ); $parallax = et_pb_responsive_options()->get_any_value( $props, "parallax{$suffix}", '', true ); $parallax_method = et_pb_responsive_options()->get_any_value( $props, "parallax_method{$suffix}", '', true ); $gradient_overlays_image = et_pb_responsive_options()->get_any_value( $props, "{$base_name}_color_gradient_overlays_image{$suffix}", '', true ); $background_gradient_blend = et_pb_responsive_options()->get_any_value( $props, "{$base_name}_blend{$suffix}", 'normal', true ); // B.2. Set default value for parallax and parallax method on hover when they are empty. if ( $is_hover || $is_sticky ) { $parallax = et_pb_hover_options()->get_raw_value( "{$custom_prefix}parallax", $props, $parallax ); $parallax_method = et_pb_hover_options()->get_raw_value( "{$custom_prefix}parallax_method", $props, $parallax_method ); $gradient_overlays_image = et_pb_hover_options()->get_raw_value( "{$base_name}_color_gradient_overlays_image", $props, $gradient_overlays_image ); $background_gradient_blend = et_pb_hover_options()->get_raw_value( "{$base_name}_blend", $props, $background_gradient_blend ); $gradient_properties_desktop = $background_options->get_gradient_properties( $props, $base_name, '' ); $gradient_properties = $background_options->get_gradient_mode_properties( $mode, $props, $base_name, $gradient_properties_desktop ); } else { $gradient_properties = $background_options->get_gradient_properties( $props, $base_name, $suffix ); } $background_gradient_style = $background_options->get_gradient_style( $gradient_properties ); if ( 'on' === $use_gradient_options && 'on' === $gradient_overlays_image && 'on' === $parallax ) { $is_gradient_on = '' !== $background_gradient_style; } // B.3. Override background image with featured image if needed. if ( 'on' === $featured_image && 'background' === $featured_placement && '' !== $featured_image_src ) { $background_image = $featured_image_src; } // C.1. Parallax BG Class to inform if other modes exist. $parallax_classname = array(); $parallax_gradient_classname = array(); if ( ( '_tablet' === $suffix || '' === $suffix ) && in_array( '_phone', $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_phone_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_phone_exist'; } if ( '' === $suffix && in_array( '_tablet', $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_tablet_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_tablet_exist'; } if ( in_array( $hover_suffix, $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_hover_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_hover_exist'; } if ( in_array( $sticky_suffix, $parallax_processed, true ) ) { $parallax_classname[] = 'et_parallax_bg_sticky_exist'; $parallax_gradient_classname[] = 'et_parallax_gradient_sticky_exist'; } // C.2. Set up parallax class and wrapper. if ( '' !== $background_image && 'on' === $parallax ) { $parallax_classname[] = 'et_parallax_bg'; $parallax_gradient_classname[] = 'et_parallax_gradient'; if ( 'off' === $parallax_method ) { $parallax_classname[] = 'et_pb_parallax_css'; $parallax_gradient_classname[] = 'et_pb_parallax_css'; if ( isset( $props['inner_shadow'] ) && 'off' !== $props['inner_shadow'] ) { $parallax_classname[] = 'et_pb_inner_shadow'; } } // Parallax BG Class with suffix. if ( '' !== $suffix ) { $parallax_classname[] = "et_parallax_bg{$suffix}"; $parallax_gradient_classname[] = "et_parallax_gradient{$suffix}"; } $background_gradient_image = sprintf( 'background-image: %1$s;', esc_html( $background_gradient_style ) ); $background_gradient_blend = 'normal' !== $background_gradient_blend ? sprintf( 'mix-blend-mode: %1$s;', esc_html( $background_gradient_blend ) ) : ''; $parallax_gradient = sprintf( '', esc_attr( implode( ' ', $parallax_gradient_classname ) ), // #1 et_core_esc_previously( $background_gradient_image ), // #2 et_core_esc_previously( $background_gradient_blend ) // #3 ); $parallax_background .= sprintf( '%3$s', esc_attr( implode( ' ', $parallax_classname ) ), esc_url( $background_image ), $is_gradient_on ? et_core_esc_previously( $parallax_gradient ) : '' ); // Cleanup. $background_gradient_image = null; $background_gradient_blend = null; $parallax_gradient = null; // set `.et_parallax_bg_wrap` border-radius. et_set_parallax_bg_wrap_border_radius( $props, $this->slug, $this->main_css_element ); } // C.3. Hover parallax class. if ( '' !== $background_image && $is_hover ) { $this->add_classname( 'et_pb_section_parallax_hover' ); } // C.4. Sticky parallax class. if ( '' !== $background_image && $is_sticky ) { $this->add_classname( 'et_pb_section_parallax_sticky' ); } array_push( $parallax_processed, $suffix ); } // Added classname for module wrapper. if ( '' !== $parallax_background ) { $this->add_classname( 'et_pb_section_parallax' ); } return $parallax_background; } /** * Check if filters are used in a module. * * @since 4.10.0 * @return bool Use of filter. */ public function are_filters_used() { $filter_keys = array( 'filter_hue_rotate' => '0deg', 'filter_saturate' => '100%', 'filter_brightness' => '100%', 'filter_contrast' => '100%', 'filter_invert' => '0%', 'filter_sepia' => '0%', 'filter_opacity' => '100%', 'filter_blur' => '0px', 'mix_blend_mode' => 'normal', ); foreach ( $this->props as $attr => $value ) { if ( ! $value ) { continue; } foreach ( array_keys( $filter_keys ) as $filter_key ) { $is_attr = false !== strpos( $attr, $filter_key ); $default_value = $filter_keys[ $filter_key ]; if ( $is_attr && $value !== $default_value ) { return true; } } } return false; } /** * Generate CSS Filters * Check our shortcode arguments for CSS `filter` properties. If found, set the style rules for this block. (This * function reads options set by the 'Filters' and 'Image Filters' builder menu fields.) * * @since 3.23 Add responsive setting styling processing here. * @since 4.6.0 Add sticky style support. * * @param string $function_name Builder module's function name (keeps the CSS rules straight). * @param string $prefix Optional string prepended to the field name (i.e., `filter_saturate` -> `child_filter_saturate`). * @param mixed $selectors Array or string containing all target DOM element(s), ID(s), and/or class(es). * * @return string Any additional CSS classes (added if filters were applied). */ public function generate_css_filters( $function_name = '', $prefix = '', $selectors = array( '%%order_class%%' ) ) { if ( '' === $function_name ) { ET_Core_Logger::error( '$function_name is required.' ); return; } // If `$selectors` is a string, convert to an array before we continue. $selectors_prepared = $selectors; if ( ! is_array( $selectors ) ) { $selectors_prepared = explode( ',', et_core_intentionally_unescaped( $selectors, 'fixed_string' ) ); } $responsive_selectors = $selectors_prepared; $additional_classes = ''; // If we don't have a target selector, get out now. if ( ! $selectors_prepared ) { return $additional_classes; } $hover_suffix = et_pb_hover_options()->get_suffix(); $sticky = et_pb_sticky_options(); $sticky_suffix = $sticky->get_suffix(); $field_suffixes = array( '', 'tablet', 'phone', $hover_suffix, $sticky_suffix ); $filters_default = array(); $filters_default_fb = array(); $filters_hover = array(); $hover_selectors = array(); $filter_keys = array( 'hue_rotate', 'saturate', 'brightness', 'contrast', 'invert', 'sepia', 'opacity', 'blur', ); foreach ( $field_suffixes as $suffix ) { $sticky_mode = $sticky_suffix === $suffix; if ( $hover_suffix === $suffix ) { $selectors_prepared = array_map( array( $this, 'add_hover_to_selectors' ), $selectors_prepared ); } if ( $sticky_mode ) { $selectors_prepared = $sticky->add_sticky_to_selectors( $selectors, $this->is_sticky_module, false ); } // Mobile parameters. Update suffix and add media query argument for styles declaration. $device_suffix = ''; $media_query = array(); $is_mobile = in_array( $suffix, array( 'tablet', 'phone' ), true ); if ( $is_mobile ) { $breakpoint = 'tablet' === $suffix ? 'max_width_980' : 'max_width_767'; $media_query = array( 'media_query' => self::get_media_query( $breakpoint ) ); // For mobile, we need to reset $suffix and use $devie_suffix instead. Later on with // empty suffix, the filter will only return desktop value and will be used as default // and will be merged with filter mobile values. $device_suffix = "_{$suffix}"; $suffix = ''; } // Some web browser glitches with filters and blend modes can be improved this way // see https://bugs.chromium.org/p/chromium/issues/detail?id=157218 for more info. $backface_visibility = 'backface-visibility:hidden;'; $backface_visibility_added = array(); $additional_classes = ''; // Blend Mode. $mix_blend_mode = self::$data_utils->array_get( $this->props, "{$prefix}mix_blend_mode", '' ); // Filters. $filter = array(); $filter_names = array(); // Assign filter values and names. foreach ( $filter_keys as $filter_key ) { $filter_name = "{$prefix}filter_{$filter_key}"; $filter_names[] = $filter_name; $filter[ $filter_key ] = self::$data_utils->array_get( $this->props, "{$filter_name}{$suffix}", '' ); } $is_any_filter_responsive = et_pb_responsive_options()->is_any_responsive_enabled( $this->props, $filter_names ); $is_any_filter_hover_enabled = et_pb_hover_options()->is_any_hover_enabled( $this->props, $filter_names ); // For mobile, it should return any value exist if current device value is empty. if ( $is_mobile ) { // Blend Mode. $is_blend_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$prefix}mix_blend_mode" ); $mix_blend_mode = $is_blend_responsive ? et_pb_responsive_options()->get_any_value( $this->props, "{$prefix}mix_blend_mode{$suffix}", '', true ) : ''; // Filters. $filters_mobile = array(); foreach ( $filter as $filter_key => $filter_value ) { if ( ! et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$prefix}filter_{$filter_key}" ) ) { continue; } $filters_mobile[ $filter_key ] = et_pb_responsive_options()->get_any_value( $this->props, "{$prefix}filter_{$filter_key}{$device_suffix}", '', true ); } // If any responsive settings active on filter settings, set desktop value as default. if ( $is_any_filter_responsive ) { $filters_mobile = array_merge( $filter, $filters_mobile ); } // Replace current filter values with mobile filter values. $filter = $filters_mobile; } // Remove any filters with null or default values. $filter = array_filter( $filter, 'strlen' ); // Optional: CSS `mix-blend-mode` rule. $mix_blend_mode_default = ET_Global_Settings::get_value( 'all_mix_blend_mode', 'default' ); if ( $mix_blend_mode && $mix_blend_mode !== $mix_blend_mode_default ) { if ( ! $sticky_mode ) { foreach ( $selectors_prepared as $selector ) { $el_style = array_merge( array( 'selector' => $selector, 'declaration' => sprintf( 'mix-blend-mode: %1$s;', esc_html( $mix_blend_mode ) ) . $backface_visibility, ), $media_query ); self::set_style( $function_name, $el_style ); $backface_visibility_added[] = $selector; } } $additional_classes .= ' et_pb_css_mix_blend_mode'; } elseif ( 'et_pb_column' === $function_name ) { // Columns need to pass through. $additional_classes .= ' et_pb_css_mix_blend_mode_passthrough'; } // Optional: CSS `filter` rule. if ( empty( $filter ) ) { // Since we added responsive settings, the process should not be stopped here. // It should continue until tablet and phone are checked completely. Replace // return with continue. continue; } $css_value = array(); $css_value_fb_hover = array(); foreach ( $filter as $label => $value ) { // Check against our default settings, and only append the rule if it differs // (only for default state since hover and mobile might be equal to default, // ie. no filter on hover only). if ( ET_Global_Settings::get_value( 'all_filter_' . $label, 'default' ) === $value && $hover_suffix !== $suffix && ! $sticky_mode && ! $is_mobile && ! ( $is_any_filter_responsive && $is_any_filter_hover_enabled ) ) { continue; } // Don't apply hover filter if it is not enabled. if ( $hover_suffix === $suffix && ! et_pb_hover_options()->is_enabled( "{$prefix}filter_{$label}{$suffix}", $this->props ) ) { continue; } // Don't apply sticky filter if it is not enabled. if ( $sticky_mode && ! $sticky->is_enabled( "{$prefix}filter_{$label}{$suffix}", $this->props ) ) { continue; } $value = et_sanitize_input_unit( $value, false, 'deg' ); $label_css_format = str_replace( '_', '-', $label ); // Construct string of all CSS Filter values. $css_value[ $label ] = esc_html( "${label_css_format}(${value})" ); // Construct Visual Builder hover rules. if ( ! in_array( $label, array( 'opacity', 'blur' ), true ) ) { // Skip those, because they mess with VB controls. $css_value_fb_hover[ $label ] = esc_html( "${label_css_format}(${value})" ); } } // Append our new CSS rules. if ( $css_value ) { // Store the default (non-hover) filters. if ( '' === $suffix ) { $filters_default = $css_value; } // Merge the hover filters onto the default filters so that filters that // have no hover option set are not removed from the CSS declaration. if ( $hover_suffix === $suffix ) { $css_value = array_merge( $filters_default, $css_value ); $filters_hover = $css_value; } foreach ( $selectors_prepared as $selector ) { $backface_visibility_declaration = in_array( $selector, $backface_visibility_added, true ) ? '' : $backface_visibility; // Allow custom child filter target hover selector. if ( $hover_suffix === $suffix ) { if ( 'child_' === $prefix ) { $selector = self::$_->array_get( $this->advanced_fields, 'filters.child_filters_target.css.hover', $selector ); } $hover_selectors[] = $selector; } $el_style = array_merge( array( 'selector' => $selector, 'declaration' => sprintf( 'filter: %1$s;', implode( ' ', $css_value ) ) . $backface_visibility_declaration, ), $media_query ); self::set_style( $function_name, $el_style ); } // Add css for hover styles in sticky state. if ( $sticky_mode && ! empty( $filters_hover ) && ! empty( $hover_selectors ) ) { $sticky_hover_css_value = implode( ' ', $filters_hover ); foreach ( $hover_selectors as $hover_selector ) { $sticky_hover_selector = $sticky->add_sticky_to_order_class( $hover_selector, $this->is_sticky_module ); $el_style = array_merge( array( 'selector' => $sticky_hover_selector, 'declaration' => sprintf( 'filter: %1$s;', $sticky_hover_css_value ) . $backface_visibility_declaration, ), $media_query ); self::set_style( $function_name, $el_style ); } } $additional_classes .= ' et_pb_css_filters'; } // If we have VB hover-friendly CSS rules, we'll gather those and append them here. if ( $css_value_fb_hover ) { // Store the default (non-hover) filters. if ( '' === $suffix ) { $filters_default_fb = $css_value_fb_hover; } // Merge the hover filters onto the default filters so that filters that // have no hover option set are not removed from the CSS declaration. if ( $hover_suffix === $suffix ) { $css_value_fb_hover = array_merge( $filters_default_fb, $css_value_fb_hover ); } if ( ! $sticky_mode ) { foreach ( $selectors_prepared as $selector ) { $selector_hover = str_replace( '%%order_class%%', 'html:not(.et_fb_edit_enabled) #et-fb-app %%order_class%%:hover', $selector ); $el_style = array( 'selector' => $selector_hover, 'declaration' => esc_html( sprintf( 'filter: %1$s;', implode( ' ', $css_value_fb_hover ) ) ), ); self::set_style( $function_name, $el_style ); } } $additional_classes .= ' et_pb_css_filters_hover'; } } return $additional_classes; } /** * Convert classes array to a string. Also removes any duplicate classes * * @param array $classes A list of CSS classnames. * * @return array */ public function stringify_css_filter_classes( $classes ) { // Remove repeating classes. $classes = array_unique( $classes ); // Transform classes to a string. $classes = ' ' . implode( ' ', $classes ); return $classes; } /** * Adds a suffix at the end of the selector * E.g: add_suffix_to_selectors(':hover', '%%order_class%%% .image') >>> '%%order_class%%% .image:hover' * * @param string $suffix e.g ':hover'. * @param string $selector CSS selector. * * @return string */ public function add_suffix_to_selectors( $suffix, $selector ) { $selectors = explode( ',', $selector ); $selectors = array_map( 'trim', $selectors ); foreach ( $selectors as &$selector ) { $selector .= $suffix; } return implode( ', ', $selectors ); } /** * Adds `:hover` in selector at the end of the selector * E.g: add_hover_to_selectors('%%order_class%%% .image') >>> '%%order_class%%% .image:hover' * * @param string $selector CSS selector. * * @return string * * @deprecated Use et_pb_hover_options()->add_hover_to_selectors( $selector ); */ public function add_hover_to_selectors( $selector ) { return et_pb_hover_options()->add_hover_to_selectors( $selector ); } /** * Adds `:hover` in selector at the end of the selector if $add_hover is true * otherwise returns the original selector * * @param string $selector CSS selector. * @param bool $add_hover Whether to add hover on selector. * * @return string */ protected function _maybe_add_hover_to_selectors( $selector, $add_hover = false ) { return $add_hover ? et_pb_hover_options()->add_hover_to_selectors( $selector ) : $selector; } /** * Adds `:hover` in selector after `%%order_class%%` * E.g: add_hover_to_order_class('%%order_class%%% .image') >>> '%%order_class%%%:hover .image' * * @param string $selector CSS selector. * * @return string * * @deprecated Use et_pb_hover_options()->add_hover_to_order_class( $selector ); */ public function add_hover_to_order_class( $selector ) { return et_pb_hover_options()->add_hover_to_order_class( $selector ); } /** * Adds `:hover` to order class only if is specified, in other cse returns original selector * otherwise returns the original selector * * @param string $selector CSS selector. * @param bool $add_hover Whether to add hover on selector. * * @return string */ protected function _maybe_add_hover_to_order_class( $selector, $add_hover = false ) { return $add_hover ? et_pb_hover_options()->add_hover_to_order_class( $selector ) : $selector; } /** * Convert smart quotes and & entity to their applicable characters * * @param string $text Input text. * * @return string */ public static function convert_smart_quotes_and_amp( $text ) { $smart_quotes = array( '“', '”', '″', '‘', '’', ''', '&', ); $replacements = array( '"', '"', '"', ''', ''', ''', '&', ); if ( 'fr_FR' === get_locale() ) { $french_smart_quotes = array( ' »', '″>', ); $french_replacements = array( '"', '">', ); $smart_quotes = array_merge( $smart_quotes, $french_smart_quotes ); $replacements = array_merge( $replacements, $french_replacements ); } $text = str_replace( $smart_quotes, $replacements, $text ); return $text; } /** * Process multiple checkbox field value. * * @param array $value_map Checkbox value map. * @param string $value Checkbox value. * * @return string */ public function process_multiple_checkboxes_field_value( $value_map, $value ) { $result = array(); $index = 0; foreach ( explode( '|', $value ) as $checkbox_value ) { if ( 'on' === $checkbox_value ) { $result[] = $value_map[ $index ]; } $index++; } return implode( '|', $result ); } /** * Adds one or more CSS classes to the module on the frontend. * * @since 3.1 * * @param string|array $to_add classname(s) to be added. * @param number|bool $position position of added classname (0-based). Some class need to be placed * at exact position. i.e. .et_pb_column_{$type} on column inner. */ public function add_classname( $to_add, $position = false ) { if ( empty( $to_add ) ) { return; } $classname = is_array( $to_add ) ? $to_add : array( $to_add ); if ( is_numeric( $position ) ) { array_splice( $this->classname, intval( $position ), 0, $classname ); } else { $this->classname = array_merge( $this->classname, $classname ); } } /** * Removes one ore more CSS classes to the module on the frontend * * @since 3.1 * * @param string|array $to_remove classname(s) to be removed. */ public function remove_classname( $to_remove ) { $this->classname = array_filter( $this->classname ); if ( is_string( $to_remove ) && '' !== $to_remove ) { $this->classname = array_diff( $this->classname, array( $to_remove ) ); } elseif ( is_array( $to_remove ) ) { $to_remove = array_filter( $to_remove ); $this->classname = array_diff( $this->classname, $to_remove ); } } /** * Outputs module class * * @param string $function_name Module slug. * * @since 3.1 * * @return string escaped class */ public function module_classname( $function_name = '' ) { if ( ! in_array( $function_name, self::$uses_module_classname, true ) ) { // Add module slug to array of modules where `module_classname()` used. self::$uses_module_classname[] = $function_name; } $module_name = str_replace( 'et_pb_', '', $this->slug ); /** * Filters module classes. * * @since 3.1 * * @param array $classname Array of classnames. * @param int $render_count Number of times render function has been executed */ $classname = (array) array_unique( apply_filters( "et_builder_{$module_name}_classes", $this->classname, $this->render_count() ) ); return implode( ' ', array_map( 'esc_attr', $classname ) ); } /** * Outputs module id * * @since 3.1 * * @param bool $include_attribute wrap module id with id attribute name or not (to be used directly on module div). * * @return string module id / module id wrapped by id attribute */ public function module_id( $include_attribute = true ) { $module_id = esc_attr( $this->props['module_id'] ); $output = $include_attribute ? sprintf( ' id="%1$s"', $module_id ) : $module_id; return '' !== $module_id ? $output : ''; } /** * Helper method for rendering button markup which works compatible with advanced options' button * * @since 3.1 * * @param array $args button settings. * * @return string rendered button HTML */ public function render_button( $args = array() ) { // Prepare arguments. $defaults = array( 'button_id' => '', 'button_classname' => array(), 'button_custom' => '', 'button_rel' => '', 'button_text' => '', 'button_text_escaped' => false, 'button_url' => '', 'custom_icon' => '', 'custom_icon_tablet' => '', 'custom_icon_phone' => '', 'display_button' => true, 'has_wrapper' => true, 'url_new_window' => '', 'multi_view_data' => '', ); $args = wp_parse_args( $args, $defaults ); // Do not proceed if display_button argument is false. if ( ! $args['display_button'] ) { return ''; } $button_text = $args['button_text_escaped'] ? $args['button_text'] : esc_html( $args['button_text'] ); // Do not proceed if button_text argument is empty and not having multi view value. if ( '' === $button_text && ! $args['multi_view_data'] ) { return ''; } // Button classname. $button_classname = array( 'et_pb_button' ); // Add multi view CSS hidden helper class when button text is empty on desktop mode. if ( '' === $button_text && $args['multi_view_data'] ) { $button_classname[] = 'et_multi_view_hidden'; } if ( ! empty( $args['button_classname'] ) ) { $button_classname = array_merge( $button_classname, $args['button_classname'] ); } // Custom icon data attribute. $use_data_icon = '' !== $args['custom_icon'] && 'on' === $args['button_custom']; if ( $use_data_icon && et_pb_maybe_extended_icon( $args['custom_icon'] ) ) { $args['custom_icon'] = esc_attr( et_pb_get_extended_font_icon_value( $args['custom_icon'] ) ); } $data_icon = $use_data_icon ? sprintf( ' data-icon="%1$s"', esc_attr( et_pb_process_font_icon( $args['custom_icon'] ) ) ) : ''; $use_data_icon_tablet = '' !== $args['custom_icon_tablet'] && 'on' === $args['button_custom']; if ( $use_data_icon_tablet && et_pb_maybe_extended_icon( $args['custom_icon_tablet'] ) ) { $args['custom_icon_tablet'] = esc_attr( et_pb_get_extended_font_icon_value( $args['custom_icon_tablet'] ) ); } $data_icon_tablet = $use_data_icon_tablet ? sprintf( ' data-icon-tablet="%1$s"', esc_attr( et_pb_process_font_icon( $args['custom_icon_tablet'] ) ) ) : ''; $use_data_icon_phone = '' !== $args['custom_icon_phone'] && 'on' === $args['button_custom']; if ( $use_data_icon_phone && et_pb_maybe_extended_icon( $args['custom_icon_phone'] ) ) { $args['custom_icon_phone'] = esc_attr( et_pb_get_extended_font_icon_value( $args['custom_icon_phone'] ) ); } $data_icon_phone = $use_data_icon_phone ? sprintf( ' data-icon-phone="%1$s"', esc_attr( et_pb_process_font_icon( $args['custom_icon_phone'] ) ) ) : ''; // Render button. return sprintf( '%7$s%2$s%8$s', esc_url( $args['button_url'] ), et_core_esc_previously( $button_text ), ( 'on' === $args['url_new_window'] ? ' target="_blank"' : '' ), et_core_esc_previously( $data_icon ), esc_attr( implode( ' ', array_unique( $button_classname ) ) ), // #5 et_core_esc_previously( $this->get_rel_attributes( $args['button_rel'] ) ), $args['has_wrapper'] ? '
' : '', $args['has_wrapper'] ? '
' : '', '' !== $args['button_id'] ? sprintf( ' id="%1$s"', esc_attr( $args['button_id'] ) ) : '', et_core_esc_previously( $data_icon_tablet ), // #10 et_core_esc_previously( $data_icon_phone ), et_core_esc_previously( $args['multi_view_data'] ) ); } /** * Determine builder module is saving cache. * * @return mixed|void */ public static function is_saving_cache() { return apply_filters( 'et_builder_modules_is_saving_cache', false ); } /** * Get array of attributes which have dynamic content enabled. * * @since 3.17.2 * * @param mixed[] $attrs Module attributes. * * @return string[] */ protected function _get_enabled_dynamic_attributes( $attrs ) { $enabled_dynamic_attributes = isset( $attrs['_dynamic_attributes'] ) ? $attrs['_dynamic_attributes'] : ''; $enabled_dynamic_attributes = array_filter( explode( ',', $enabled_dynamic_attributes ) ); return $enabled_dynamic_attributes; } /** * Check if an attribute value is dynamic or not. * * @since 3.17.2 * * @param string $attribute Attribute name. * @param string $value Attribute value. * @param array $enabled_dynamic_attributes Attributes which have dynamic content enabled. * * @return bool */ protected function _is_dynamic_value( $attribute, $value, $enabled_dynamic_attributes ) { if ( ! in_array( $attribute, $enabled_dynamic_attributes, true ) ) { return false; } return et_builder_parse_dynamic_content( $value )->is_dynamic(); } /** * Re-encode legacy dynamic content values in an attrs array. * * @since 3.20.2 * * @param string[] $attrs Module attributes. * @param string[] $enabled_dynamic_attributes Attributes which have dynamic content enabled. * * @return string[] */ protected function _encode_legacy_dynamic_content( $attrs, $enabled_dynamic_attributes ) { if ( is_array( $attrs ) ) { foreach ( $attrs as $field => $value ) { $attrs[ $field ] = $this->_encode_legacy_dynamic_content_value( $field, $value, $enabled_dynamic_attributes ); } } return $attrs; } /** * Re-encode legacy dynamic content value. * * @since 3.20.2 * * @param string $field Attribute name. * @param string $value Attribute value. * @param array $enabled_dynamic_attributes Attributes which have dynamic content enabled. * * @return string */ protected function _encode_legacy_dynamic_content_value( $field, $value, $enabled_dynamic_attributes ) { if ( ! in_array( $field, $enabled_dynamic_attributes, true ) ) { return $value; } $json = et_builder_clean_dynamic_content( $value ); if ( preg_match( '/^@ET-DC@(.*?)@$/', $json ) ) { return $value; } return $this->_resolve_value_from_json( $field, $json, $enabled_dynamic_attributes ); } /** * Resolve a value, be it static or dynamic to a static one. * * @since 3.17.2 * * @param integer $post_id Current post id. * @param string $field Content key. * @param string $value Content value. * @param string[] $enabled_dynamic_attributes Attributes which have dynamic content enabled. * @param boolean $serialize Whether value is serializable. * * @return string */ protected function _resolve_value( $post_id, $field, $value, $enabled_dynamic_attributes, $serialize ) { global $wp_query; if ( ! in_array( $field, $enabled_dynamic_attributes, true ) ) { return $value; } $builder_value = et_builder_parse_dynamic_content( $value ); if ( $serialize ) { return $builder_value->serialize(); } $is_blog_query = isset( $wp_query->et_pb_blog_query ) && $wp_query->et_pb_blog_query; if ( ! $is_blog_query && ! $wp_query->is_singular() ) { return $builder_value->resolve( null ); } return $builder_value->resolve( $post_id ); } /** * Resolve a value from the legacy JSON format of dynamic content. * This is essentially a migration but is implemented separately * as it needs to parse every field of every module and do it * before actual migrations are ran. * * @since 3.20.2 * * @param string $field Field content. * @param string $value Json value. * @param string[] $enabled_dynamic_attributes Attributes which have dynamic content enabled. * * @return string */ protected function _resolve_value_from_json( $field, $value, $enabled_dynamic_attributes ) { if ( ! in_array( $field, $enabled_dynamic_attributes, true ) ) { return $value; } $json = et_builder_clean_dynamic_content( $value ); // Replace encoded quotes. $json = str_replace( array( '“', '”', '″', '%22' ), '"', $json ); // Strip

artifacts from wpautop in before/after settings. Example: // {"dynamic":true,"content":"post_title","settings":{"before":"

//

","after":"

//

"}} // This is a rough solution implemented due to time constraints. $json = preg_replace( '~ ("(?:before|after)":") # $1 = Anchor to the before/after settings. (?: # Match cases where the value starts with the offending tag. <\/?p> # The root of all evil. [\r\n]+ # Whitespace follows the tag. )* (?: # Match cases where the value ends with the offending tag. ([^"]*) # $2 = The preceeding value. [\r\n]+ # Whitespace preceedes the tag. <\/?p> # The root of all evil. )* ~xi', '$1$2', $json ); // Remove line-breaks which break the json strings. $json = preg_replace( '/\r|\n/', '', $json ); $json_value = et_builder_parse_dynamic_content_json( $json ); if ( null === $json_value ) { return $value; } return $json_value->serialize(); } /** * Escape an attribute's value. * * @since 3.17.2 * * @param string $attribute Attribute name. * @param string $html 'limited', 'full', 'none'. * @param string $predefined_value Predifined value need to escape. * * @return string */ protected function _esc_attr( $attribute, $html = 'none', $predefined_value = null ) { $html = in_array( $html, array( 'limited', 'full' ), true ) ? $html : 'none'; $raw = isset( $this->attrs_unprocessed[ $attribute ] ) ? $this->attrs_unprocessed[ $attribute ] : ''; $formatted = isset( $this->props[ $attribute ] ) ? $this->props[ $attribute ] : ''; $dynamic_attributes = $this->_get_enabled_dynamic_attributes( $this->props ); // More often than not content is not an attribute so we need to handle that special case. if ( 'content' === $attribute && ! isset( $this->attrs_unprocessed[ $attribute ] ) ) { $raw = $this->content_unprocessed; $formatted = $this->content; } if ( ! is_null( $predefined_value ) ) { $formatted = $predefined_value; } if ( ! $this->_is_dynamic_value( $attribute, $raw, $dynamic_attributes ) ) { if ( 'full' === $html ) { return $formatted; } return esc_html( $formatted ); } if ( 'limited' === $html ) { return wp_kses( $formatted, array( 'strong' => array( 'id' => array(), 'class' => array(), 'style' => array(), ), 'em' => array( 'id' => array(), 'class' => array(), 'style' => array(), ), 'i' => array( 'id' => array(), 'class' => array(), 'style' => array(), ), ) ); } // Dynamic content values are escaped when they are resolved so we do not want to // double-escape them when using them in the frontend, for example. return et_core_esc_previously( $formatted ); } /** * Get the current TB layout ID if we are rendering one or the current post ID instead. * * @since 4.0 * @since 4.14.8 Get WP Template ID if we are rendering Divi Builder block in template. * * @return integer */ public static function get_layout_id() { // TB Layout ID. $layout_id = self::get_theme_builder_layout_id(); if ( $layout_id ) { return $layout_id; } // WP Template ID. $template_id = self::get_wp_editor_template_id(); if ( $template_id ) { return $template_id; } // Post ID by default. return self::get_current_post_id_reverse(); } /** * Get the current theme builder layout. * Returns 'default' if no layout has been started. * * @since 4.0 * * @return string */ public static function get_theme_builder_layout_type() { $count = count( self::$theme_builder_layout ); if ( $count > 0 ) { return self::$theme_builder_layout[ $count - 1 ]['type']; } return 'default'; } /** * Check if a module is rendered as normal post content or theme builder layout. * * @since 4.0 * * @return bool */ public static function is_theme_builder_layout() { return 'default' !== self::get_theme_builder_layout_type(); } /** * Get the current theme builder layout id. * Returns 0 if no layout has been started. * * @since 4.0 * * @return integer */ public static function get_theme_builder_layout_id() { $count = count( self::$theme_builder_layout ); if ( $count > 0 ) { return self::$theme_builder_layout[ $count - 1 ]['id']; } return 0; } /** * Begin a theme builder layout. * * @since 4.0 * * @param integer $layout_id Layout post id. * * @return void */ public static function begin_theme_builder_layout( $layout_id ) { $type = get_post_type( $layout_id ); if ( ! et_theme_builder_is_layout_post_type( $type ) ) { $type = 'default'; } self::$theme_builder_layout[] = array( 'id' => (int) $layout_id, 'type' => $type, ); } /** * End the current theme builder layout. * * @since 4.0 * * @return void */ public static function end_theme_builder_layout() { array_pop( self::$theme_builder_layout ); } /** * Get the order class suffix for the current theme builder layout, if any. * * @since 4.0 * * @return string */ protected static function _get_theme_builder_order_class_suffix() { $layout_type = self::get_theme_builder_layout_type(); $type_map = array( ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE => '_tb_header', ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE => '_tb_body', ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE => '_tb_footer', ); if ( empty( $layout_type ) || ! isset( $type_map[ $layout_type ] ) ) { return ''; } return $type_map[ $layout_type ]; } /** * Begin Divi Builder block output on WP Editor template. * * As identifier od Divi Builder block render template location and the template ID. * Introduced to handle Divi Layout block render on WP Template outside Post Content. * WP Editor templates: * - wp_template * - wp_template_part * * @since 4.14.8 * * @param array $template_id Template post ID. * * @return void */ public static function begin_wp_editor_template( $template_id ) { $type = get_post_type( $template_id ); if ( ! et_builder_is_wp_editor_template_post_type( $type ) ) { $type = 'default'; } self::$wp_editor_template[] = array( 'id' => (int) $template_id, 'type' => $type, ); } /** * End Divi Builder block output on WP Editor template. * * @since 4.14.8 * * @return void */ public static function end_wp_editor_template() { array_pop( self::$wp_editor_template ); } /** * Whether a module is rendered in WP Editor template or not. * * @since 4.14.8 * * @return bool WP Editor template status. */ public static function is_wp_editor_template() { return 'default' !== self::get_wp_editor_template_type(); } /** * Get the current WP Editor template id. * * Returns 0 if no template has been started. * * @since 4.14.8 * * @return integer Template post ID (wp_id). */ public static function get_wp_editor_template_id() { $count = count( self::$wp_editor_template ); $id = 0; if ( $count > 0 ) { $id = et_()->array_get( self::$wp_editor_template, array( $count - 1, 'id' ), 0 ); } // Just want to be safe to not return any unexpected result. return is_int( $id ) ? $id : 0; } /** * Get the current WP Editor template type. * * Returns 'default' if no template has been started. * * @since 4.14.8 * * @param boolean $is_id_needed Whether template ID is needed or not. * * @return string Template type. */ public static function get_wp_editor_template_type( $is_id_needed = false ) { $count = count( self::$wp_editor_template ); $type = ''; if ( $count > 0 ) { $type = et_()->array_get( self::$wp_editor_template, array( $count - 1, 'type' ) ); // Page may have more than one template parts. So, the wp_id is needed in certain // situation as unique identifier. if ( $is_id_needed && ET_WP_EDITOR_TEMPLATE_PART_POST_TYPE === $type ) { $id = self::get_wp_editor_template_id(); $type .= "-{$id}"; } } // Just want to be safe to not return any unexpected result. return ! empty( $type ) && is_string( $type ) ? $type : 'default'; } /** * Get the order class suffix for the current WP Editor template, if any. * * @since 4.14.8 * * @return string Order class suffix. */ protected static function _get_wp_editor_order_class_suffix() { $template_type = self::get_wp_editor_template_type(); $type_map = array( ET_WP_EDITOR_TEMPLATE_POST_TYPE => '_wp_template', ET_WP_EDITOR_TEMPLATE_PART_POST_TYPE => '_wp_template_part', ); if ( ! isset( $type_map[ $template_type ] ) ) { return ''; } $suffix = $type_map[ $template_type ]; // Page may have more than one template parts. So, the wp_id is needed identifier. if ( ET_WP_EDITOR_TEMPLATE_PART_POST_TYPE === $template_type ) { $id = self::get_wp_editor_template_id(); $suffix .= "-{$id}"; } return $suffix; } /** * Convert field name into css property name. * * @param string $field Field name. * * @return string|string[] */ protected function field_to_css_prop( $field ) { return str_replace( '_', '-', $field ); } /** * Initialize Modules Cache. * * @since 3.24 */ public static function init_cache() { $cache = self::get_cache_filename(); if ( $cache && et_()->WPFS()->is_readable( $cache ) ) { // Load cache. $result = @unserialize( et_()->WPFS()->get_contents( $cache ) ); if ( false !== $result ) { if ( count( $result ) < 3 ) { // Old cache format detected, delete everything. et_fb_delete_builder_assets(); if ( ! file_exists( $cache ) ) { // If cache has been successfully deleted, then init again. self::init_cache(); } return; } list ( self::$_cache, self::$_fields_unprocessed ) = $result; // Define option template variable instead of using list to avoid error that might // happen when option template file exists (theme is updated) and frontend is // accessed while static module field data hasn't been updated. $cached_option_template_data = et_()->array_get( $result, '2', array() ); $cached_option_template = et_()->array_get( $result, '3', array() ); $cached_option_template_tab_slug_maps = et_()->array_get( $result, '4', array() ); // init_cache() is called really early. $template property might not be available yet. if ( null === self::$option_template ) { self::$option_template = et_pb_option_template(); } // Set option template data from static cache if exist. if ( is_array( $cached_option_template_data ) && ! empty( $cached_option_template_data ) ) { self::$option_template->set_data( $cached_option_template_data ); } // Set option template from static cache if exist. if ( is_array( $cached_option_template ) && ! empty( $cached_option_template ) ) { self::$option_template->set_templates( $cached_option_template ); } // Set option template tab slug maps from static cache if exist. if ( is_array( $cached_option_template_tab_slug_maps ) && ! empty( $cached_option_template_tab_slug_maps ) ) { self::$option_template->set_tab_slug_map( $cached_option_template_tab_slug_maps ); } // Box Shadow sets WP hooks internally so we gotta load it anyway -> #blame_george. ET_Builder_Module_Fields_Factory::get( 'BoxShadow' ); } else { // Cache couldn't be unserialized, delete the file so it will be regenerated. @unlink( $cache ); } } elseif ( $cache ) { // Only save cache when a builder page is being rendered, needed because some data // (e.g. mail provider defaults) is only generated in this case, hence saving while rendering // a FE page or during AJAX call would result in cache missing data. self::$_cache = array(); add_filter( 'et_builder_modules_is_saving_cache', '__return_true' ); add_filter( 'et_builder_should_load_all_module_data', '__return_true' ); add_action( 'et_builder_ready', array( 'ET_Builder_Element', 'save_cache' ) ); } } /** * Get Modules cache file name. * * @param mixed $post_type When set to `false`, autodetect. * * @since 3.24 * * @return bool|mixed|string */ public static function get_cache_filename( $post_type = false ) { global $post, $et_builder_post_type; $ajax_use_cache = apply_filters( 'et_builder_ajax_use_cache', false ); if ( false === $post_type ) { if ( is_a( $post, 'WP_POST' ) ) { $post_type = $post->post_type; } elseif ( $ajax_use_cache ) { // phpcs:ignore WordPress.Security.NonceVerification -- Nonce verified in the ajax request. $post_type = et_()->array_get( $_POST, 'et_post_type', 'page' ); } elseif ( is_admin() && ! wp_doing_ajax() ) { $post_type = 'page'; $et_builder_post_type = $post_type; } if ( false === $post_type ) { return false; } } $post_type = apply_filters( 'et_builder_cache_post_type', $post_type, 'modules' ); $post_type = trim( sanitize_file_name( $post_type ), '.' ); // Per language Cache due to fields data being localized. // Use user custom locale only if admin or VB/BFB. $lang = is_admin() || et_fb_is_enabled() ? get_user_locale() : get_locale(); $lang = trim( sanitize_file_name( $lang ), '.' ); $prefix = 'modules'; $cache = sprintf( '%s/%s', ET_Core_PageResource::get_cache_directory(), $lang ); $files = glob( sprintf( '%s/%s-%s-*.data', $cache, $prefix, $post_type ) ); $exists = is_array( $files ) && $files; if ( $exists ) { return $files[0]; } elseif ( $ajax_use_cache ) { // Allowlisted AJAX requests aren't allowed to generate cache, only to use it. return false; } wp_mkdir_p( $cache ); // Create uniq filename. $uniq = str_replace( '.', '', (string) microtime( true ) ); $file = sprintf( '%s/%s-%s-%s.data', $cache, $prefix, $post_type, $uniq ); return wp_is_writable( dirname( $file ) ) ? $file : false; } /** * Get Module cache file name's id. * * @since 3.28 * * @param mixed $post_type When set to `false`, autodetect. * * @return bool|string */ public static function get_cache_filename_id( $post_type = false ) { $filename = self::get_cache_filename( $post_type ); if ( ! is_string( $filename ) ) { return false; } preg_match( '/(?<=-)[0-9]*(?=.data)/', $filename, $matches ); return isset( $matches[0] ) ? $matches[0] : false; } /** * Save the builder module caache. */ public static function save_cache() { remove_filter( 'et_builder_modules_is_saving_cache', '__return_true' ); $cache = self::get_cache_filename(); if ( $cache ) { et_()->WPFS()->put_contents( $cache, serialize( array( self::$_cache, self::$_fields_unprocessed, self::$option_template->all(), self::$option_template->templates(), self::$option_template->get_tab_slug_map(), '3.0', ) ) ); } } /** * Render image element HTML * * @since 3.27.1 * * @param string $image_props Image data props key or actual image URL. * @param array $image_attrs_raw List of extra image attributes. * @param bool $echo Whether to print the image output or return it. * @param bool $disable_responsive Whether to enable the responsive image or not. * * @return string The images's HTML output. Empty string on failure. */ protected function render_image( $image_props, $image_attrs_raw = array(), $echo = true, $disable_responsive = false ) { // Bail early when the $image_props arg passed is empty. if ( ! $image_props ) { return ''; } $img_src = $image_props && is_string( $image_props ) ? self::$_->array_get( $this->props, $image_props, $image_props ) : $image_props; if ( ! $img_src ) { return ''; } if ( ! count( $image_attrs_raw ) ) { $html = sprintf( '', esc_url( $img_src ) ); return et_image_add_srcset_and_sizes( $html, $echo ); } $image_attrs = array(); $is_disable_responsive = $disable_responsive || ! et_is_responsive_images_enabled(); foreach ( $image_attrs_raw as $name => $value ) { // Skip src attributes key. if ( 'src' === $name ) { continue; } // Skip srcset & sizes attributes when setting is off. if ( $is_disable_responsive && in_array( $name, array( 'srcset', 'sizes' ), true ) ) { continue; } // Skip if attributes value is empty. if ( ! strlen( $value ) ) { continue; } // Format as JSON if the value is array or object. if ( is_array( $value ) || is_object( $value ) ) { $value = wp_json_encode( $value ); } // Trim extra space from attributes value. $value = trim( $value ); // Standalone attributes that act as Booleans (Numerical indexed array keys such as required, disabled, multiple). if ( is_numeric( $name ) ) { $value = et_core_esc_attr( $value, $value ); if ( ! is_wp_error( $value ) ) { $image_attrs[ $value ] = et_core_esc_previously( $value ); } } else { $value = et_core_esc_attr( $name, $value ); if ( ! is_wp_error( $value ) ) { $image_attrs[ $name ] = esc_attr( $name ) . '="' . et_core_esc_previously( $value ) . '"'; } } } $html = sprintf( '', esc_url( $img_src ), et_core_esc_previously( implode( ' ', $image_attrs ) ) ); if ( ! $is_disable_responsive && ! isset( $image_attrs['srcset'] ) && ! isset( $image_attrs['sizes'] ) ) { $html = et_image_add_srcset_and_sizes( $html, false ); } if ( ! $echo ) { return $html; } echo et_core_intentionally_unescaped( $html, 'html' ); } /** * Get advanced field settings exposed for layout block preview * * @since 4.3.2 * * @return array */ public static function get_layout_block_assistive_settings() { return self::$layout_block_assistive_settings; } /** * Get whether the provided element content contains at least one of the * specified modules based on their slugs. * * @since 4.3.3 * * @param string $content Element content. * @param string[] $module_slugs Module slug to search. * * @return bool */ protected static function contains( $content, $module_slugs ) { foreach ( $module_slugs as $slug ) { if ( false !== strpos( $content, '[' . $slug ) ) { return true; } } return false; } /** * Public access provider for self::contains(). * * @since 4.17.4 * * @param string $content Element content. * @param string[] $module_slugs Module slug to search. * * @return bool */ public static function module_contains( $content, $module_slugs ) { return self::contains( $content, $module_slugs ); } /** * Generate background field setting properties by template * * NOTE: Unless the `priority` property is used, the order that settings are listed is the order * that they'll appear in the settings modal. * * @param string $field_template Field template slug. * @param array $overrides Field properties to override. * @param array $unsets Field properties to unset. * * @return array * * @since 4.8.0 * */ public static function background_field_template( $field_template, $overrides = array(), $unsets = array() ) { static $cache = null; if ( is_null( $cache ) ) { $cache = array(); $tabs = array( // Tab: color, priority start with 100. 'color' => array( 'priority' => 100, 'data' => array( 'enable_color' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color' => array( 'type' => 'color-alpha', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'use_color' => array( 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), ), ), // Tab: gradient, priority start with 200. 'gradient' => array( 'priority' => 200, 'data' => array( 'use_color_gradient' => array( 'priority' => 201, 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'color_gradient_repeat' => array( 'priority' => 205, 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color_gradient_type' => array( 'priority' => 203, 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color_gradient_direction' => array( 'priority' => 204, 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'show_if' => array( 'color_gradient_type' => array( 'conic', 'linear', ), ), ), 'color_gradient_direction_radial' => array( 'priority' => 204, 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'show_if' => array( 'color_gradient_type' => array( 'radial', 'circular', 'elliptical', ), ), ), 'color_gradient_stops' => array( 'priority' => 202, 'type' => 'gradient-stops', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color_gradient_unit' => array( 'priority' => 206, 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'color_gradient_overlays_image' => array( 'priority' => 299, 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), // Deprecated. 'color_gradient_start' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), // Deprecated. 'color_gradient_start_position' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'option_category' => 'configuration', ), // Deprecated. 'color_gradient_end' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), // Deprecated. 'color_gradient_end_position' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), ), ), // Tab: image, priority start with 300. 'image' => array( 'priority' => 300, 'data' => array( 'enable_image' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'image' => array( 'type' => 'upload', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'parallax' => array( 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'parallax_method' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'size' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'image_width' => array( 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'image_height' => array( 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'position' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'horizontal_offset' => array( 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'vertical_offset' => array( 'type' => 'range', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'repeat' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'blend' => array( 'type' => 'select', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), ), ), // Tab: video, priority start with 400. 'video' => array( 'priority' => 400, 'data' => array( 'enable_video_mp4' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_mp4' => array( 'type' => 'upload', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'enable_video_webm' => array( 'type' => 'skip', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_webm' => array( 'type' => 'upload', 'mobile_options' => false, 'hover' => false, 'sticky' => false, 'tab_filler' => true, ), 'video_width' => array( 'type' => 'text', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_height' => array( 'type' => 'text', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'allow_player_pause' => array( 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_pause_outside_viewport' => array( 'type' => 'yes_no_button', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), 'video_computed' => array( 'type' => 'computed', 'mobile_options' => false, 'hover' => false, 'sticky' => false, ), ), ), // Tab: pattern, priority start with 500. 'pattern' => array( 'priority' => 500, 'data' => et_ph_pattern_field_templates(), ), // Tab: mask, priority start with 500. 'mask' => array( 'priority' => 600, 'data' => et_ph_mask_field_templates(), ), ); foreach ( $tabs as $background_tab_slug => $tab ) { $priority = $tab['priority']; foreach ( $tab['data'] as $template_slug => $template_data ) { $template_priority = isset( $template_data['priority'] ) && is_numeric( $template_data['priority'] ) ? $template_data['priority'] : $priority; if ( $template_priority < $tab['priority'] ) { $template_priority += $tab['priority']; } $cache[ $template_slug ] = array_merge( $template_data, array( 'field_template' => $template_slug, 'background_tab' => $background_tab_slug, 'priority' => $template_priority, 'tab_slug' => 'general', 'toggle_slug' => 'background', ) ); $priority += 10; } } } $template = isset( $cache[ $field_template ] ) ? $cache[ $field_template ] : array(); if ( $overrides && is_array( $overrides ) ) { $template = array_merge( $template, $overrides ); } if ( $unsets && is_array( $unsets ) ) { foreach ( $unsets as $unset ) { unset( $template[ $unset ] ); } } return array_merge( $template, array( 'field_template' => $field_template, ) ); } /** * Generate markup for background pattern. * * @since 4.15.0 * * @param array $args Props. * @param string $base_name Background base name. * * @return string Background pattern markup. */ public function background_pattern( $args = array(), $base_name = 'background' ) { $props = ! empty( $args ) ? (array) $args : $this->props; $modes = array( 'desktop', 'hover', 'sticky', 'tablet', 'phone' ); $is_active = false; foreach ( $modes as $mode ) { // Checks whether pattern style is enabled and has value. $style_name = et_pb_responsive_options()->get_inheritance_background_value( $props, "{$base_name}_pattern_style", $mode, 'background', $this->fields_unprocessed ); if ( ! empty( $style_name ) ) { // We need to render pattern element. $is_active = true; break; } } $output = ''; if ( $is_active ) { $class_name = array( 'et_pb_background_pattern', ); $output = sprintf( '', implode( ' ', array_map( 'esc_attr', $class_name ) ) ); } return $output; } /** * Generate markup for background mask. * * @since 4.15.0 * * @param array $args Props. * @param string $base_name Background base name. * * @return string Background mask markup. */ public function background_mask( $args = array(), $base_name = 'background' ) { $props = ! empty( $args ) ? (array) $args : $this->props; $modes = array( 'desktop', 'hover', 'sticky', 'tablet', 'phone' ); $is_active = false; foreach ( $modes as $mode ) { // Checks whether mask style is enabled and has value. $style_name = et_pb_responsive_options()->get_inheritance_background_value( $props, "{$base_name}_mask_style", $mode, 'background', $this->fields_unprocessed ); if ( ! empty( $style_name ) ) { // We need to render mask element. $is_active = true; break; } } $output = ''; if ( $is_active ) { $class_name = array( 'et_pb_background_mask', ); $output = sprintf( '', implode( ' ', array_map( 'esc_attr', $class_name ) ) ); } return $output; } /** * Get components before & after module. * * This method is introduced to handle additional components added on WooCommerce * product summary that should be moved to any suitable modules on builder preview. * * @since 4.14.5 * * @param string $module_slug Module slug. * @param array $module_data Module data passed. * * @return array Components before & after module. */ public static function get_component_before_after_module( $module_slug, $module_data ) { // Default processed components for the return request. Need to use `__` to be set // as the attribute name. $processed_components = array( 'has_components' => false, '__before_component' => '', '__after_component' => '', ); /** * Filters module's before & after components for current builder page. * * @since 4.14.5 * * @param array $processed_components Default of module's before & after components. * @param string $module_slug Module slug. * @param array $module_data Module data passed from the request. */ $components = apply_filters( "{$module_slug}_fb_before_after_components", $processed_components, $module_slug, $module_data ); // Skip if there is no before & after components. $has_components = et_()->array_get( $components, 'has_components' ); if ( true !== $has_components ) { return $processed_components; } $processed_components = array( 'has_components' => true, '__before_component' => et_()->array_get( $components, '__before_component' ), '__after_component' => et_()->array_get( $components, '__after_component' ), ); return $processed_components; } /** * ================================================================================================================ * -------------------------->>> Class-level (static) deprecations begin here! <<<--------------------------------- * ================================================================================================================ */ // phpcs:disable -- Deprecated functions. /** * @deprecated See {@see self::get_parent_slugs_regex()} */ public static function get_parent_shortcodes( $post_type ) { $method = __METHOD__; $replacement = __CLASS__ . '::get_parent_slugs_regex()'; et_error( "You're Doing It Wrong! {$method} is deprecated. Use {$replacement} instead." ); return self::get_parent_slugs_regex( $post_type ); } /** * @deprecated See {@see self::get_child_slugs_regex()} */ public static function get_child_shortcodes( $post_type ) { $method = __METHOD__; $replacement = __CLASS__ . '::get_child_slugs_regex()'; et_error( "You're Doing It Wrong! {$method} is deprecated. Use {$replacement} instead." ); return self::get_child_slugs_regex( $post_type ); } /** * Deprecated. * * @deprecated * * @param string $post_type * @param string $mode * * @return array */ public static function get_defaults( $post_type = '', $mode = 'all' ) { et_error( "You're Doing It Wrong! " . __METHOD__ . ' is deprecated and should not be used.' ); return array(); } /** * Deprecated. * * @deprecated * * @param string $post_type * @param string $mode * * @return array */ public static function get_fields_defaults( $post_type = '', $mode = 'all' ) { et_error( "You're Doing It Wrong! " . __METHOD__ . ' is deprecated and should not be used.' ); return array(); } /** * @deprecated */ public static function get_slugs_with_children( $post_type ) { $parent_modules = self::get_parent_modules( $post_type ); $slugs = array(); foreach ( $parent_modules as $module ) { if ( ! empty( $module->child_slug ) ) { $slugs[] = sprintf( '"%1$s":"%2$s"', esc_js( $module->slug ), esc_js( $module->child_slug ) ); } } return '{' . implode( ',', $slugs ) . '}'; } // phpcs:enable /** * ================================================================================================================ * ------------------------------->>> Non-static deprecations begin here! <<<-------------------------------------- * ================================================================================================================ */ /** * Determine if current request is VB Data Request by checking $_POST['action'] value * * @deprecated {@see et_builder_is_loading_vb_data()} * * @since 4.0.7 Deprecated. * * @return bool */ protected function is_loading_vb_data() { return et_builder_is_loading_data(); } /** * Determine if current request is BB Data Request by checking $_POST['action'] value * * @deprecated {@see et_builder_is_loading_bb_data()} * * @since 4.0.7 Deprecated. * * @return bool */ protected function is_loading_bb_data() { return et_builder_is_loading_data( 'bb' ); } /* NOTE: Adding a new method? New methods should be placed BEFORE deprecated methods. */ } do_action( 'et_pagebuilder_module_init' ); /** * Base class for module. * * Class ET_Builder_Module */ class ET_Builder_Module extends ET_Builder_Element { /** * Whether element is structure element. * * @var bool */ public $is_structure_element = false; } /** * Base class for structure elements. * * Class ET_Builder_Structure_Element */ class ET_Builder_Structure_Element extends ET_Builder_Element { /** * Whether element is structure element. * * @var bool */ public $is_structure_element = true; /** * BB :: Wrap setting option in parent div. * * @param string $option_output Setting options markup. * @param array $field Setting field. * @param string $name Setting field name e.g background_color. * * @return string|string[] */ public function wrap_settings_option( $option_output, $field, $name = '' ) { // Option template convert array field into string id; return early to prevent error. if ( is_string( $field ) ) { return ''; } $field_type = ! empty( $field['type'] ) ? $field['type'] : ''; switch ( $field_type ) { case 'column_settings_background': $output = $this->generate_columns_settings_background(); $field['hover'] = 'tabs'; break; case 'column_settings_padding': $output = $this->generate_columns_settings_padding(); break; case 'column_settings_css_fields': $output = $this->generate_columns_settings_css_fields(); break; case 'column_settings_css': $output = $this->generate_columns_settings_css(); break; case 'column-structure': // column structure option is not supported in BB. return ''; break; default: $depends = false; $new_depends = isset( $field['show_if'] ) || isset( $field['show_if_not'] ); if ( ! $new_depends && ( isset( $field['depends_show_if'] ) || isset( $field['depends_show_if_not'] ) ) ) { $depends = true; if ( isset( $field['depends_show_if_not'] ) ) { $depends_show_if_not = is_array( $field['depends_show_if_not'] ) ? implode( ',', $field['depends_show_if_not'] ) : $field['depends_show_if_not']; $depends_attr = sprintf( ' data-depends_show_if_not="%s"', esc_attr( $depends_show_if_not ) ); } else { $depends_attr = sprintf( ' data-depends_show_if="%s"', esc_attr( $field['depends_show_if'] ) ); } } // Overriding background color's attribute, turning it into appropriate background attributes. if ( isset( $field['type'] ) && isset( $field['name'] ) && in_array( $field['name'], array( 'background_color' ), true ) ) { $field['type'] = 'background'; // Appending background class. if ( isset( $field['option_class'] ) ) { $field['option_class'] .= ' et-pb-option--background'; } else { $field['option_class'] = 'et-pb-option--background'; } // Removing depends default variable which hides background color for unified background field UI. $depends = false; if ( isset( $field['depends_show_if'] ) ) { unset( $field['depends_show_if'] ); } } $output = sprintf( '%6$s

%5$s
%7$s', ( ! empty( $field['type'] ) && 'tiny_mce' === $field['type'] ? ' et-pb-option-main-content' : '' ), $depends || $new_depends ? ' et-pb-depends' : '', ( ! empty( $field['type'] ) && 'hidden' === $field['type'] ? ' et_pb_hidden' : '' ), ( $depends ? $depends_attr : '' ), "\n\t\t\t\t" . $option_output . "\n\t\t\t", "\t", "\n\n\t\t", ( ! empty( $field['type'] ) && 'hidden' === $field['type'] ? esc_attr( sprintf( ' et-pb-option-%1$s', $field['name'] ) ) : '' ), ( ! empty( $field['option_class'] ) ? ' ' . $field['option_class'] : '' ), isset( $field['specialty_only'] ) && 'yes' === $field['specialty_only'] ? ' et-pb-specialty-only-option' : '', isset( $field['type'] ) ? esc_attr( $field['type'] ) : '', esc_attr( $field['name'] ), $new_depends ? ' et-pb-new-depends' : '' ); break; } if ( ! empty( $field['hover'] ) ) { if ( 'tabs' === $field['hover'] ) { $name = ( 'columns_background' === $name ) ? 'background_color_<%= counter %>' : $name; $this->last_hover_tab_field = $name; } $hover = $this->last_hover_tab_field; if ( $hover ) { $begin = '
', $this->generate_column_vars_bg() ); $tab_navs = sprintf( '', et_builder_i18n( 'Color' ), esc_html__( 'Gradient', 'et_builder' ), et_builder_i18n( 'Image' ), esc_html__( 'Video', 'et_builder' ), $this->get_icon( 'background-color' ), $this->get_icon( 'background-gradient' ), $this->get_icon( 'background-image' ), $this->get_icon( 'background-video' ) ); $tab_color = sprintf( '
', esc_html__( 'Background Color', 'et_builder' ), $this->get_icon( 'add' ), $this->get_icon( 'setting' ), $this->get_icon( 'delete' ), esc_html__( 'Hex Value', 'et_builder' ) ); $tab_gradient_options = ''; // Gradient Preview (with Add/Swap/Delete buttons). $tab_gradient_options .= sprintf( '
', $this->get_icon( 'add' ), $this->get_icon( 'swap' ), $this->get_icon( 'delete' ) ); // Use Background Color Gradient. $tab_gradient_options .= sprintf( '
%2$s %3$s
', esc_html__( 'Background Gradient', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ) ); // Gradient Type. $tab_gradient_options .= sprintf( '
', esc_html__( 'Gradient Type', 'et_builder' ), et_builder_i18n( 'Linear' ), et_builder_i18n( 'Circular' ), et_builder_i18n( 'Elliptical' ), et_builder_i18n( 'Conical' ) ); // Gradient Direction. $tab_gradient_options .= sprintf( '
', esc_html__( 'Gradient Direction', 'et_builder' ) ); // Gradient Position. $tab_gradient_options .= sprintf( '
', esc_html__( 'Gradient Position', 'et_builder' ), et_builder_i18n( 'Center' ), et_builder_i18n( 'Top Left' ), et_builder_i18n( 'Top' ), et_builder_i18n( 'Top Right' ), // #5 et_builder_i18n( 'Right' ), et_builder_i18n( 'Bottom Right' ), et_builder_i18n( 'Bottom' ), et_builder_i18n( 'Bottom Left' ), et_builder_i18n( 'Left' ) // #10 ); // Gradient Stops. $tab_gradient_options .= sprintf( '
', esc_html__( 'Gradient Stops', 'et_builder' ), esc_attr( ET_Global_Settings::get_value( 'all_background_gradient_stops' ) ) ); // Repeat Gradient toggle. $tab_gradient_options .= sprintf( '
%2$s %3$s
', esc_html__( 'Repeat Gradient', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ) ); // Gradient Unit list. $tab_gradient_options .= sprintf( '
', esc_html__( 'Gradient Unit', 'et_builder' ), et_builder_i18n( 'Percent' ), et_builder_i18n( 'Pixels' ), et_builder_i18n( 'Font Size (em)' ), et_builder_i18n( 'Root-level Font Size (rem)' ), // #5 et_builder_i18n( 'X-Height (ex)' ), et_builder_i18n( 'Zero-width (ch)' ), et_builder_i18n( 'Picas (pc)' ), et_builder_i18n( 'Points (pt)' ), et_builder_i18n( 'Centimeters (cm)' ), // #10 et_builder_i18n( 'Millimeters (mm)' ), et_builder_i18n( 'Inches (in)' ), et_builder_i18n( 'Viewport Height (vh)' ), et_builder_i18n( 'Viewport Width (vw)' ), et_builder_i18n( 'Viewport Minimum (vmin)' ), // #15 et_builder_i18n( 'Viewport Maximum (vmax)' ) ); // Gradient/Image Overlay. $tab_gradient_options .= sprintf( '
%2$s %3$s
', esc_html__( 'Place Gradient Above Background Image', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ) ); // Options for the Gradient tab. $tab_gradient = sprintf( '
%1$s
', $tab_gradient_options ); $select_background_size = sprintf( '', esc_html__( 'Cover', 'et_builder' ), esc_html__( 'Fit', 'et_builder' ), esc_html__( 'Actual Size', 'et_builder' ) ); $select_background_position = sprintf( '', et_builder_i18n( 'Top Left' ), et_builder_i18n( 'Top Center' ), et_builder_i18n( 'Top Right' ), et_builder_i18n( 'Center Left' ), et_builder_i18n( 'Center' ), et_builder_i18n( 'Center Right' ), et_builder_i18n( 'Bottom Left' ), et_builder_i18n( 'Bottom Center' ), et_builder_i18n( 'Bottom Right' ) ); $select_background_repeat = sprintf( '', esc_html__( 'No Repeat', 'et_builder' ), esc_html__( 'Repeat', 'et_builder' ), esc_html__( 'Repeat X (horizontal)', 'et_builder' ), esc_html__( 'Repeat Y (vertical)', 'et_builder' ), et_builder_i18n( 'Space' ), esc_html__( 'Round', 'et_builder' ) ); $select_background_blend = sprintf( '', et_builder_i18n( 'Normal' ), et_builder_i18n( 'Multiply' ), et_builder_i18n( 'Screen' ), et_builder_i18n( 'Overlay' ), et_builder_i18n( 'Darken' ), et_builder_i18n( 'Lighten' ), et_builder_i18n( 'Color Dodge' ), et_builder_i18n( 'Color Burn' ), et_builder_i18n( 'Hard Light' ), et_builder_i18n( 'Soft Light' ), et_builder_i18n( 'Difference' ), et_builder_i18n( 'Exclusion' ), et_builder_i18n( 'Hue' ), et_builder_i18n( 'Saturation' ), et_builder_i18n( 'Color' ), et_builder_i18n( 'Luminosity' ) ); $tab_image = sprintf( '
%9$s %10$s
%15$s
%17$s
%19$s
%21$s
', esc_html__( 'Background Image', 'et_builder' ), $this->get_icon( 'add' ), $this->get_icon( 'setting' ), $this->get_icon( 'delete' ), et_builder_i18n( 'Upload an image' ), // #5 esc_html__( 'Choose a Background Image', 'et_builder' ), esc_html__( 'Set As Background', 'et_builder' ), esc_html__( 'Use Parallax Effect', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ), // #10 esc_html__( 'Parallax Method', 'et_builder' ), esc_html__( 'True Parallax', 'et_builder' ), esc_html__( 'CSS', 'et_builder' ), esc_html__( 'Background Image Size', 'et_builder' ), $select_background_size, // #15 esc_html__( 'Background Image Position', 'et_builder' ), $select_background_position, esc_html__( 'Background Image Repeat', 'et_builder' ), $select_background_repeat, esc_html__( 'Background Image Blend', 'et_builder' ), // #20 $select_background_blend ); $tab_video = sprintf( '
%13$s %14$s
%13$s %14$s
', esc_html__( 'Background Video MP4', 'et_builder' ), $this->get_icon( 'add' ), $this->get_icon( 'setting' ), $this->get_icon( 'delete' ), esc_html__( 'Upload a video', 'et_builder' ), // #5 esc_html__( 'Choose a Background Video MP4 File', 'et_builder' ), esc_html__( 'Set As Background Video', 'et_builder' ), esc_html__( 'Background Video Webm', 'et_builder' ), esc_html__( 'Choose a Background Video WEBM File', 'et_builder' ), esc_html__( 'Background Video Width', 'et_builder' ), // #10 esc_html__( 'Background Video Height', 'et_builder' ), esc_html__( 'Pause Video When Another Video Plays', 'et_builder' ), et_builder_i18n( 'On' ), et_builder_i18n( 'Off' ), esc_html__( 'Pause Video While Not In View', 'et_builder' ) // #15 ); $output .= sprintf( '
%3$s %4$s %5$s %6$s %7$s
<%% counter++; }); %%>', esc_html__( 'Column', 'et_builder' ), et_builder_i18n( 'Background' ), $tab_navs, $tab_color, $tab_gradient, // #5 $tab_image, $tab_video ); return $output; } /** * BB :: Generate Padding settings for columns. * * @return string */ public function generate_columns_settings_padding() { $output = sprintf( '<%% var columns = typeof columns_layout !== \'undefined\' ? columns_layout.split(",") : [], counter = 1; _.each( columns, function ( column_type ) { var current_value_pt, current_value_pr, current_value_pb, current_value_pl, current_value_padding_tablet, current_value_padding_phone, has_tablet_padding, has_phone_padding; switch ( counter ) { %1$s } %%>', $this->generate_column_vars_padding() ); $output .= sprintf( '
%7$s
<%% counter++; }); %%>', esc_html__( 'Column', 'et_builder' ), esc_html__( 'Padding', 'et_builder' ), et_builder_i18n( 'Top' ), et_builder_i18n( 'Right' ), et_builder_i18n( 'Bottom' ), // #5 et_builder_i18n( 'Left' ), et_pb_generate_mobile_settings_tabs() // #7 ); return $output; } /** * BB :: Generate "Custom CSS" settings for columns. * * @return string */ public function generate_columns_settings_css() { $output = sprintf( '<%% var columns_css = typeof columns_layout !== \'undefined\' ? columns_layout.split(",") : [], counter_css = 1; _.each( columns_css, function ( column_type ) { var current_module_id_value, current_module_class_value, current_custom_css_before_value, current_custom_css_main_value, current_custom_css_after_value; switch ( counter_css ) { %1$s } %%>
<%% counter_css++; }); %%>', $this->generate_column_vars_css(), esc_html__( 'Column', 'et_builder' ), et_builder_i18n( 'Before' ), et_builder_i18n( 'Main Element' ), et_builder_i18n( 'After' ) ); return $output; } /** * BB :: Generate "CSS ID & Classes" settings for columns. * * @return string */ public function generate_columns_settings_css_fields() { $output = sprintf( '<%% var columns_css = typeof columns_layout !== \'undefined\' ? columns_layout.split(",") : [], counter_css = 1; _.each( columns_css, function ( column_type ) { var current_module_id_value, current_module_class_value; switch ( counter_css ) { %1$s } %%>
<%% counter_css++; }); %%>', $this->generate_column_vars_css(), esc_html__( 'Column', 'et_builder' ), esc_html__( 'CSS ID', 'et_builder' ), esc_html__( 'CSS Class', 'et_builder' ) ); return $output; } } body { color: #2d2d2d; }