get( $attrs, "{$name}_last_edited", '' ); return $this->get_responsive_status( $last_edited ); } /** * Check if responsive settings are enabled on one of the options list. * * @since 3.23 * * @param array $attrs All module attributes. * @param array $list Options list. * @return boolean Responsive styles status. */ public function is_any_responsive_enabled( $attrs, $list ) { // Ensure list is not empty and valid array. if ( empty( $list ) || ! is_array( $list ) ) { return false; } // Check the responsive status one by one. $is_responsive_enabled = false; foreach ( $list as $name ) { if ( $this->is_responsive_enabled( $attrs, $name ) ) { // Break early if current field enabled responsive is found. $is_responsive_enabled = true; break; } } return $is_responsive_enabled; } /** * Get responsive status based one last edited value. * * Parsed *_last_edited value and determine wheter the passed string means it has responsive value * or not. *_last_edited holds two values (responsive status and last opened tabs) in the following * format: status|last_opened_tab. * * Copy of et_pb_get_responsive_status() with a little modified and to organize the code. * * @param string $last_edited Last edited field value. * @return bool Responsive field status. */ public function get_responsive_status( $last_edited ) { if ( empty( $last_edited ) || ! is_string( $last_edited ) ) { return false; } $parsed_last_edited = explode( '|', $last_edited ); return isset( $parsed_last_edited[0] ) ? 'on' === $parsed_last_edited[0] : false; } /** * Generate video background markup. * * When background support responsive settings, the default callback will be replaced with * get_video_background() function to retrieve all video values for desktop, hover, tablet, * and phone. * * @since 3.23 * * @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'; $attr_prefix = "{$base_name}_"; // Build custom args. $default_args = array( "{$attr_prefix}video_mp4" => isset( $args[ "{$attr_prefix}video_mp4" ] ) ? $args[ "{$attr_prefix}video_mp4" ] : '', "{$attr_prefix}video_webm" => isset( $args[ "{$attr_prefix}video_webm" ] ) ? $args[ "{$attr_prefix}video_webm" ] : '', "{$attr_prefix}video_width" => isset( $args[ "{$attr_prefix}video_width" ] ) ? $args[ "{$attr_prefix}video_width" ] : '', "{$attr_prefix}video_height" => isset( $args[ "{$attr_prefix}video_height" ] ) ? $args[ "{$attr_prefix}video_height" ] : '', 'computed_variables' => array( 'base_name' => $base_name, ), ); $hover_args = array( "{$attr_prefix}video_mp4__hover" => isset( $args[ "{$attr_prefix}video_mp4__hover" ] ) ? $args[ "{$attr_prefix}video_mp4__hover" ] : '', "{$attr_prefix}video_webm__hover" => isset( $args[ "{$attr_prefix}video_webm__hover" ] ) ? $args[ "{$attr_prefix}video_webm__hover" ] : '', "{$attr_prefix}video_width__hover" => isset( $args[ "{$attr_prefix}video_width__hover" ] ) ? $args[ "{$attr_prefix}video_width__hover" ] : '', "{$attr_prefix}video_height__hover" => isset( $args[ "{$attr_prefix}video_height__hover" ] ) ? $args[ "{$attr_prefix}video_height__hover" ] : '', 'computed_variables' => array( 'base_name' => $base_name, 'device' => '_hover', ), ); $tablet_args = array( "{$attr_prefix}video_mp4_tablet" => isset( $args[ "{$attr_prefix}video_mp4_tablet" ] ) ? $args[ "{$attr_prefix}video_mp4_tablet" ] : '', "{$attr_prefix}video_webm_tablet" => isset( $args[ "{$attr_prefix}video_webm_tablet" ] ) ? $args[ "{$attr_prefix}video_webm_tablet" ] : '', "{$attr_prefix}video_width_tablet" => isset( $args[ "{$attr_prefix}video_width_tablet" ] ) ? $args[ "{$attr_prefix}video_width_tablet" ] : '', "{$attr_prefix}video_height_tablet" => isset( $args[ "{$attr_prefix}video_height_tablet" ] ) ? $args[ "{$attr_prefix}video_height_tablet" ] : '', 'computed_variables' => array( 'base_name' => $base_name, 'device' => 'tablet', ), ); $phone_args = array( "{$attr_prefix}video_mp4_phone" => isset( $args[ "{$attr_prefix}video_mp4_phone" ] ) ? $args[ "{$attr_prefix}video_mp4_phone" ] : '', "{$attr_prefix}video_webm_phone" => isset( $args[ "{$attr_prefix}video_webm_phone" ] ) ? $args[ "{$attr_prefix}video_webm_phone" ] : '', "{$attr_prefix}video_width_phone" => isset( $args[ "{$attr_prefix}video_width_phone" ] ) ? $args[ "{$attr_prefix}video_width_phone" ] : '', "{$attr_prefix}video_height_phone" => isset( $args[ "{$attr_prefix}video_height_phone" ] ) ? $args[ "{$attr_prefix}video_height_phone" ] : '', 'computed_variables' => array( 'base_name' => $base_name, 'device' => 'phone', ), ); $video_backgrounds = array(); // Get video background markup. $background_video = ET_Builder_Element::get_video_background( $default_args ); if ( $background_video ) { $video_backgrounds['desktop'] = $background_video; } $background_video_hover = ET_Builder_Element::get_video_background( $hover_args ); if ( $background_video_hover ) { $video_backgrounds['hover'] = $background_video_hover; } $background_video_tablet = ET_Builder_Element::get_video_background( $tablet_args ); if ( $background_video_tablet ) { $video_backgrounds['tablet'] = $background_video_tablet; } $background_video_phone = ET_Builder_Element::get_video_background( $phone_args ); if ( $background_video_phone ) { $video_backgrounds['phone'] = $background_video_phone; } return $video_backgrounds; } /** * Returns the field original name by removing the `_tablet` or `_phone` suffix if it exists. * * Only remove tablet/phone string of the last setting name. Doesn't work for other format. * * @since 3.23 * * @param string $name Setting name. * @return string Base setting name. */ public function get_field_base_name( $name ) { // Do not use rtim as it removes by character not by string. So, cases like `key_tablets` // will be reduced to `key`. $regex = '/(.*)(_tablet|_phone)$/'; $replace = '${1}'; return preg_replace( $regex, $replace, $name ); } /** * Returns the field responsive name by adding the `_tablet` or `_phone` suffix if it exists. * * @since 3.27.4 * * @param string $name Setting name. * @param string $device Device name. * @return string Field setting name. */ public function get_field_name( $name, $device = 'desktop' ) { // Field name should not be empty. if ( empty( $name ) ) { return $name; } // Ensure device is not empty. $device = '' === $device ? 'desktop' : $device; // Get device name. return 'desktop' !== $device ? "{$name}_{$device}" : $name; } /** * Returns the device name by removing the `name` prefix. If the result is one of tablet or phone, * return it. But, if it's empty, return desktop. * * @since 3.23 * * @param string $name Setting name. * @return string Device name. */ public function get_device_name( $name ) { // Do not use rtim as it removes by character not by string. So, cases like `key_tablets` // will be reduced to `key`. $regex = '/(.*)(tablet|phone)$/'; $replace = '${2}'; $result = preg_replace( $regex, $replace, $name ); return in_array( $result, array( 'tablet', 'phone' ) ) ? $result : 'desktop'; } /** * Get responsive value based on field base name and device. * * NOTE: Function get_single_value() is different with get_any_value(). It will return only * current field value without checking the previous device value. * * For example: We have Title Text Font Size -> desktop 30px, tablet 10px, phone 10px. Fetch * the value for phone, it will return pure 10px even the value is same with tablet. * We have Subtitle Text Font Size -> desktop 20px, tablet 15px, phone ''. Fetch * the value for phone, it will return pure '' or default even the value is empty. * * To get tablet or phone value: * 1. You can pass only field base name and device name as the 4th argument. The parameters * structure it's made like that to make it similar with other get* method we already have. * For example: get_single_value( $this->props, 'title_text_font_size', '', 'tablet' ). * * 2. Or you can pass the actual field name with device. If the field name is already contains * _tablet and _phone, don't pass device parameter because it will be added as suffix. * For example: get_single_value( $this->props, 'title_text_font_size_tablet', '' ). * * @since 3.23 * * @param array $attrs All module attributes. * @param string $name Option name. * @param array $default_value Default value. * @param string $device Current device name. * @return mixed Current option value based on active device. */ public function get_single_value( $attrs, $name = '', $default_value = '', $device = 'desktop' ) { // Ensure $device is not empty. $device = '' === $device ? 'desktop' : $device; // Ensure always use device as suffix if device is not desktop or empty. if ( 'desktop' !== $device ) { $base_name = $this->get_field_base_name( $name ); $name = "{$base_name}_{$device}"; } return $this->get( $attrs, $name, $default_value, true ); } /** * Get current active device value from attributes. * * NOTE: Function get_any_value() is different with get_value(). It also compare the value * with the previous device value to avoid duplication. Or you can also force to return * either current or previous default value if needed. * * For example: We have Title Text Font Size -> desktop 30px, tablet 30px, phone 10px. When * we fetch the value for tablet, it will return pure empty string ('') because * tablet value is equal with desktop value. * * We have Title Text Font Size -> desktop 30px, tablet '', phone ''. When * we fetch the value for phone and force it to return any value, it will * return 30px because phone and tablet value is empty and the function will * look up to tablet or even desktop value. * * To get tablet or phone value: * 1. You can pass only field base name and device name as the 4th argument. The parameters * structure it's made like that to make it similar with other get* method we already have. * For example: get_any_value( $this->props, 'title_text_font_size', '', 'tablet' ). * * 2. Or you can pass the actual field name with device. If the field name is already contains * _tablet and _phone, don't pass device parameter because it will be added as suffix. * For example: get_any_value( $this->props, 'title_text_font_size_tablet', '' ). * * 3. You can also force to return any value by passing true on the 5th argument. In some cases * we need this to fill missing tablet/phone value with desktop value. * * @since 3.23 * * @param array $attrs All module attributes. * @param string $name Option name. * @param array $default_value Default value. * @param bool $force_return Force to return any value. * @param string $device Current device name. * @return mixed Current option value based on active device. */ public function get_any_value( $attrs, $name = '', $default_value = '', $force_return = false, $device = 'desktop' ) { // Ensure $device is not empty. $device = '' === $device ? 'desktop' : $device; // Ensure always use device as suffix if device is not desktop/empty. if ( 'desktop' !== $device ) { $base_name = $this->get_field_base_name( $name ); $name = "{$base_name}_{$device}"; } // Get current value. $current_value = $this->get( $attrs, $name, '' ); // Get previous value to be compared. $prev_value = $this->get_default_value( $attrs, $name, $default_value ); // Force to return any values given. if ( $force_return ) { return ! empty( $current_value ) ? $current_value : $prev_value; } // Ensure current value is different with the previous device or default. if ( $current_value === $prev_value ) { return ''; } return $current_value; } /** * Get property's values for requested device. * * This function is added to summarize how we fetch desktop/hover/tablet/phone value. This * function still uses get_any_value to get current device values. * * @since 3.23 * * @param array $attrs List of all attributes and values. * @param string $name Property name. * @param mixed $default_value Default value. * @param string $device Device name. * @param boolean $force_return Force to return any values found. * * @return array Pair of devices and the values. */ public function get_property_value( $attrs, $name, $default_value = '', $device = 'desktop', $force_return = false ) { // Default values. $default_value = esc_attr( $default_value ); // Ensure $device is not empty. $device = '' === $device ? 'desktop' : $device; // Ensure attrs (values list) and name (property name) are not empty. if ( empty( $attrs ) || '' === $name ) { return $default_value; } $is_enabled = 'desktop' !== $device ? $this->is_responsive_enabled( $attrs, $name ) : true; $suffix = 'desktop' !== $device ? "_{$device}" : ''; $value = $is_enabled ? $this->get_any_value( $attrs, "{$name}{$suffix}", $default_value, $force_return ) : $default_value; return esc_attr( $value ); } /** * Get all properties values for all devices. * * This function is added to summarize how we fetch desktop/hover, tablet, and phone values. This * function still use get_any_value to get current device values. * * @since 3.23 * * @param array $attrs List of all attributes and values. * @param string $name Property name. * @param mixed $default_value Default value. * @param boolean $force_return Force to return any values found. * * @return array Pair of devices and the values. */ public function get_property_values( $attrs, $name, $default_value = '', $force_return = false ) { // Default values. $default_value = esc_attr( $default_value ); $values = array( 'desktop' => $default_value, 'tablet' => $default_value, 'phone' => $default_value, ); // Ensure attrs (values list) and name (property name) are not empty. if ( empty( $attrs ) || '' === $name ) { return $values; } $is_responsive = $this->is_responsive_enabled( $attrs, $name ); // Get values for each devices. $values['desktop'] = esc_html( $this->get_any_value( $attrs, $name, $default_value, $force_return ) ); if ( $is_responsive ) { $values['tablet'] = esc_html( $this->get_any_value( $attrs, "{$name}_tablet", $default_value, $force_return ) ); $values['phone'] = esc_html( $this->get_any_value( $attrs, "{$name}_phone", $default_value, $force_return ) ); } return $values; } /** * Get property value after checking whether it uses responsive or not * * If responsive is used, automatically return array of all devices value. * If responsive is not used, return string of desktop value * * @since 4.6.0 * * @param array $attrs List of all attributes and values. * @param string $name Property name. * @param mixed $default_value Default value. * @param boolean $force_return Force to return any values found. * * @return string|array String if not responsive, Pair of devices and the values if responsive. */ public function get_checked_property_value( $attrs, $name, $default_value = '', $force_return = false ) { $is_responsive = $this->is_responsive_enabled( $attrs, $name ); return $is_responsive ? $this->get_property_values( $attrs, $name, $default_value, $force_return ) : $this->get_property_value( $attrs, $name, $default_value, 'desktop', $force_return ); } /** * Get composite property's value for requested device. * * This function is added to summarize how we fetch desktop/hover/tablet/phone value. This * function still uses get_any_value to get current device values. * * @since 3.27.4 * * @param array $attrs List of all attributes and values. * @param string $composite_name Composite property name. * @param string $name Property name. * @param mixed $default_value Default value. * @param string $device Device name. * @param boolean $force_return Force to return any values found. * * @return array Pair of devices and the values. */ public function get_composite_property_value( $attrs, $composite_name, $name, $default_value = '', $device = 'desktop', $force_return = false ) { // Default values. $default_value = esc_attr( $default_value ); // Ensure $device is not empty. $device = '' === $device ? 'desktop' : $device; // Ensure attrs, composite name (parent property name), name (property name) are not empty. if ( empty( $attrs ) || '' === $composite_name || '' === $name ) { return $default_value; } $is_enabled = 'desktop' !== $device ? $this->is_responsive_enabled( $attrs, $composite_name ) : true; $suffix = 'desktop' !== $device ? "_{$device}" : ''; $value = $is_enabled ? $this->get_any_value( $attrs, "{$name}{$suffix}", $default_value, $force_return ) : $default_value; return esc_attr( $value ); } /** * Get all composite properties values for all devices. * * This function is added to summarize how we fetch desktop/hover, tablet, and phone values. This * function still use get_any_value to get current device values. * * @since 3.27.4 * * @param array $attrs List of all attributes and values. * @param string $composite_name Composite property name. * @param string $name Property name. * @param mixed $default_value Default value. * @param boolean $force_return Force to return any values found. * * @return array Pair of devices and the values. */ public function get_composite_property_values( $attrs, $composite_name, $name, $default_value = '', $force_return = false ) { // Default values. $default_value = esc_attr( $default_value ); $values = array( 'desktop' => $default_value, 'tablet' => $default_value, 'phone' => $default_value, ); // Ensure attrs, composite name (parent property name), name (property name) are not empty. if ( empty( $attrs ) || '' === $composite_name || '' === $name ) { return $values; } $is_responsive = $this->is_responsive_enabled( $attrs, $composite_name ); // Get values for each devices. $values['desktop'] = esc_attr( $this->get_any_value( $attrs, $name, $default_value, $force_return ) ); if ( $is_responsive ) { $values['tablet'] = esc_attr( $this->get_any_value( $attrs, "{$name}_tablet", $default_value, $force_return ) ); $values['phone'] = esc_attr( $this->get_any_value( $attrs, "{$name}_phone", $default_value, $force_return ) ); } return $values; } /** * Get multiple attributes value from current active device. * * Basically, this function is combination of: * - Get any value of attribute * - Check attribute responsive status for tablet/phone * - Only send non empty attributes, except you force to return any given value * - Doing all of the process above for more than one fields * * @since 3.23 * * @param array $attrs All module attributes. * @param string $list List of options name. Name should be field base name. * @param bool $force_return Force to return any value. * @param string $device Current device name. * @return array All option values. */ public function get_any_responsive_values( $attrs, $list, $force_return = false, $device = 'desktop' ) { // Ensure list is not empty and valid array. if ( empty( $list ) || ! is_array( $list ) ) { return array(); } // Ensure device is not empty. $device = '' === $device ? 'desktop' : $device; // Fetch each attribute and store it in $values. $values = array(); foreach ( $list as $field_key => $field_value ) { // Check responsive status if current device is tablet or phone. if ( 'desktop' !== $device && ! $this->is_responsive_enabled( $attrs, $field_key ) ) { continue; } // Get value. $value = $this->get_any_value( $attrs, $field_key, $field_value, $force_return, $device ); // No need to save the value if it's empty and we don't force to return any value. if ( ! $force_return && empty( $value ) ) { continue; } $values[ $field_key ] = $value; } return $values; } /** * Get default value of active device. Mechanism: * * - Desktop => Return default value. * - Tablet => Return desktop value or default value. * - Phone => Return tablet value or desktop value or default value. * * @since 3.23 * * @param array $attrs All module attributes. * @param string $name Option name. * @param array $default_value All module advanced defaults. * @return mixed Previous option value based on active device. */ public function get_default_value( $attrs, $name = '', $default_value = '' ) { // Get option base name. $base_name = $this->get_field_base_name( $name ); $device = $this->get_device_name( $name ); // Get default value and return it for Desktop. if ( 'desktop' === $device ) { return $default_value; } // Get tablet value and return it for Tablet. $desktop_value = $this->get( $attrs, "{$base_name}", $default_value, true ); if ( 'tablet' === $device ) { return $desktop_value; } // Get phone value and return it for Phone. $tablet_value = $this->get( $attrs, "{$base_name}_tablet", $desktop_value, true ); if ( 'phone' === $device ) { return $tablet_value; } return $default_value; } /** * Returns responsive modes list from largest to narrow * * @return string[] */ public function get_modes() { return array( self::DESKTOP, self::TABLET, self::PHONE ); } /** * Returns next wider mode then provided * * @param $mode * * @return null|string */ public function get_wider_mode( $mode ) { $modes = $this->get_modes(); $key = array_search( $this->validate_mode( $mode ), $modes ); return false != $key ? et_()->array_get( $modes, '[' . ( -- $key ) . ']', null ) : null; } /** * Returns next narrower mode then provided * * @param $mode * * @return null|string */ public function get_narrower_mode( $mode ) { $modes = $this->get_modes(); $key = array_search( $this->validate_mode( $mode ), $modes ); return false !== $key && isset( $modes[ $key + 1 ] ) ? $modes[ $key + 1 ] : null; } /** * Return default responsive mode * * @return string */ public function get_default_mode() { return self::DESKTOP; } /** * Returns setting field name by responsive mode * * @param $setting * @param $mode * * @return string */ public function get_field( $setting, $mode ) { return $setting . $this->mode_field( (string) $this->validate_mode( $mode ) ); } /** * Returns setting field name of the last edited mode * * @param string $setting * * @return string */ public function get_last_edited_field( $setting ) { return "{$setting}_last_edited"; } /** * Checks if setting responsive mode is enabled * * @param $setting * @param $props * * @return bool */ public function is_enabled( $setting, $props ) { $value = et_builder_module_prop( $this->get_last_edited_field( $this->get_field_base_name( $setting ) ), $props, '' ); return et_pb_get_responsive_status( $value ); } /** * Returns the props value by mode * If no mode provided, the default mode is used * * @param $setting * @param $props * @param null $mode * @param string $default * * @return mixed */ public function get_value( $setting, $props, $mode = null, $default = '' ) { $mode = $this->get_mode_or_default( $mode ); if ( $this->get_default_mode() != $mode && ! $this->is_enabled( $setting, $props ) ) { return $default; } return et_builder_module_prop( $this->get_field( $setting, $mode ), $props, $default ); } /** * Is the implementation of get_value specifically for desktop mode * * Note: since the desktop mode is the default mode, * this method would similar to get_value without providing mode, * but can be used for a more explicit representation * * @param string $setting * @param array $props * @param mixed $default * * @return mixed */ public function get_desktop_value( $setting, $props, $default = null ) { return $this->get_value( $setting, $props, self::DESKTOP, $default ); } /** * Is the implementation of get_value specifically for tablet mode * * @param string $setting * @param array $props * @param mixed $default * * @return mixed */ public function get_tablet_value( $setting, $props, $default = null ) { return $this->get_value( $setting, $props, self::TABLET, $default ); } /** * Is the implementation of get_value specifically for phone mode * * @param string $setting * @param array $props * @param mixed $default * * @return mixed */ public function get_phone_value( $setting, $props, $default = null ) { return $this->get_value( $setting, $props, self::PHONE, $default ); } /** * Returns the last edited responsive mode of the provided setting * If not valid value is provided, default mode is returned * * @param $setting * @param $props * * @return string */ public function get_last_edited( $setting, $props ) { $value = et_builder_module_prop( $this->get_last_edited_field( $setting ), $props, '' ); $mode = et_()->array_get( explode( '|', $value ), '[1]' ); return $this->validate_mode( $mode ) ? $mode : $this->get_default_mode(); } /** * Get breakpoint minimum widths * * @since 4.3.2 * * @return array */ public function get_breakpoint_min_widths() { return array( 'desktop' => 980, 'tablet' => 768, 'phone' => 0, ); } /** * Get breakpoint based on device name. * * @since 4.0 * * @param string $device * * @return string */ public function get_breakpoint_by_device( $device = 'desktop' ) { switch ( $device ) { case 'desktop_only': return 'min_width_981'; case 'tablet': return 'max_width_980'; case 'tablet_only': return '768_980'; case 'desktop_tablet_only': return 'min_width_768'; case 'phone': return 'max_width_767'; default: return 'general'; } } /** * @param $mode * * @return bool|string */ protected function validate_mode( $mode ) { return in_array( strtolower( $mode ), $this->get_modes() ) ? strtolower( $mode ) : false; } protected function get_mode_or_default( $mode ) { return $this->validate_mode( $mode ) ? strtolower( $mode ) : $this->get_default_mode(); } /** * Returns mode suffix * The default mode suffix is empty * * @param $mode * * @return string */ protected function mode_field( $mode ) { switch ( $mode ) { case $this->get_default_mode(): return ''; default: return "_$mode"; } } /** * Generates the css code for responsive options. * * Uses array of values for each device as input parameter and css_selector with property to * apply the css. * * Copy of et_pb_generate_responsive_css() with some modifications to improve. * * @since 3.23 * * @param array $values_array All device values. * @param mixed $css_selector CSS selector. * @param string $css_property CSS property. * @param string $function_name Module slug. * @param string $additional_css Additional CSS. * @param string $type Value type to determine need filter or not. Previously, it only * accept value from range control and run a function to process * range value. * @param string $priority CSS style declaration priority. */ public function generate_responsive_css( $values_array, $css_selector, $css_property, $function_name, $additional_css = '', $type = 'range', $priority = '' ) { if ( empty( $values_array ) ) { return; } foreach ( $values_array as $device => $current_value ) { if ( empty( $current_value ) ) { continue; } // 1. Selector. // There are some cases where selector is an object contains specific selector for // each devices. $selector = $css_selector; if ( is_array( $css_selector ) ) { $selector = ! empty( $css_selector[ $device ] ) ? $css_selector[ $device ] : ''; } if ( empty( $selector ) ) { continue; } // 2. Declare CSS style. // There are some cases before we can declare the CSS style: // 1. The value is an array contains pair of properties and values. // 2. The value is single string but we have multiple properties exist. // 3. The value is single string with only one property. $declaration = ''; // Value can be provided as a string or array in following format: // array( // 'property_1' => 'value_1', 'property_2' => 'value_2', ... , // 'property_n' => 'value_n' // ) if ( is_array( $current_value ) ) { foreach ( $current_value as $this_property => $this_value ) { if ( empty( $this_property ) || '' === $this_value ) { continue; } // Get valid value. Previously, it only works for range control value and run // et_builder_process_range_value function directly. Keep it as it is now for // backward compatibility. $valid_value = $this_value; if ( 'range' === $type ) { $valid_value = et_builder_process_range_value( $this_value, $this_property ); } $declaration .= sprintf( '%1$s: %2$s%3$s', $this_property, esc_html( $valid_value ), '' !== $additional_css ? $additional_css : ';' ); } } elseif ( is_array( $css_property ) ) { // Get valid value. Previously, it only works for range control value and run // et_builder_process_range_value function directly. $valid_value = $current_value; foreach ( $css_property as $this_property ) { if ( empty( $this_property ) ) { continue; } if ( 'range' === $type ) { $valid_value = et_builder_process_range_value( $current_value, $this_property ); } $declaration .= sprintf( '%1$s: %2$s%3$s', $this_property, esc_html( $valid_value ), '' !== $additional_css ? $additional_css : ';' ); } } elseif ( ! empty( $css_property ) ) { // Get valid value. Previously, it only works for range control value and run // et_builder_process_range_value function directly. $valid_value = $current_value; if ( 'range' === $type ) { $valid_value = et_builder_process_range_value( $current_value, $css_property ); } $declaration = sprintf( '%1$s: %2$s%3$s', $css_property, esc_html( $valid_value ), '' !== $additional_css ? $additional_css : ';' ); } if ( '' === $declaration ) { continue; } $style = array( 'selector' => $selector, 'declaration' => $declaration, ); if ( 'desktop_only' === $device ) { $style['media_query'] = ET_Builder_Element::get_media_query( 'min_width_981' ); } elseif ( 'desktop' !== $device ) { $current_media_query = 'tablet' === $device ? 'max_width_980' : 'max_width_767'; $style['media_query'] = ET_Builder_Element::get_media_query( $current_media_query ); } // Priority. if ( '' !== $priority ) { $style['priority'] = $priority; } ET_Builder_Element::set_style( $function_name, $style ); } } /** * Generates the CSS code for responsive options based on existing declaration. * * Similar with generate_responsive_css(), but it's only used to declare the CSS and selector * without set styles on ET_Builder_Element. * * @since 3.23 * * @param array $values_array All device values. * @param mixed $css_selector CSS selector. * @param string $function_name Module slug. * @param string $priority CSS style declaration priority. */ public function declare_responsive_css( $values_array, $css_selector, $function_name, $priority = '' ) { if ( empty( $values_array ) ) { return; } foreach ( $values_array as $device => $declaration ) { if ( empty( $declaration ) ) { continue; } $style = array( 'selector' => $css_selector, 'declaration' => $declaration, ); // Media query. if ( 'desktop_only' === $device ) { $style['media_query'] = ET_Builder_Element::get_media_query( 'min_width_981' ); } elseif ( 'desktop' !== $device ) { $current_media_query = 'tablet' === $device ? 'max_width_980' : 'max_width_767'; $style['media_query'] = ET_Builder_Element::get_media_query( $current_media_query ); } // Priority. if ( '' !== $priority ) { $style['priority'] = $priority; } ET_Builder_Element::set_style( $function_name, $style ); } } /** * Generate additional responsive fields such as _tablet, _phone, and _last_edited. * * @since 3.23 * * @param string $field_name Base field name. * @param string $toggle_slug Toggle slug name. * @param string $tab_slug Tab slug name. * @param array $field Field data in array. * @return array Responsive fields set. */ public function generate_responsive_fields( $field_name, $toggle_slug, $tab_slug, $field = array() ) { $responsive_options = array(); // Add fields with responsive suffix for each devices. $responsive_options[ "{$field_name}_tablet" ] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); $responsive_options[ "{$field_name}_phone" ] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); $responsive_options[ "{$field_name}_last_edited" ] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); // Add computed effect field on mobile and hover fields. if ( ! empty( $field['computed_affects'] ) && isset( $field['affects_mobile'] ) && true === $field['affects_mobile'] ) { $responsive_options[ "{$field_name}_tablet" ]['computed_affects'] = $field['computed_affects']; $responsive_options[ "{$field_name}_phone" ]['computed_affects'] = $field['computed_affects']; $responsive_options[ "{$field_name}__hover" ] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, 'computed_affects' => $field['computed_affects'], ); } // Set default on tablet and phone if needed. if ( ! empty( $field['default_on_mobile'] ) ) { $default = ! empty( $field['default'] ) ? $field['default'] : ''; $default_mobile = ! empty( $field['default_on_mobile'] ) ? $field['default_on_mobile'] : $default; $default_tablet = ! empty( $field['default_on_tablet'] ) ? $field['default_on_tablet'] : $default_mobile; $default_phone = ! empty( $field['default_on_phone'] ) ? $field['default_on_phone'] : $default_mobile; $responsive_options[ "{$field_name}_tablet" ]['default'] = $default_tablet; $responsive_options[ "{$field_name}_phone" ]['default'] = $default_phone; } // Set default on hover if needed. if ( ! empty( $field['default_on_hover'] ) ) { $default = ! empty( $field['default'] ) ? $field['default'] : ''; $default_hover = ! empty( $field['default_on_hover'] ) ? $field['default_on_hover'] : $default_mobile; $responsive_options[ "{$field_name}__hover" ]['default'] = $default_hover; } return $responsive_options; } /** * Get main background value based on enabled status of current field. It's used to selectively * get the correct color, gradient status, image, and video. It's introduced along with new * enable fields to decide should we remove or inherit the value from larger device. * * @since 3.24.1 * * @param array $attrs All module attributes. * @param string $base_setting Setting need to be checked. * @param string $preview_mode Current preview mode. * @param string $background_base Background base name (background, button_bg, etc.) * @param array $fields All module fields definition. * @param string $value Active value. * @param string $default_value Active default value. * * @return string New value. */ public function get_inheritance_background_value( $attrs, $base_setting, $preview_mode, $background_base = 'background', $fields = array(), $value = '', $default_value = '' ) { // Default new value is same with the generated or active one. $new_value = $value; $enable_fields = array( "{$background_base}_color" => "{$background_base}_enable_color", 'use_background_color_gradient' => 'use_background_color_gradient', "{$background_base}_use_color_gradient" => "{$background_base}_use_color_gradient", "{$background_base}_image" => "{$background_base}_enable_image", "video_{$background_base}_values" => "video_{$background_base}_values", "{$background_base}_pattern_style" => "{$background_base}_enable_pattern_style", "{$background_base}_mask_style" => "{$background_base}_enable_mask_style", ); // Empty string is slug for desktop. $map_slugs = array( 'desktop' => array( '' ), 'hover' => array( '__hover', '' ), 'sticky' => array( '__sticky', '' ), 'tablet' => array( '_tablet', '' ), 'phone' => array( '_phone', '_tablet', '' ), ); // Start checking if current field is enabled or disabled. $base_enable_field_name = ! empty( $enable_fields[ $base_setting ] ) ? $enable_fields[ $base_setting ] : ''; // Bail early if setting name is different. if ( '' === $base_enable_field_name || ! isset( $map_slugs[ $preview_mode ] ) ) { return $new_value; } $new_value = ''; $origin_mp4_enabled = ''; $origin_mp4_data = array(); $origin_webm_enabled = ''; $origin_webm_data = array(); foreach ( $map_slugs[ $preview_mode ] as $slug ) { // BG Color, BG Image, BG Pattern, BG Mask. if ( in_array( $base_setting, array( "{$background_base}_color", "{$background_base}_image", "{$background_base}_pattern_style", "{$background_base}_mask_style", ), true ) ) { $base_type = str_replace( "{$background_base}_", '', $base_setting ); $enable_default = ! empty( $fields[ "{$background_base}_enable_{$base_type}{$slug}" ] ) && ! empty( $fields[ "{$background_base}_enable_{$base_type}{$slug}" ]['default'] ) ? $fields[ "{$background_base}_enable_{$base_type}{$slug}" ]['default'] : ''; $enable_value = ! empty( $attrs[ "{$background_base}_enable_{$base_type}{$slug}" ] ) ? $attrs[ "{$background_base}_enable_{$base_type}{$slug}" ] : $enable_default; $setting_value = ! empty( $attrs[ "{$background_base}_{$base_type}{$slug}" ] ) ? $attrs[ "{$background_base}_{$base_type}{$slug}" ] : ''; $is_tab_enabled = 'off' !== $enable_value; if ( '' !== $setting_value && $is_tab_enabled ) { $new_value = $setting_value; break; } elseif ( ! $is_tab_enabled ) { $new_value = ''; break; } // BG Gradient. } elseif ( in_array( $base_setting, array( 'use_background_color_gradient', "{$background_base}_use_color_gradient" ), true ) ) { $new_value = 'off'; $field_map = array( 'use_background_color_gradient' => array( 'value' => "use_background_color_gradient{$slug}", 'start' => "{$background_base}_color_gradient_start{$slug}", 'end' => "{$background_base}_color_gradient_end{$slug}", 'stops' => "{$background_base}_color_gradient_stops{$slug}", 'unit' => "{$background_base}_color_gradient_unit{$slug}", ), "{$background_base}_use_color_gradient" => array( 'value' => "{$background_base}_use_color_gradient{$slug}", 'start' => "{$background_base}_color_gradient_start{$slug}", 'end' => "{$background_base}_color_gradient_end{$slug}", 'stops' => "{$background_base}_color_gradient_stops{$slug}", 'unit' => "{$background_base}_color_gradient_unit{$slug}", ), ); $field_value = ''; $field_start = ''; $field_end = ''; $field_stops = ''; if ( ! empty( $field_map[ $base_setting ] ) ) { $field_value = ! empty( $field_map[ $base_setting ]['value'] ) ? $field_map[ $base_setting ]['value'] : ''; $field_start = ! empty( $field_map[ $base_setting ]['start'] ) ? $field_map[ $base_setting ]['start'] : ''; $field_end = ! empty( $field_map[ $base_setting ]['end'] ) ? $field_map[ $base_setting ]['end'] : ''; $field_stops = ! empty( $field_map[ $base_setting ]['stops'] ) ? $field_map[ $base_setting ]['stops'] : ''; } // Set value from attrs, otherwise, assign default value, for desktop/tablet/phone. $use_gradient_value = $this->get_any_value( $attrs, $field_value, 'off', true ); $gradient_start_value = $this->get_any_value( $attrs, $field_start, '', true ); $gradient_end_value = $this->get_any_value( $attrs, $field_end, '', true ); $gradient_stops_value = $this->get_any_value( $attrs, $field_stops, '', true ); // Set value from attrs, otherwise, assign value from desktop. if ( in_array( $slug, array( '__hover', '__sticky' ), true ) ) { $use_gradient_value = ! empty( $attrs[ $field_value ] ) ? $attrs[ $field_value ] : $use_gradient_value; $gradient_start_value = ! empty( $attrs[ $field_start ] ) ? $attrs[ $field_start ] : $gradient_start_value; $gradient_end_value = ! empty( $attrs[ $field_end ] ) ? $attrs[ $field_end ] : $gradient_end_value; $gradient_stops_value = ! empty( $attrs[ $field_stops ] ) ? $attrs[ $field_stops ] : $gradient_stops_value; } $is_gradient_enabled = 'off' !== $use_gradient_value; if ( ( '' !== $gradient_stops_value || ( '' !== $gradient_start_value || '' !== $gradient_end_value ) ) && $is_gradient_enabled ) { $new_value = 'on'; break; } elseif ( ! $is_gradient_enabled ) { $new_value = 'off'; break; } // BG Video. } elseif ( "video_{$background_base}_values" === $base_setting ) { $base_slug = preg_replace( '/[_]+/', '', $slug ); $current_mode = '' !== $base_slug ? $base_slug : 'desktop'; // Video markup. $video_background = et_()->array_get( $attrs, "{$base_setting}.{$current_mode}", '' ); // MP4. $enable_mp4_default = et_()->array_get( $fields, "{$background_base}_enable_video_mp4{$slug}", '' ); $enable_mp4_value = $this->get_any_value( $attrs, "{$background_base}_enable_video_mp4{$slug}", $enable_mp4_default, true ); $is_mp4_enabled = 'off' !== $enable_mp4_value; $video_mp4_value = et_pb_responsive_options()->get_any_value( $attrs, "{$background_base}_video_mp4" ); if ( 'hover' === $current_mode ) { $video_mp4_hover_value = et_()->array_get( $attrs, "{$background_base}_video_mp4__hover", '' ); $video_mp4_value = '' !== $video_mp4_hover_value ? $video_mp4_hover_value : $video_mp4_value; } else { $video_mp4_value = et_pb_responsive_options()->get_any_value( $attrs, "{$background_base}_video_mp4{$slug}", '', true ); } // Check MP4 enabled and data status. if ( '' === $origin_mp4_enabled ) { if ( '' !== $video_mp4_value && $is_mp4_enabled ) { $origin_mp4_enabled = 'enabled'; $origin_mp4_data = array( 'mode' => $current_mode, 'video_value' => $video_mp4_value, 'video_background' => $video_background, 'display' => ! empty( $video_background ) ? 'self' : 'inherit', ); } elseif ( false === $is_mp4_enabled ) { $origin_mp4_enabled = 'disabled'; $origin_mp4_data = array(); } } elseif ( 'enabled' === $origin_mp4_enabled ) { if ( isset( $origin_mp4_data['video_background'] ) && empty( $origin_mp4_data['video_background'] ) ) { $origin_mp4_data['video_background'] = $video_background; } } // Webm. $enable_webm_default = et_()->array_get( $fields, "{$background_base}_enable_video_webm{$slug}", '' ); $enable_webm_value = $this->get_any_value( $attrs, "{$background_base}_enable_video_webm{$slug}", $enable_webm_default, true ); $is_webm_enabled = 'off' !== $enable_webm_value; $video_webm_value = et_pb_responsive_options()->get_any_value( $attrs, "{$background_base}_video_webm" ); if ( 'hover' === $current_mode ) { $video_webm_hover_value = et_()->array_get( $attrs, "{$background_base}_video_webm__hover", '' ); $video_webm_value = '' !== $video_webm_hover_value ? $video_webm_hover_value : $enable_webm_value; } else { $video_webm_value = et_pb_responsive_options()->get_any_value( $attrs, "{$background_base}_video_webm{$slug}", '', true ); } // Check Webm enabled and data status. if ( '' === $origin_webm_enabled ) { if ( '' !== $video_webm_value && $is_webm_enabled ) { $origin_webm_enabled = 'enabled'; $origin_webm_data = array( 'mode' => $current_mode, 'video_value' => $video_webm_value, 'video_background' => $video_background, 'display' => ! empty( $video_background ) ? 'self' : 'inherit', ); } elseif ( ! $is_webm_enabled ) { $origin_webm_enabled = 'disabled'; $origin_webm_data = array(); } } elseif ( 'enabled' === $origin_webm_enabled ) { if ( isset( $origin_webm_data['video_background'] ) && empty( $origin_webm_data['video_background'] ) ) { $origin_webm_data['video_background'] = $video_background; } } // Continue if current mode is not desktop. if ( '' !== $slug ) { continue; } // Decide to display the video or not. if ( 'disabled' === $origin_mp4_enabled && 'disabled' === $origin_webm_enabled ) { $new_value = array( 'display' => 'hide', 'video' => '', ); } else { // MP4 display and video status. $mp4_enabled_display = et_()->array_get( $origin_mp4_data, 'display', '' ); $mp4_enabled_mode = et_()->array_get( $origin_mp4_data, 'mode', '' ); $mp4_video_background = ''; if ( $preview_mode === $mp4_enabled_mode ) { $mp4_video_background = et_()->array_get( $origin_mp4_data, 'video_background', '' ); } // Webm display and video status. $webm_enabled_display = et_()->array_get( $origin_webm_data, 'display', '' ); $webm_enabled_mode = et_()->array_get( $origin_webm_data, 'mode', '' ); $webm_video_background = ''; if ( $preview_mode === $webm_enabled_mode ) { $webm_video_background = et_()->array_get( $origin_webm_data, 'video_background', '' ); } // Set display current video or not. $new_video_display = 'hide'; if ( '' !== $mp4_enabled_display ) { $new_video_display = $mp4_enabled_display; } elseif ( '' !== $webm_enabled_display ) { $new_video_display = $webm_enabled_display; } // Set video markup. $new_video_background = ''; if ( '' !== $mp4_video_background ) { $new_video_background = $mp4_video_background; } elseif ( '' !== $webm_video_background ) { $new_video_background = $webm_video_background; } $new_value = array( 'display' => $new_video_display, 'video' => $new_video_background, ); } } } return $new_value; } public function create( $additional_options ) { // Add hover field indication $additional_options['hover_enabled'] = array( 'type' => 'skip', 'default' => 0, ); // Generate responsive fields for additional options (Design). // There are 4 types where the mobile_options exist on the options. // 1. Exist on the option definition. // 2. Exist on the computed field type, just like point 1 but we threat it differently // because there are some properties need to be updated and added. // 3. Exist on the background-field. // 4. Exist on the composite field. foreach ( $additional_options as $field_name => $field ) { $is_mobile_options = isset( $field['mobile_options'] ) && $field['mobile_options']; $is_hover = isset( $field['hover'] ) && 'tabs' === $field['hover']; $field_type = isset( $field['type'] ) ? $field['type'] : ''; $field_context = isset( $field['context'] ) ? $field['context'] : ''; $field_last_edited = isset( $field['last_edited'] ) ? $field['last_edited'] : ''; // Mobile options property maybe exist on the field. if ( $is_mobile_options ) { // Get tab and toggle slugs value. $tab_slug = isset( $field['tab_slug'] ) ? $field['tab_slug'] : ''; $toggle_slug = isset( $field['toggle_slug'] ) ? $field['toggle_slug'] : ''; // 2. Mobile options property for computed fields. if ( 'computed' === $field_type ) { // Computed depends on. Add suffix after depends on info. if ( ! empty( $field['computed_depends_on'] ) ) { $computed_depends_on = $field['computed_depends_on']; foreach ( $computed_depends_on as $depends_value ) { if ( $is_hover ) { array_push( $field['computed_depends_on'], "{$depends_value}_tablet", "{$depends_value}_phone", "{$depends_value}__hover" ); } else { array_push( $field['computed_depends_on'], "{$depends_value}_tablet", "{$depends_value}_phone" ); } } } // Computed minimum. Add suffix after minimum info. if ( ! empty( $field['computed_minimum'] ) ) { $computed_minimum = $field['computed_minimum']; foreach ( $computed_minimum as $minimum_value ) { if ( $is_hover ) { array_push( $field['computed_minimum'], "{$minimum_value}_tablet", "{$minimum_value}_phone", "{$minimum_value}__hover" ); } else { array_push( $field['computed_minimum'], "{$minimum_value}_tablet", "{$minimum_value}_phone" ); } } } $additional_options[ "{$field_name}" ] = $field; continue; } // 3. Mobile options property maybe exist under background field. if ( 'background-field' === $field_type ) { // Just in case current field is background-field and the mobile_options // attributes are located in the fields. Ensure background fields is exist. if ( ! empty( $field['background_fields'] ) ) { // Fetch the fields and check for mobile_options. foreach ( $field['background_fields'] as $background_name => $background_field ) { if ( isset( $background_field['mobile_options'] ) && $background_field['mobile_options'] ) { // Get tab and toggle slugs value. $tab_slug = isset( $background_field['tab_slug'] ) ? $background_field['tab_slug'] : ''; $toggle_slug = isset( $background_field['toggle_slug'] ) ? $background_field['toggle_slug'] : ''; // Add fields with responsive suffix for each devices. $additional_options = array_merge( $additional_options, et_pb_responsive_options()->generate_responsive_fields( $background_name, $toggle_slug, $tab_slug ) ); } } } continue; } // 1. Mobile options property added directly on options definition. Add fields // with responsive suffix for each devices. $additional_options = array_merge( $additional_options, et_pb_responsive_options()->generate_responsive_fields( $field_name, $toggle_slug, $tab_slug, $field ) ); // Additional last edited field just in case we need more last edited field. if ( ! empty( $field_last_edited ) ) { $additional_options[ "{$field_last_edited}_last_edited" ] = array( 'type' => 'skip', 'tab_slug' => $tab_slug, 'toggle_slug' => $toggle_slug, ); } continue; } // 4. Mobile options property maybe exist under composite field. if ( 'composite' === $field_type ) { // Just in case current field is composite and the mobile_options attributes // are located in the controls. Ensure composite structure is exist. $composite_structure = isset( $field['composite_structure'] ) ? $field['composite_structure'] : array(); if ( empty( $composite_structure ) ) { continue; } foreach ( $composite_structure as $composite_field ) { // Ensure composite field controls is exist and not empty. $composite_field_controls = isset( $composite_field['controls'] ) ? $composite_field['controls'] : array(); if ( empty( $composite_field_controls ) ) { continue; } // Fetch the controls and check for mobile_options. foreach ( $composite_field_controls as $control_name => $control ) { if ( isset( $control['mobile_options'] ) && $control['mobile_options'] ) { // Get tab and toggle slugs value. $tab_slug = isset( $control['tab_slug'] ) ? $control['tab_slug'] : ''; $toggle_slug = isset( $control['toggle_slug'] ) ? $control['toggle_slug'] : ''; // Add fields with responsive suffix for each devices. $additional_options = array_merge( $additional_options, et_pb_responsive_options()->generate_responsive_fields( $control_name, $toggle_slug, $tab_slug ) ); } } } } } return $additional_options; } }