<?php

if ( ! function_exists( 'et_core_data_init' ) ):
function et_core_data_init() {}
endif;


if ( ! function_exists( 'et_' ) ):
function et_() {
	global $et_;

	if ( ! $et_ ) {
		$et_ = ET_Core_Data_Utils::instance();
	}

	return $et_;
}
endif;


if ( ! function_exists( 'et_html_attr' ) ):
/**
 * Generates a properly escaped attribute string.
 *
 * @param string $name         The attribute name.
 * @param string $value        The attribute value.
 * @param bool   $space_before Whether or not the result should start with a space. Default is `true`.
 *
 * @return string
 */
function et_html_attr( $name, $value, $space_before = true ) {
	$result = ' ' . esc_attr( $name ) . '="' . esc_attr( $value ) . '"';

	return $space_before ? $result : trim( $result );
}
endif;

if ( ! function_exists( 'et_html_attrs' ) ):
/**
 * Generate properly escaped attributes string
 *
 * @since 3.10
 *
 * @param array $attributes Array of attributes
 *
 * @return string
 */
function et_html_attrs( $attributes = array() ) {
	$output = '';

	foreach ( $attributes as $name => $value ) {
		$parsed_value = is_array( $value ) ? implode( ' ', $value ) : $value;

		$output .= et_html_attr( $name, $parsed_value );
	}

	return $output;
}
endif;


if ( ! function_exists( 'et_sanitized_previously' ) ):
/**
 * Semantical previously sanitized acknowledgement
 *
 * @deprecated {@see et_core_sanitized_previously()}
 *
 * @since 3.17.3 Deprecated
 *
 * @param mixed $value The value being passed-through
 *
 * @return mixed
 */
function et_sanitized_previously( $value ) {
	et_debug( "You're Doing It Wrong! Attempted to call " . __FUNCTION__ . "(), use et_core_sanitized_previously() instead." );
	return $value;
}
endif;

if ( ! function_exists( 'et_core_sanitized_previously' ) ):
/**
 * Semantical previously sanitized acknowledgement
 *
 * @since 3.17.3
 *
 * @param mixed $value The value being passed-through
 *
 * @return mixed
 */
function et_core_sanitized_previously( $value ) {
	return $value;
}
endif;

if ( ! function_exists( 'et_core_esc_image_url' ) ) :
	/**
	 * Escape image URL
	 *
	 * @since 4.27.1
	 *
	 * @param string $url Image URL.
	 *
	 * @return string
	 */
	function et_core_esc_image_url( $url ) {
		return esc_url( $url, array( 'http', 'https', 'data' ) );
	}
endif;

if ( ! function_exists( 'et_core_esc_attr' ) ):
/**
 * Escape attribute value
 *
 * @since 3.27.1?
 *
 * @param string $attr_key Element attribute key.
 * @param (string|array) $attr_value Element attribute value.
 *
 * @return (string|array|WP_Error)
 */
function et_core_esc_attr( $attr_key, $attr_value ) {
	$attr_key = strtolower( $attr_key );

	$allowed_attrs_default = array(
		// Filter style.
		// @see https://developer.wordpress.org/reference/functions/safecss_filter_attr/
		'style'       => 'safecss_filter_attr',

		// We just pick some of the HTML attributes considered safe to use.
		'data-*'      => 'esc_attr',
		'align'       => 'esc_attr',
		'alt'         => 'esc_attr',
		'autofocus'   => 'esc_attr',
		'autoplay'    => 'esc_attr',
		'class'       => 'esc_attr',
		'cols'        => 'esc_attr',
		'controls'    => 'esc_attr',
		'disabled'    => 'esc_attr',
		'height'      => 'esc_attr',
		'id'          => 'esc_attr',
		'max'         => 'esc_attr',
		'min'         => 'esc_attr',
		'multiple'    => 'esc_attr',
		'name'        => 'esc_attr',
		'placeholder' => 'esc_attr',
		'required'    => 'esc_attr',
		'rows'        => 'esc_attr',
		'size'        => 'esc_attr',
		'sizes'       => 'esc_attr',
		'srcset'      => 'esc_attr',
		'step'        => 'esc_attr',
		'tabindex'    => 'esc_attr',
		'target'      => 'esc_attr',
		'title'       => 'esc_attr',
		'type'        => 'esc_attr',
		'value'       => 'esc_attr',
		'width'       => 'esc_attr',

		// We just pick some of the HTML attributes containing a URL.
		// @see https://developer.wordpress.org/reference/functions/wp_kses_uri_attributes/
		'action'      => 'esc_url',
		'background'  => 'et_core_esc_image_url',
		'formaction'  => 'esc_url',
		'href'        => 'esc_url',
		'icon'        => 'et_core_esc_image_url',
		'poster'      => 'et_core_esc_image_url',
		'src'         => 'et_core_esc_image_url',
		'usemap'      => 'esc_url',
	);

	/**
	 * Filters allowed attributes
	 *
	 * @since 3.27.1?
	 *
	 * @param array  $allowed_attrs_default Key/value paired array, the key used as the attribute identifier, 
	 *                                      the value used as the callback to escape the value.
	 * @param string $attr_key              Element attribute key.
	 * @param (string|array) $attr_value    Element attribute value.
	 *
	 * @return array
	 */
	$allowed_attrs = apply_filters( 'et_core_esc_attr', $allowed_attrs_default, $attr_key, $attr_value );

	// Get attribute key callback.
	$callback = isset( $allowed_attrs[ $attr_key ] ) && 'data-*' !== $attr_key ? $allowed_attrs[ $attr_key ] : false;

	// Get data attribute key callback.
	if ( ! $callback && 'data-*' !== $attr_key && 0 === strpos( $attr_key, 'data-' ) && isset( $allowed_attrs['data-*'] ) ) {
		$callback = $allowed_attrs['data-*'];
	}

	if ( ! $callback || ! is_callable( $callback ) ) {
		return new WP_Error( 'invalid_attr_key', __( 'Invalid attribute key', 'et_builder' ) );
	}

	if ( is_array( $attr_value ) ) {
		return array_map( $callback, $attr_value );
	}

	// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
	return call_user_func( $callback, $attr_value );
}
endif;

if ( ! function_exists( 'et_core_sanitize_element_tag' ) ):
/**
 * Sanitize element tag
 *
 * @since 3.27.1?
 *
 * @param string $tag Element tag.
 *
 * @return (string|WP_Error)
 */
function et_core_sanitize_element_tag( $tag ) {
	$tag      = strtolower( $tag );
	$headings = array(
		'h1',
		'h2',
		'h3',
		'h4',
		'h5',
		'h6',
	);

	// Bail early for heading tags.
	if ( in_array( $tag, $headings, true ) ) {
		return $tag;
	}

	$tag = preg_replace( '/[^a-z]/', '', $tag );

	$disallowed_tags = array(
		'applet',
		'body',
		'canvas',
		'command',
		'content',
		'element',
		'embed',
		'frame',
		'frameset',
		'head',
		'html',
		'iframe',
		'noembed',
		'noframes',
		'noscript',
		'object',
		'param',
		'script',
		'shadow',
		'slot',
		'style',
		'template',
		'title',
		'xml',
	);

	if ( empty( $tag ) || in_array( $tag, $disallowed_tags, true ) ) {
		return new WP_Error( 'invalid_element_tag', __( 'Invalid tag element', 'et_builder' ) );
	}

	return $tag;
}
endif;

/**
 * Pass thru semantical previously escaped acknowledgement
 *
 * @since 3.17.3
 *
 * @param string value being passed through
 * @return string
 */
if ( ! function_exists( 'et_core_esc_previously' ) ) :
function et_core_esc_previously( $passthru ) {
	return $passthru;
}
endif;

/**
 * Pass thru function used to pacfify phpcs sniff.
 * Used when the nonce is checked elsewhere.
 *
 * @since 3.17.3
 *
 * @return void
 */
if ( ! function_exists( 'et_core_nonce_verified_previously' ) ) :
function et_core_nonce_verified_previously() {
	// :)
}
endif;

/**
 * Pass thru semantical escaped by WordPress core acknowledgement
 *
 * @since 3.17.3
 *
 * @param string value being passed through
 * @return string
 */
if ( ! function_exists( 'et_core_esc_wp' ) ) :
function et_core_esc_wp( $passthru ) {
	return $passthru;
}
endif;

/**
 * Pass thru semantical intentionally unescaped acknowledgement
 *
 * @since 3.17.3
 *
 * @param string value being passed through
 * @param string excuse the value is allowed to be unescaped
 * @return string
 */
if ( ! function_exists( 'et_core_intentionally_unescaped' ) ) :
function et_core_intentionally_unescaped( $passthru, $excuse ) {
	// Add valid excuses as they arise
	$valid_excuses = array(
		'cap_based_sanitized',
		'fixed_string',
		'react_jsx',
		'html',
		'underscore_template',
	);

	if ( ! in_array( $excuse, $valid_excuses ) ) {
		_doing_it_wrong( __FUNCTION__, esc_html__( 'This is not a valid excuse to not escape the passed value.', 'et_core' ), esc_html( et_get_theme_version() ) );
	}

	return $passthru;
}
endif;

/**
 * Sanitize value depending on user capability
 *
 * @since 3.17.3
 *
 * @return string value being passed through
 */
if ( ! function_exists( 'et_core_sanitize_value_by_cap' ) ) :
function et_core_sanitize_value_by_cap( $passthru, $sanitize_function = 'et_sanitize_html_input_text', $cap = 'unfiltered_html' ) {
	if ( ! current_user_can( $cap ) ) {
		$passthru = $sanitize_function( $passthru );
	}

	return $passthru;
}
endif;

if ( ! function_exists( 'et_core_sanitize_custom_css_meta_value' ) ) :
	/**
	 * Sanitize Custom CSS meta value while preserving escape sequences.
	 *
	 * @since 5.0.0
	 *
	 * @param mixed $value Custom CSS meta value.
	 *
	 * @return mixed
	 */
	function et_core_sanitize_custom_css_meta_value( $value ) {
		if ( current_user_can( 'unfiltered_html' ) ) {
			return $value;
		}

		$sanitize_css_value = static function ( $css_value ) use ( &$sanitize_css_value ) {
			if ( is_array( $css_value ) ) {
				$sanitized_value = [];

				foreach ( $css_value as $index => $nested_value ) {
					$sanitized_value[ $index ] = $sanitize_css_value( $nested_value );
				}

				return $sanitized_value;
			}

			return is_string( $css_value ) ? wp_kses_post( $css_value ) : $css_value;
		};

		return $sanitize_css_value( $value );
	}
endif;

/**
 * Pass thru semantical intentionally unsanitized acknowledgement
 *
 * @since 3.17.3
 *
 * @param string value being passed through
 * @param string excuse the value is allowed to be unsanitized
 * @return string
 */
if ( ! function_exists( 'et_core_intentionally_unsanitized' ) ) :
function et_core_intentionally_unsanitized( $passthru, $excuse ) {
	// Add valid excuses as they arise
	$valid_excuses = array();

	if ( ! in_array( $excuse, $valid_excuses ) ) {
		_doing_it_wrong( __FUNCTION__, esc_html__( 'This is not a valid excuse to not sanitize the passed value.', 'et_core' ), esc_html( et_get_theme_version() ) );
	}

	return $passthru;
}
endif;

/**
 * Fixes unclosed HTML tags
 *
 * @since 3.18.4
 *
 * @param string $content source HTML
 *
 * @return string
 */
if ( ! function_exists( 'et_core_fix_unclosed_html_tags' ) ):
function et_core_fix_unclosed_html_tags( $content ) {
	// Exit if source has no HTML tags or we miss what we need to fix them anyway.
	if ( false === strpos( $content, '<' ) || ! class_exists( 'DOMDocument' ) ) {
		return $content;
	}

	$scripts = false;

	if ( false !== strpos( $content, '<script' ) ) {
		// Replace scripts with placeholders so we don't mess with HTML included in JS strings.
		$scripts = new ET_Core_Data_ScriptReplacer();
		$content = preg_replace_callback( '|<script.*?>[\s\S]+?</script>|', array( $scripts, 'replace' ), $content );
	}

	$doc = new DOMDocument();
	@$doc->loadHTML( sprintf(
		'<html><head>%s</head><body>%s</body></html>',
		// Use WP charset
		sprintf( '<meta http-equiv="content-type" content="text/html; charset=%s" />', get_bloginfo( 'charset' ) ),
		$content
	) );

	if ( preg_match( '|<body>([\s\S]+)</body>|', $doc->saveHTML(), $matches ) ) {
		// Extract the fixed content.
		$content = $matches[1];
	}

	if ( $scripts ) {
		// Replace placeholders with scripts.
		$content = strtr( $content, $scripts->map() );
	}

	return $content;
}
endif;


/**
 * Converts string to UTF-8 if mb_convert_encoding function exists
 *
 * @since 3.19.17
 *
 * @param string $string source string
 *
 * @return string
 */
if ( ! function_exists( 'et_core_maybe_convert_to_utf_8' ) ):
function et_core_maybe_convert_to_utf_8( $string ) {
	if ( function_exists( 'mb_convert_encoding' ) ) {
		return mb_convert_encoding( $string, 'UTF-8' );
	}

	return $string;
}
endif;
