HEX
Server: LiteSpeed
System: Linux server.nevid-deploma.com 4.18.0-553.111.1.lve.el8.x86_64 #1 SMP Fri Mar 13 13:42:17 UTC 2026 x86_64
User: smilepac (1037)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: //proc/thread-self/cwd/wp-content/plugins/codevz-plus/classes/class-woocommerce.php
<?php if ( ! defined( 'ABSPATH' ) ) {exit;} // Exit if accessed directly.

/**
 * Woocommerce functions.
 */

// AJAX filters for sidebar widgets.
// Insert nonce into filters div
function codevz_insert_selected_filters_div() {
	echo '<div class="codevz-plus-shop-ajax-filters" data-nonce="' . esc_attr(wp_create_nonce('codevz_plus_shop_ajax_nonce')) . '"><i class="fa czico-Icon-Action-Filter"></i><div></div></div>';
}
//add_action('woocommerce_before_shop_loop', 'codevz_insert_selected_filters_div', 100);
//add_action('wp_ajax_filter_products_by_price', 'ajax_filter_products_by_price');
//add_action('wp_ajax_nopriv_filter_products_by_price', 'ajax_filter_products_by_price');

// AJAX handler for price filter
function ajax_filter_products_by_price() {

	// Nonce verification
	if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'codevz_plus_shop_ajax_nonce')) {
		wp_send_json_error(['error' => 'Invalid security token.'], 403);
	}

	// Sanitize input
	$min_price = floatval($_POST['min_price']);
	$max_price = floatval($_POST['max_price']);
	$paged     = max(1, intval($_POST['page'] ? $_POST['page'] : 1));
	$per_page  = 12;

	// Query products
	$args = [
		'post_type'      => 'product',
		'post_status'    => 'publish',
		'posts_per_page' => $per_page,
		'offset'         => ($paged - 1) * $per_page,
		'meta_query'     => [
			[
				'key'     => '_price',
				'value'   => [$min_price, $max_price],
				'compare' => 'BETWEEN',
				'type'    => 'NUMERIC',
			],
		],
		'fields' => 'ids',
	];

	$product_ids = get_posts($args);

	// Pagination
	$total = count(get_posts(array_merge($args, ['posts_per_page' => -1])));
	$max_pages = ceil($total / $per_page);

	// Output product list
	ob_start();
	foreach ($product_ids as $id) {
		setup_postdata($post = get_post($id));
		wc_get_template_part('content', 'product');
	}
	$products_html = ob_get_clean();
	wp_reset_postdata();

	// Output pagination
	$pagination_html = '';
	if ($max_pages > 1) {
		$pagination_html = '<nav class="woocommerce-pagination"><ul class="page-numbers">';
		for ($i = 1; $i <= $max_pages; $i++) {
			$pagination_html .= sprintf(
				'<li><a class="page-numbers%s" href="#" data-page="%d">%d</a></li>',
				$i === $paged ? ' current' : '', $i, $i
			);
		}
		$pagination_html .= '</ul></nav>';
	}

	// Send response
	wp_send_json([
		'products'   => $products_html,
		'pagination' => $pagination_html,
	]);
}




add_action( 'woocommerce_single_product_summary', 'bz_nav_static_preview', 1 );
function bz_nav_static_preview() {

	if ( ! Codevz_Plus::option( 'woo_product_prev_next_nav' ) ) {
		return;
	}

	$prev = get_previous_post( true, '', 'product_cat' );
	if ( ! $prev ) {
		$prev = get_previous_post( false, '', 'product_cat' );
	}
	$next = get_next_post( true, '', 'product_cat' );
	if ( ! $next ) {
		$next = get_next_post( false, '', 'product_cat' );
	}
	?>
	<div class="codevz-woo-prod-nav">

		<?php if ( $prev ) :
			$p = wc_get_product( $prev->ID ); ?>
			<div class="codevz-woo-nav-item">
				<a href="<?php echo get_permalink( $prev->ID ); ?>" class="codevz-woo-prev">
					<i class="fa fa-angle-left"></i>
				</a>
				<div class="codevz-woo-preview-box">
					<a href="<?php echo get_permalink( $prev->ID ); ?>">
						<?php echo $p->get_image('woocommerce_thumbnail'); ?>
						<div>
							<h4><?php echo $p->get_name(); ?></h4>
							<span><?php echo ( $p->get_price_html() ?: esc_html__( 'Sold out', 'codevz-plus' ) ); ?></span>
						</div>
					</a>
				</div>
			</div>
		<?php endif; ?>

		<div class="codevz-woo-nav-item cz_tooltip_up">
			<a href="<?php echo esc_url( get_post_type_archive_link( 'product' ) ); ?>" class="codevz-woo-archive" data-title="<?php esc_html_e( 'Return to products', 'codevz-plus' ); ?>">
				<i class="fa czico-120-layout"></i>
			</a>
		</div>

		<?php if ( $next ) :
			$p = wc_get_product( $next->ID ); ?>
			<div class="codevz-woo-nav-item">
				<a href="<?php echo get_permalink( $next->ID ); ?>" class="codevz-woo-next">
					<i class="fa fa-angle-right"></i>
				</a>
				<div class="codevz-woo-preview-box">
					<a href="<?php echo get_permalink( $next->ID ); ?>">
						<?php echo $p->get_image('woocommerce_thumbnail'); ?>
						<div>
							<h4><?php echo $p->get_name(); ?></h4>
							<span><?php echo ( $p->get_price_html() ?: esc_html__( 'Sold out', 'codevz-plus' ) ); ?></span>
						</div>
					</a>
				</div>
			</div>
		<?php endif; ?>

<style>
.codevz-woo-prod-nav {
	display: flex;
	align-items: center;
	gap: 15px;
	margin: 8px;
	float: right
}	
.rtl .codevz-woo-prod-nav {
	flex-direction: row-reverse
}
.cz_popup_in .codevz-woo-prod-nav {
	display: none !important
}
.rtl .codevz-woo-prod-nav {
	float: left
}
.codevz-woo-nav-item {
    position:relative
}
.codevz-woo-nav-item a {
	opacity: .6;
    font-size: 20px;
    display:inline-block
}
.codevz-woo-nav-item a:hover {
	opacity: 1
}
.codevz-woo-preview-box {
    position: absolute;
    top: 40px;
    left: 50%;
    transform:translateX(-50%);
    background: #fff;
    border: 1px solid #67676726;
    border-radius: 4px;
    padding: 15px;
    width: 200px;
    z-index: 99;
    opacity: 0;
    visibility: hidden;
    transition: .3s ease-in-out;
    box-shadow: 0 4px 20px rgba(0,0,0,0.09)
}
.codevz-woo-nav-item:hover > .codevz-woo-preview-box,
.codevz-woo-nav-item:hover > .codevz-woo-preview-box a {
    opacity: 1;
    visibility: visible
}
.codevz-woo-preview-box a {
	display: flex;
	align-items: center;
	gap: 15px;
	color: #333
}
.codevz-woo-preview-box > a > div {
	display: flex;
	flex-direction: column
}
.codevz-woo-preview-box img {
	width: 64px;
	margin: 0
}
.codevz-woo-preview-box h4 {
	color: inherit;
	font-size:16px;
	margin: 0;
	overflow: hidden;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2; 
	-webkit-box-orient: vertical
}
.codevz-woo-preview-box span {
	color: inherit;
	font-size: 14px;
	font-weight: 600;
	opacity: .7
}
</style>

	</div>
	<?php
}

// Quantity +/-
add_action( 'woocommerce_before_quantity_input_field', function () {
	echo '<div class="quantity-down" aria-label="Decrease quantity">-</div>';
});
add_action( 'woocommerce_after_quantity_input_field', function () {
	echo '<div class="quantity-up" aria-label="Increase quantity">+</div>';
});

// AJAX add to cart single page.
function handle_single_add_to_cart() {
    check_ajax_referer('codevz_woo_nonce', 'nonce');

    $product_id = apply_filters('woocommerce_add_to_cart_product_id', absint($_POST['product_id']));
    $quantity = empty($_POST['quantity']) ? 1 : wc_stock_amount($_POST['quantity']);
    $variation_id = isset($_POST['variation_id']) ? absint($_POST['variation_id']) : 0;
    $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity);
    $product_status = get_post_status($product_id);

    if ($passed_validation && WC()->cart->add_to_cart($product_id, $quantity, $variation_id) && 'publish' === $product_status) {
        do_action('woocommerce_ajax_added_to_cart', $product_id);
        if ('yes' === get_option('woocommerce_cart_redirect_after_add')) {
            wc_add_to_cart_message(array($product_id => $quantity), true);
        }
        WC_AJAX::get_refreshed_fragments();
    } else {
        $data = array(
            'error' => true,
            'product_url' => apply_filters('woocommerce_cart_redirect_after_error', get_permalink($product_id), $product_id)
        );
        wp_send_json($data);
    }
    wp_die();
}
add_action('wp_ajax_single_add_to_cart', 'handle_single_add_to_cart');
add_action('wp_ajax_nopriv_single_add_to_cart', 'handle_single_add_to_cart');

// Remove item AJAX.
function handle_single_remove_from_cart() {
    check_ajax_referer('codevz_woo_nonce', 'nonce');

    $cart_item_key = isset($_POST['remove_item']) ? sanitize_text_field(wp_unslash($_POST['remove_item'])) : '';

    if ($cart_item_key && WC()->cart->remove_cart_item($cart_item_key)) {
        WC_AJAX::get_refreshed_fragments();
    } else {
        wp_send_json_error();
    }
    wp_die();
}
add_action('wp_ajax_single_add_to_cart_remove', 'handle_single_remove_from_cart');
add_action('wp_ajax_nopriv_single_add_to_cart_remove', 'handle_single_remove_from_cart');


// Add extra class to woo list widgets.
function xtra_add_class_to_wc_widgets($params) {

	$widget_id = isset( $params[0]['widget_id'] ) ? $params[0]['widget_id'] : false;

	if ( $widget_id && Codevz_Plus::contains( $widget_id, [ '_layered_nav', '_product_categories' ] ) ) {
		$params[0]['before_widget'] = str_replace(
			'class="',
			'class="widget_categories ',
			$params[0]['before_widget']
		);
	}

	return $params;
}
add_filter('dynamic_sidebar_params', 'xtra_add_class_to_wc_widgets');

function codevz_custom_rating_output( $rating_html, $rating, $count ) {

    if ( is_product() ) {

        $average = number_format( (float) $rating, 1 );

        $before = '<span class="codevz-woo-average-rating" style="font-size:16px; padding:4px 8px; display:block; border:1px solid #68676730; border-radius:4px; line-height:1; font-weight:bold; font-family:sans-serif; opacity:.7;">';
        $before .= esc_html( $average );
        $before .= '</span>';

        $after = '<span href="#tab-reviews" class="codevz-woo-rating-count" style="position:relative;bottom:1px;font-size:13px;opacity:.5;cursor:pointer">(';
        $after .= intval( $count ) . ' ' . esc_html__( 'Rating', 'codevz-plus' ) . ')</span>';
        $after .= '<style>.cz_popup_in .codevz-woo-rating-count{pointer-events: none}li.product .codevz-woo-rating-count,.comment-text .codevz-woo-rating-count{display: none}</style>';

        $rating_html = $before . $rating_html . $after;
    }

    return $rating_html;
}
add_filter( 'woocommerce_product_get_rating_html', 'codevz_custom_rating_output', 10, 3 );



add_action('woocommerce_before_shop_loop', 'custom_shop_category_bar', 9999 );
function custom_shop_category_bar() {

	$wcb = Codevz_Plus::option( 'woo_categories_bar' );

	if ( ! $wcb || is_search() ) {
		return false;
	}

	// Get categories.
	$categories = get_terms( [ 'taxonomy' => 'product_cat', 'hide_empty' => true, 'orderby' => 'count', 'order' => 'DESC', 'number' => 35 ] );

	if ( empty( $categories ) || is_wp_error( $categories ) ) {
		return;
	}

	// Start categories bar.
	echo '<div class="clr"></div>';
	echo '<div class="codevz-wcb codevz-wcb-' . esc_attr( $wcb ) . ' clr">';
	echo '<div class="codevz-wcb-inner clr">';

	// Translate.
	if ( is_rtl() ) {
		$count_title = esc_html__( 'Product', 'codevz-plus' );
	} else {
		$count_title = esc_html__( 'Items', 'codevz-plus' );
	}

	// Count all products.
	$total = wp_count_posts( 'product' );
	$total = ( ! empty( $total ) || isset( $total->publish ) ) ? $total->publish : 0;

	// “All items” button
	$is_all_active = is_shop() ? ' codevz-wcb-active' : '';
	echo '<a href="'.esc_url(get_permalink(wc_get_page_id('shop'))).'" class="'.$is_all_active.'">
			<div class="codevz-wcb-icon"><i class="fa czico-seller-new"></i></div>
			<div class="codevz-wcb-title"><strong>' . esc_html__( 'All', 'codevz-plus' ) . ' ' . esc_html__( 'Products', 'codevz-plus' ) . '</strong><small>' . esc_html( $total . ' ' . $count_title ) . '</small></div>
		  </a>';

	// Category list
	foreach ( $categories as $cat ) {
		$link = get_term_link( $cat );
		if ( is_wp_error( $link ) ) {
			continue;
		}

		// Get category thumbnail or icon.
		$image = get_term_meta( $cat->term_id, 'thumbnail_id', true );
		$icon  = get_term_meta( $cat->term_id, 'codevz_cat_meta', true );
		$final_icon = '<div class="codevz-wcb-icon">';
		if ( $image ) {
			$final_icon .= '<img src="' . esc_url( wp_get_attachment_image_url( $image, 'thumbnail' ) ) . '" alt="' . esc_attr( $cat->name ) . '">';
		} else if ( ! empty( $icon[ 'cat_icon' ] ) ) {
			$final_icon .= '<i class="' . esc_attr( $icon[ 'cat_icon' ] ) . '"></i>';
		} else {
			$final_icon .= '<i class="far fa-folder"></i>';
		}
		$final_icon .= '</div>';

		$is_active = ( is_product_category() && get_queried_object_id() === $cat->term_id ) ? ' codevz-wcb-active' : '';

		echo '<a href="' . esc_url( $link ) . '" class="' . $is_active . '">
				' . $final_icon . '
				<div class="codevz-wcb-title">
					<strong>' .esc_html( $cat->name ) . '</strong>
					<small>' . esc_html( $cat->count ) . ' ' . esc_html( $count_title ) . '</small>
				</div>
			  </a>';
	}

	echo '</div></div>';

	?>

<script>
document.addEventListener("DOMContentLoaded", () => {
    const slider = document.querySelector(".codevz-wcb-inner");

    let isDown = false;
    let isDragging = false;
    let startX = 0;
    let lastX = 0;
    let velX = 0;
    let momentumID;

    /* -------------------------
       PREVENT TEXT/IMAGE SELECT
    ------------------------- */
    slider.style.userSelect = "none";
    slider.addEventListener("dragstart", (e) => e.preventDefault());

    /* -------------------------
       INERTIA
    ------------------------- */
    function momentum() {
        slider.scrollLeft -= velX;
        velX *= 0.95;
        if (Math.abs(velX) > 0.3) {
            momentumID = requestAnimationFrame(momentum);
        }
    }

    /* -------------------------
       MOUSE DOWN
    ------------------------- */
    slider.addEventListener("mousedown", (e) => {
        isDown = true;
        isDragging = false;   // reset
        startX = lastX = e.pageX;
        velX = 0;
        cancelAnimationFrame(momentumID);
    });

    /* -------------------------
       MOUSE MOVE
    ------------------------- */
    slider.addEventListener("mousemove", (e) => {
        if (!isDown) {return;}

        const dx = e.pageX - lastX;

        if (Math.abs(e.pageX - startX) > 5) {
            isDragging = true;    // more than 5px = DRAG
        }

        lastX = e.pageX;
        slider.scrollLeft -= dx;
        velX = dx;

        if (isDragging) {
            e.preventDefault();     // stops selecting text & blocking link hover
        }
    });

    /* -------------------------
       MOUSE UP / LEAVE
    ------------------------- */
    ["mouseup", "mouseleave"].forEach(ev => {
        slider.addEventListener(ev, (e) => {
            if (!isDown) {return;}
            isDown = false;

            // If dragging happened → block click
            if (isDragging) {
                e.preventDefault();
                momentum();
            }

            setTimeout(() => (isDragging = false), 0);
        });
    });

    /* -------------------------
       BLOCK CLICK ONLY IF DRAG
    ------------------------- */
    slider.addEventListener("click", (e) => {
        if (isDragging) {
            e.preventDefault();  // don't open links when drag
            e.stopImmediatePropagation();
        }
    }, true);

    /* -------------------------
       TOUCH: allow native scroll
    ------------------------- */
    slider.addEventListener("touchstart", () => {}, { passive: true });

	/* -------------------------
	   SCROLL TO ACTIVE ITEM
	------------------------- */
	function scrollToActive() {
	    const active = slider.querySelector(".codevz-wcb-active");
	    if (!active) {return;}

	    const sliderRect = slider.getBoundingClientRect();
	    const itemRect   = active.getBoundingClientRect();

	    const isOutLeft  = itemRect.left < sliderRect.left;
	    const isOutRight = itemRect.right > sliderRect.right;

	    if (isOutLeft || isOutRight) {
	        slider.scrollLeft += (itemRect.left - sliderRect.left) - (slider.clientWidth / 2 - active.clientWidth / 2);
	    }
	}

	// run once after load
	setTimeout(scrollToActive, 50);

    /* -------------------------
       GRADIENT FADES
    ------------------------- */
	const isRTL = document.body.classList.contains( 'rtl' );
	function codevzCheckFade() {
	    const max = slider.scrollWidth - slider.clientWidth;
	    const scrollPos = Math.abs(slider.scrollLeft);
	    
	    let showStart, showEnd;

	    if (!isRTL) {
	        showStart = scrollPos > 5 ? 1 : 0;
	        showEnd = scrollPos < max - 5 ? 1 : 0;
	        
	        slider.style.setProperty('--show-left', showStart);
	        slider.style.setProperty('--show-right', showEnd);
	    } else {
	        showStart = scrollPos > 5 ? 1 : 0;
	        showEnd = scrollPos < max - 5 ? 1 : 0;

	        slider.style.setProperty('--show-right', showStart);
	        slider.style.setProperty('--show-left', showEnd);
	    }
	}

	slider.addEventListener("scroll", codevzCheckFade, { passive: true });
	codevzCheckFade();
});
</script>

<style>
.codevz-wcb {
	overflow: hidden;
	position: relative;
	margin: 0px 0 2em
}

.codevz-wcb-inner {
	--show-left: 0;
	--show-right: 0;
	--fade-width: 15%; /* Increased width for a more noticeable fade */

	transition: mask-image 0.2s ease-in-out;
	-webkit-transition: -webkit-mask-image 0.2s ease-in-out;

	-webkit-mask-image: linear-gradient(
	    to right,
	    rgba(0, 0, 0, calc(1 - var(--show-left))) 0%,
	    black var(--fade-width),
	    black calc(100% - var(--fade-width)),
	    rgba(0, 0, 0, calc(1 - var(--show-right))) 100%
	);

	mask-image: linear-gradient(
	    to right,
	    rgba(0, 0, 0, calc(1 - var(--show-left))) 0%,
	    black var(--fade-width),
	    black calc(100% - var(--fade-width)),
	    rgba(0, 0, 0, calc(1 - var(--show-right))) 100%
	);

	display: flex;
	gap: 20px;
	margin: 0 -20px;
	padding: 0;
	overflow-x: auto;
	scrollbar-width: none; /* Firefox */
	-ms-overflow-style: none; /* IE */
	cursor: grab;
	user-select: none;
	overflow-x: auto;
	white-space: nowrap;
}
.codevz-wcb-inner::-webkit-scrollbar {
	display: none; /* Chrome/Safari */
}
.codevz-wcb-inner a {
	user-select: none;
	min-width: 170px;
	flex-shrink: 0;
	display: flex;
	overflow: hidden;
	align-items: center;
	color: #676767;
	background: #6767670f;
	padding: 12px;
	line-height: 1
}
.codevz-wcb-inner a:hover,
.codevz-wcb-inner .codevz-wcb-active {
	background: #676767;
	color: #fff
}
.codevz-wcb-inner .codevz-wcb-icon {
	flex: none;
	border-radius: inherit;
	margin-inline-end: 15px
}
.codevz-wcb-inner .codevz-wcb-icon img {
	width: 40px;
	height: 40px
}
.codevz-wcb-inner .codevz-wcb-icon i {
	width: 1em;
	height: 1em;
	line-height: 1em;
	font-size: 20px;
	padding: 10px;
	text-align: center;
	color: #333;
	background: #fff;
	border-radius: inherit
}
.codevz-wcb-inner .codevz-wcb-title {
	width: 100%;
	white-space: nowrap;
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	overflow: hidden;
	text-overflow: ellipsis
}
.codevz-wcb-inner .codevz-wcb-title strong {
	font-size: 15px;
	display: block
}
.codevz-wcb-inner .codevz-wcb-title small {
	font-size: 13px;
	opacity: .75;
	display: block;
	margin: 5px 0 0
}

.codevz-wcb-1 .codevz-wcb-icon {
	display: none
}
.codevz-wcb-1 .codevz-wcb-inner a {
	padding: 15px 20px;
	width: 120px
}
.codevz-wcb-3 .codevz-wcb-inner a {
	flex-direction: row-reverse
}
.codevz-wcb-3 .codevz-wcb-icon {
	margin-inline-start: 15px;
	margin-inline-end: 0
}
.codevz-wcb-4 .codevz-wcb-inner a,
.codevz-wcb-5 .codevz-wcb-inner a {
	flex-direction: column;
	text-align: center;
	padding: 20px;
	width: 130px
}
.codevz-wcb-4 .codevz-wcb-icon {
	margin-inline-start: 0;
	margin-inline-end: 0;
	margin-bottom: 15px
}
.codevz-wcb-5 .codevz-wcb-inner a {
	flex-direction: column-reverse
}
.codevz-wcb-5 .codevz-wcb-icon {
	margin-inline-start: 0;
	margin-inline-end: 0;
	margin-top: 15px
}

@media screen and (max-width: 768px) {

	.codevz-wcb-inner a {
		padding: 10px;
		min-width: 120px;
	}
	.codevz-wcb-inner .codevz-wcb-icon i {
		font-size: 15px;
		padding: 8px
	}
	.codevz-wcb-inner .codevz-wcb-title strong {
		font-size: 12px
	}
	.codevz-wcb-inner .codevz-wcb-title small {
		font-size: 10px
	}
	.codevz-wcb-inner .codevz-wcb-icon img {
		width: 31px;
		height: 31px
	}

}
</style>
<?php
}

add_action( 'comment_form_before', 'codevz_rating_breakdown' );
function codevz_rating_breakdown() {
    global $product;
    if ( ! $product ) {return;}

    $counts     = $product->get_rating_counts();
    $total      = array_sum( $counts );
    if ( ! $total ) {return;}

	echo '<div class="codevz-plus-rbd">';

		echo '<div class="codevz-plus-rbd-total">';

			echo '<div class="codevz-plus-rbd-avg">' . $product->get_average_rating() . '</div>';

			echo '<div class="codevz-plus-rbd-rating">';
				$average      = $product->get_average_rating();
				$rating_whole = floor($average);
				$rating_fraction = $average - $rating_whole;
				$flug = 0;
				for ($i = 1; $i <= 5; $i++) {
					if ($i <= $rating_whole) {
						echo '<i class="fas fa-star"></i>';
					} else {
						if ($rating_fraction > 0 && $flug == 0) {
							echo '<i class="fas fa-star-half-alt"></i>';
							$flug = 1;
						} else {
							echo '<i class="far fa-star empty"></i>';
						}
					}
				}
		    echo '</div>';

		    echo '<div class="codevz-plus-rbd-count">(' . $total . ' ' . esc_html__( 'Rating', 'codevz-plus' ) . ')</div>';

	    echo '</div>';

	    echo '<div class="codevz-plus-rbd-rows">';
		    for ( $i = 5; $i >= 1; $i-- ) {
		        $count = isset( $counts[ $i ] ) ? $counts[ $i ] : 0;
		        $percent = $total ? round( ( $count / $total ) * 100 ) : 0;

		        echo '
		        <div>
		            <span class="codevz-plus-rbd-label">' . $i . ' ★</span>
		            <div class="codevz-plus-rbd-bar">
		                <span style="width:' . $percent . '%"></span>
		            </div>
		            <span class="codevz-plus-rbd-percent">' . $percent . '%</span>
		        </div>';
		    }
	    echo '</div>';

    echo '</div>';

    echo '<style>
.codevz-plus-rbd {
	margin: 30px 0;
    display: flex;
    align-items: center;
    width: 100%
}
.codevz-plus-rbd-total {
	display: flex;
	align-items: center;
	flex-direction: column;
	margin: 0 20px;
}
.codevz-plus-rbd-avg {
	font-size: 57px;
	line-height: 1;
}
.codevz-plus-rbd-count {
	opacity: .6;
	font-size: 14px;
}
.codevz-plus-rbd-rows {
	flex: 1;
	width: 100%;
	margin: 0 40px;
}
.s8 .codevz-plus-rbd-rows,
.s9 .codevz-plus-rbd-rows,
.woo-product-tabs-vertical .codevz-plus-rbd-rows {
	margin: 0 10px
}
.codevz-plus-rbd-rating {
	display: flex;
	margin: 10px 0
}
.rtl .codevz-plus-rbd-rating {
	flex-direction: row-reverse
}
.codevz-plus-rbd-rating i {
	color: #ffc000
}
.codevz-plus-rbd-rows > div {
	display: flex;
	align-items: center;
	margin: 4px 0;
}
.codevz-plus-rbd-label {
	width: 30px;
	font-size: 13px;
}
.codevz-plus-rbd-bar {
	flex: 1;
	height: 6px;
	background: #67676738;
	margin: 0 8px;
	position: relative;
	border-radius: 3px;
}
.codevz-plus-rbd-bar span {
	display: block;
	height: 100%;
	background: #ffc000;
	border-radius: 3px;
}
.codevz-plus-rbd-percent {
	width: 35px;
	font-size: 13px;
	text-align: right;
}
</style>';
}


// Core class.
class Codevz_Plus_Woocommerce {

	use Codevz_Plus_Instance;

	// Get product ID before load product.
	protected $singular_product_id = null;

	// Init.
	public function __construct() {

		// WooCommerce init.
		add_action( 'woocommerce_init', [ $this, 'woocommerce_init' ], 11 );

		// Products search result page.
		add_filter( 'posts_search', [ $this, 'posts_search' ], 10, 2 );
		add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ] );

	}

	/**
	 * Init WooCommerce actions and filters.
	 * 
	 * @return -
	 */
	public function woocommerce_init() {

		// Register size guide post type.
		register_post_type( 'codevz_size_guide',
			[
				'public' 	=> true,
				'label' 	=> esc_html__( 'Size guide', 'codevz-plus' ),
				'supports' 	=> [ 'title', 'editor' ],
				'rewrite' 	=> [ 'slug' => 'product-size-guide' ]
			]
		);

		// Register FAQ post type.
		register_post_type( 'codevz_faq',
			[
				'public' 	=> true,
				'label' 	=> esc_html__( 'FAQ', 'codevz-plus' ),
				'supports' 	=> [ 'title', 'editor' ],
				'rewrite' 	=> [ 'slug' => 'product-faq' ]
			]
		);

		// Register brands taxonomy.
		register_taxonomy( 'codevz_brands', 'product', [
			'labels' => [
				'name' 			=> esc_html__( 'Brands', 'codevz-plus' ),
				'singular_name' => esc_html__( 'Brand', 'codevz-plus' )
			],
			'hierarchical'               => true,
			'public'                     => false,
			'show_ui'                    => false,
			'show_admin_column'          => false,
			'show_in_nav_menus'          => false,
			'show_tagcloud'              => false,
			'rewrite'					 => [ 'slug' => 'product-brand' ]
		] );

		// Temporary migration for old users.
		if ( ! get_option( 'codevz_brands_migration_1' ) ) {
			update_option( 'codevz_brands_migration_1', 1, false );
			$old_taxonomy = 'codevz_brands';
			$new_taxonomy = 'product_brand';
			$terms = get_terms(['taxonomy' => $old_taxonomy, 'hide_empty' => false]);
			if ( ! empty($terms) && ! is_wp_error($terms)) {
				foreach ($terms as $term) {
					$existing_term = term_exists($term->name, $new_taxonomy);

					if (is_array($existing_term) && isset($existing_term['term_id'])) {
						$new_term_id = $existing_term['term_id'];
					} else {
						$insert = wp_insert_term($term->name, $new_taxonomy, [
							'slug'        => $term->slug,
							'description' => $term->description
						]);
						if (is_wp_error($insert)) { continue; }
						$new_term_id = $insert['term_id'];
					}

					$brand_logo = get_term_meta($term->term_id, 'codevz_brands', true);
					$brand_logo = empty($brand_logo['brand_logo']) ? '' : absint($brand_logo['brand_logo']);
					if ($brand_logo) {
						update_term_meta($new_term_id, 'thumbnail_id', $brand_logo);
					}

					$products = get_posts([
						'post_type'      => 'product',
						'posts_per_page' => -1,
						'tax_query'      => [['taxonomy' => $old_taxonomy, 'field' => 'term_id', 'terms' => $term->term_id]],
					]);
					foreach ($products as $product) {
						wp_set_object_terms($product->ID, $new_term_id, $new_taxonomy, true);
					}
				}
			}
		}

		// Product brand and other tabs in single product page.
		add_filter( 'woocommerce_product_tabs', [ $this, 'product_tabs' ] );

		// Enqueue fragments JS.
		add_action( 'wp_enqueue_scripts', [ $this, 'wp_enqueue_scripts' ], 11 ); 

		// Number of products per page & columns.
		if ( ! class_exists( 'Woocommerce_Products_Per_Page' ) ) {

			add_filter( 'loop_shop_columns', [ $this, 'columns' ], 11 );
			add_filter( 'loop_shop_per_page', [ $this, 'loop_shop_per_page' ], 101, 1 );
			add_filter( 'woocommerce_product_query', [ $this, 'products_per_page' ], 11 );

			// Number of products browser request.
			$products_per_page = Codevz_Plus::_POST( 'ppp' );

			if ( $products_per_page ) {
				wc_setcookie( 'woocommerce_products_per_page', intval( $products_per_page ), time() + DAY_IN_SECONDS * 2, apply_filters( 'wc_session_use_secure_cookie', false ) );
			}

			// Show products per page dropdown.
			add_action( 'woocommerce_before_shop_loop', [ $this, 'products_per_page_dropdown' ], 99 );

		}

		// Disable search redirect.
		add_filter( 'woocommerce_redirect_single_search_result', '__return_false' );

		// Free shipping process bar.
		add_action( 'woocommerce_review_order_before_payment', [ $this, 'free_shipping_bar' ], 5 );
		add_action( 'woocommerce_proceed_to_checkout', [ $this, 'free_shipping_bar' ], 5 );

		// AJAX mini cart content.
		add_filter( 'woocommerce_add_to_cart_fragments', [ $this, 'cart' ], 11, 1 );

		// Number of  related products per page.
		add_filter( 'woocommerce_upsell_display_args', [ $this, 'related_products' ], 11 );
		add_filter( 'woocommerce_output_related_products_args', [ $this, 'related_products' ], 11 );

		// Customize products HTML and add quickview and wihlist.
		add_filter( 'woocommerce_post_class', [ $this, 'product_classes' ], 10, 2 );
		add_action( 'woocommerce_before_add_to_cart_button', [ $this, 'before_add_to_cart' ], 20 );
		add_action( 'woocommerce_after_add_to_cart_button', [ $this, 'after_add_to_cart' ], 20 );
		add_action( 'woocommerce_before_shop_loop_item_title', [ $this, 'woocommerce_before_shop_loop_item_title_low' ], 9 );
		add_action( 'woocommerce_before_shop_loop_item_title', [ $this, 'woocommerce_before_shop_loop_item_title_high' ], 11 );

		// Single Wrap.
		add_action( 'woocommerce_before_single_product_summary', [ $this, 'before_single' ], 11 );
		add_action( 'woocommerce_after_single_product_summary', [ $this, 'after_single' ], 1 );

		// Recently viewed products.
		add_action( 'woocommerce_after_single_product', [ $this, 'recently_viewed_products' ], 20 );

		// Quickview AJAX function.
		add_action( 'wp_ajax_xtra_quick_view', [ $this, 'quickview' ] );
		add_action( 'wp_ajax_nopriv_xtra_quick_view', [ $this, 'quickview' ] );

		// Get wishlist & compare page content via AJAX.
		add_action( 'wp_ajax_xtra_wishlist_content', [ $this, 'wishlist_content' ] );
		add_action( 'wp_ajax_nopriv_xtra_wishlist_content', [ $this, 'wishlist_content' ] );
		add_action( 'wp_ajax_xtra_compare_content', [ $this, 'compare_content' ] );
		add_action( 'wp_ajax_nopriv_xtra_compare_content', [ $this, 'compare_content' ] );

		// Wishlist shortcode.
		add_shortcode( 'cz_wishlist', [ $this, 'wishlist_shortcode' ] );

		// Compare shortcode.
		add_shortcode( 'cz_compare', [ $this, 'compare_shortcode' ] );

		// Quickview popup content.
		add_filter( 'woocommerce_product_loop_end', [ $this, 'popup' ] );

		// Modify checkout page.
		add_action( 'woocommerce_checkout_after_customer_details', [ $this, 'checkout_order_review_before' ] );
		add_action( 'woocommerce_checkout_after_order_review', [ $this, 'checkout_order_review_after' ] );

		// Add back to store button on WooCommerce cart page.
		add_action( 'woocommerce_cart_actions', [ $this, 'continue_shopping' ] );

		// Remove product description h2 tab.
		add_filter( 'woocommerce_product_description_heading', '__return_null' );

		// Modify products title and add category name.
		if ( ! is_admin() || ( isset( $_GET[ 'action' ] ) && $_GET[ 'action' ] === 'elementor' ) ) {
			add_filter( 'the_title', [ $this, 'category_under_title' ], 10, 2 );
		}

		// Sale badge percentage. 
		if ( Codevz_Plus::option( 'woo_sale_percentage' ) ) {
			add_filter( 'woocommerce_sale_flash', [ $this, 'percentage_sale_flash' ], 10, 3 );
		}

		// Custom Badges.
		if ( ! Codevz_Plus::$is_free ) {
			add_filter( 'woocommerce_after_shop_loop_item_title', [ $this, 'custom_badges' ], 11 );
			add_filter( 'woocommerce_before_single_product_summary', [ $this, 'custom_badges' ], 11 );
		}

		// Single product page countdown.
		add_action( 'woocommerce_before_single_product_summary', [ $this, 'countdown' ], 1 );

		// Taxonomy add meta box.
		add_action( 'add_meta_boxes', [ $this, 'add_meta_boxes' ] );

		// Save post action to update taxonomy fields.
		add_action( 'save_post_product', [ $this, 'save_post_product' ] );

		// Post state for compare and wishlist in admin.
		add_filter( 'display_post_states', [ $this, 'display_post_states' ], 10, 2 );

		// Move new post types menu under products.
		add_action( 'admin_menu', [ $this, 'admin_menu' ] );

		// Remove duplicated custom woo pages.
		add_action( 'admin_init', [ $this, 'admin_init' ] );

		// Add cart empty SVG icon into cart page when its empty.
		remove_action( 'woocommerce_cart_is_empty', 'wc_empty_cart_message', 10 ); // remove
		add_action( 'woocommerce_cart_is_empty', [ $this, 'cart_empty_svg' ], 10 ); // add

		// Customize product meta HTML.
		remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_meta', 40 );
		add_action( 'woocommerce_single_product_summary', [ $this, 'product_meta' ], 40 );

		// Attribute variations settings and output.
		add_action( 'woocommerce_after_add_attribute_fields', [ $this, 'attribute_add_fields' ] );
		add_action( 'woocommerce_after_edit_attribute_fields', [ $this, 'attribute_add_fields' ] );
		add_action( 'woocommerce_attribute_added', [ $this, 'attribute_added_updated' ], 10, 2 );
		add_action( 'woocommerce_attribute_updated', [ $this, 'attribute_added_updated' ], 10, 2 );$attributes = (array) wc_get_attribute_taxonomies();

		$attributes = (array) wc_get_attribute_taxonomies();

		// Variations colorpicker.
		if ( ! empty( $attributes ) ) {

			foreach( $attributes as $attribute ) {

				add_action( 'pa_' . $attribute->attribute_name . '_edit_form_fields', [ $this, 'add_variation_colorpicker' ] );
				add_action( 'pa_' . $attribute->attribute_name . '_add_form_fields',  [ $this, 'add_variation_colorpicker' ] );

				add_filter( 'manage_edit-pa_' . $attribute->attribute_name . '_columns',  [ $this, 'add_variation_color_column' ], 1 );
				add_action( 'manage_pa_' . $attribute->attribute_name . '_custom_column', [ $this, 'show_variation_color_column' ], 1, 3 );

			}

		}

		// Variations save colorpicker.
		add_action( 'edit_term', [ $this, 'save_variation_colorpicker' ], 10, 3 );
		add_action( 'created_term', [ $this, 'save_variation_colorpicker' ], 10, 3 );

		// Variations output HTML.
		add_filter( 'woocommerce_dropdown_variation_attribute_options_html', [ $this, 'variations_output' ], 10, 2 );

		// Cart page related products according to cart items.
		if ( Codevz_Plus::option( 'woo_cart_page_related_products' ) && ! Codevz_Plus::$is_free ) {

			add_action( 'woocommerce_after_cart_totals', [ $this, 'cart_related_products' ] );
			add_action( 'woocommerce_cart_is_empty', [ $this, 'cart_empty_products_section' ] );

		}

		// Show 3 steps above on cart, checkout and order completion pages.
		if ( Codevz_Plus::option( 'woo_cart_checkout_steps' ) && ! Codevz_Plus::$is_free ) {

			add_action( 'woocommerce_before_cart', [ $this, 'cart_checkout_steps' ], 1 );
			add_action( 'woocommerce_before_checkout_form', [ $this, 'cart_checkout_steps' ], 1 );

		}

		// Avatar above the account nav.
		add_filter( 'woocommerce_before_account_navigation', [ $this, 'my_account_avatar' ] );

		// Fix new links in my account nav.
		$this->my_account_endpoints();

		// Add a new menus to my account page.
		add_filter( 'woocommerce_account_menu_items', [ $this, 'my_account_menus' ] );

		// My account menus content.
		add_action( 'woocommerce_account_wishlist_endpoint', [ $this, 'my_account_wishlist' ] );
		add_action( 'woocommerce_account_viewed_endpoint',   [ $this, 'my_account_viewed' ] );
		add_action( 'woocommerce_account_reviews_endpoint',  [ $this, 'my_account_reviews' ] );
		add_action( 'woocommerce_account_tracking_endpoint', [ $this, 'my_account_tracking' ] );
		add_action( 'woocommerce_before_customer_login_form', [ $this, 'before_my_account_form' ], 9 );
		add_action( 'woocommerce_after_customer_login_form', [ $this, 'after_my_account_form' ], 9 );
		add_action( 'woocommerce_before_lost_password_form', [ $this, 'before_my_account_form' ], 9 );
		add_action( 'woocommerce_after_lost_password_form', [ $this, 'after_my_account_form' ], 9 );

		// Product gallery navigation arrows.
		add_filter( 'woocommerce_single_product_carousel_options', [ $this, 'woo_flexslider_options' ] );

		// Add short description under product rating on shop archive pages
		add_action( 'woocommerce_after_shop_loop_item_title', [ $this, 'products_short_desc' ], 4 );

		// Move outofstock products to the end of loop.
		add_filter( 'posts_clauses', [ $this, 'move_outofstock_end_of_loop' ], 99 );

		// Change product page title tag to h2.
		remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_title', 5 );
		add_action( 'woocommerce_single_product_summary', [ $this, 'add_new_product_title_tag' ], 5 );

		// Track live carts and product viewers count.
		add_action( 'woocommerce_single_product_summary', [ $this, 'log_sessions_script' ], 35 );
		add_action( 'woocommerce_add_to_cart', [ $this, 'track_cart_addition' ], 10, 6 );
		add_action( 'woocommerce_cart_item_removed', [ $this, 'track_cart_removal' ], 10, 2 );
		add_action( 'wp_ajax_codevz_woo_sessions', [ $this, 'codevz_woo_sessions' ] );
		add_action( 'wp_ajax_nopriv_codevz_woo_sessions', [ $this, 'codevz_woo_sessions' ] );
		add_action( 'wp_ajax_codevz_woo_live_cart', [ $this, 'codevz_woo_live_cart' ] );
		add_action( 'wp_ajax_nopriv_codevz_woo_live_cart', [ $this, 'codevz_woo_live_cart' ] );

		// Add phone assistance box after Add to Cart and before Product Meta
		add_action( 'woocommerce_single_product_summary', [ $this, 'phone_assistance' ], 36 );

		// Prevent slug changes for wishlist and compare pages.
		add_filter( 'wp_insert_post_data', [ $this, 'prevent_slug_change' ], 10, 2 );

		// Cart page product SKU under title.
		add_action( 'woocommerce_after_cart_item_name', [ $this, 'cart_product_sku' ], 11, 2 );

		// Add more options to orderby shop items.
		add_action( 'save_post', [ $this, 'calculate_and_store_discount_percentage' ] );
		add_action( 'woocommerce_update_product', [ $this, 'calculate_and_store_discount_percentage' ] );
		add_filter( 'woocommerce_default_catalog_orderby_options', [ $this, 'woocommerce_catalog_orderby' ] );
		add_filter( 'woocommerce_catalog_orderby', [ $this, 'woocommerce_catalog_orderby' ] );
		add_filter( 'woocommerce_get_catalog_ordering_args', [ $this, 'woocommerce_get_catalog_ordering_args' ], 10, 3 );

		// Archive columns grid icons.
		add_action( 'woocommerce_before_shop_loop', [ $this, 'shop_columns_grid_icons' ], 100 );

		// Change archive title to H2
		add_filter( 'woocommerce_show_page_title', '__return_false' );
		add_action( 'woocommerce_archive_description', [ $this, 'archive_title_tag' ], 1 );

		// Remove default Woo tax description.
		remove_action( 'woocommerce_archive_description', 'woocommerce_taxonomy_archive_description', 10 );

		// Archive tax description after/before products.
		if ( Codevz_Plus::option( 'woo_desc_below' ) ) {
			add_action( 'woocommerce_after_shop_loop', [ $this, 'term_description' ], 11 );
		} else {
			add_action( 'woocommerce_archive_description', [ $this, 'term_description' ], 11, 2 );
		}

		// Change add to cart text on single product page
		if ( Codevz_Plus::option( 'woo_add_to_cart_title' ) ) {

			add_filter( 'woocommerce_product_add_to_cart_text', [ $this, 'add_to_cart_title' ], 11 ); 
			add_filter( 'woocommerce_product_single_add_to_cart_text', [ $this, 'add_to_cart_title' ], 11 ); 

		}

		// Wailist mailing system for out of stock products.
		add_action( 'woocommerce_single_product_summary', [ $this, 'waitlist_box' ], 35 );
		add_action( 'wp_ajax_codevz_waitlist', [ $this, 'waitlist_handle' ] );
		add_action( 'wp_ajax_nopriv_codevz_waitlist', [ $this, 'waitlist_handle' ] );
		add_action( 'woocommerce_product_set_stock_status', [ $this, 'waitlist_send_emails' ], 10, 3 );

		// Fix single product slider duplicated images.
		add_filter( 'woocommerce_product_get_gallery_image_ids', [ $this, 'woocommerce_product_get_gallery_image_ids' ], 10, 2 );

		// AJAX mode for hidden product tabs.
		if ( in_array( 'product_tabs', (array) Codevz_Plus::option( 'render_delay' ) ) ) {
			add_filter( 'woocommerce_product_tabs', [ $this, 'render_delay_product_tabs' ], 99 );
		}

	}

	/**
	 * Onsale badge percentage
	 * 
	 * @return string
	 */
	public function percentage_sale_flash( $html, $post, $product ) {

		if ( $product->is_on_sale() ) {

			if ( $product->is_type( 'variable' ) ) {

				$regular_price 	= (int) $product->get_variation_regular_price( 'max' );
				$sale_price 	= (int) $product->get_variation_sale_price( 'min' );

			} else {

				$regular_price 	= (int) $product->get_regular_price();
				$sale_price 	= (int) $product->get_sale_price();

			}

			if ( $regular_price && $sale_price ) {

				// Calculate the percentage discount
				$percentage = round( ( ( $regular_price - $sale_price ) / $regular_price ) * 100 );

				$option = Codevz_Plus::option( 'woo_sale_percentage' );

				// Customize the HTML output to display the percentage discount
				if ( $option === 'woo-sale-percentage' ) {
					$html = '<span class="onsale codevz-woo-percentage">' . esc_html( $percentage ) . '%</span>';
				} else if ( $option === 'woo-sale-text-percentage' ) {
					$html .= '<span class="onsale codevz-woo-text-percentage">' . esc_html( $percentage ) . '%</span>';
				}

			}

		}

		return $html;

	}

	/**
	 * Showing custom and NEW badge on product image.
	 * 
	 * @return string
	 */
	public function custom_badges( $onsale ) {

		global $product;

		if ( ! $product->is_in_stock() ) {
			echo '<span class="onsale codevz-woo-outofstock-badge">' . esc_html__( 'Sold out', 'codevz-plus' ) . '</span>';
		}

		$product_id  = get_the_id();
		$custom_badge = get_post_meta( $product_id, 'codevz_custom_badge', true );

		if ( $custom_badge ) {
			echo '<span class="onsale codevz-woo-custom-badge">' . esc_html( $custom_badge ) . '</span>';
		}

		// NEW badge.
		if ( Codevz_Plus::option( 'woo_new_label' ) ) {

			$days = intval( abs( current_time( 'timestamp' ) - get_the_time( 'U', get_the_ID() ) ) / 86400 );

			if ( $days <= Codevz_Plus::option( 'woo_new_label_days', 1 ) ) {
				echo '<span class="onsale cz_new_badge">' . esc_html__( 'NEW', 'codevz-plus' ) . '</span>';
			}

		}

	}

	/**
	 * Run cart JS fragments on all pages for mini cart.
	 * 
	 * @return string
	 */
	public function wp_enqueue_scripts() {

		wp_enqueue_script( 'wc-cart-fragments' );

	}

	/**
	 * Change cart empty message with SVG icon.
	 * 
	 * @return string
	 */
	public function cart_empty_svg( $only_svg = true ) {

		echo '<svg class="codevz-cart-empty-svg" fill="#676767" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 231.523 231.523" xml:space="preserve">
				<g>
					<path d="M107.415,145.798c0.399,3.858,3.656,6.73,7.451,6.73c0.258,0,0.518-0.013,0.78-0.04c4.12-0.426,7.115-4.111,6.689-8.231
						l-3.459-33.468c-0.426-4.12-4.113-7.111-8.231-6.689c-4.12,0.426-7.115,4.111-6.689,8.231L107.415,145.798z"/>
					<path d="M154.351,152.488c0.262,0.027,0.522,0.04,0.78,0.04c3.796,0,7.052-2.872,7.451-6.73l3.458-33.468
						c0.426-4.121-2.569-7.806-6.689-8.231c-4.123-0.421-7.806,2.57-8.232,6.689l-3.458,33.468
						C147.235,148.377,150.23,152.062,154.351,152.488z"/>
					<path d="M96.278,185.088c-12.801,0-23.215,10.414-23.215,23.215c0,12.804,10.414,23.221,23.215,23.221
						c12.801,0,23.216-10.417,23.216-23.221C119.494,195.502,109.079,185.088,96.278,185.088z M96.278,216.523
						c-4.53,0-8.215-3.688-8.215-8.221c0-4.53,3.685-8.215,8.215-8.215c4.53,0,8.216,3.685,8.216,8.215
						C104.494,212.835,100.808,216.523,96.278,216.523z"/>
					<path d="M173.719,185.088c-12.801,0-23.216,10.414-23.216,23.215c0,12.804,10.414,23.221,23.216,23.221
						c12.802,0,23.218-10.417,23.218-23.221C196.937,195.502,186.521,185.088,173.719,185.088z M173.719,216.523
						c-4.53,0-8.216-3.688-8.216-8.221c0-4.53,3.686-8.215,8.216-8.215c4.531,0,8.218,3.685,8.218,8.215
						C181.937,212.835,178.251,216.523,173.719,216.523z"/>
					<path d="M218.58,79.08c-1.42-1.837-3.611-2.913-5.933-2.913H63.152l-6.278-24.141c-0.86-3.305-3.844-5.612-7.259-5.612H18.876
						c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h24.94l6.227,23.946c0.031,0.134,0.066,0.267,0.104,0.398l23.157,89.046
						c0.86,3.305,3.844,5.612,7.259,5.612h108.874c3.415,0,6.399-2.307,7.259-5.612l23.21-89.25C220.49,83.309,220,80.918,218.58,79.08z
						 M183.638,165.418H86.362l-19.309-74.25h135.895L183.638,165.418z"/>
					<path d="M105.556,52.851c1.464,1.463,3.383,2.195,5.302,2.195c1.92,0,3.84-0.733,5.305-2.198c2.928-2.93,2.927-7.679-0.003-10.607
						L92.573,18.665c-2.93-2.928-7.678-2.927-10.607,0.002c-2.928,2.93-2.927,7.679,0.002,10.607L105.556,52.851z"/>
					<path d="M159.174,55.045c1.92,0,3.841-0.733,5.306-2.199l23.552-23.573c2.928-2.93,2.925-7.679-0.005-10.606
						c-2.93-2.928-7.679-2.925-10.606,0.005l-23.552,23.573c-2.928,2.93-2.925,7.679,0.005,10.607
						C155.338,54.314,157.256,55.045,159.174,55.045z"/>
					<path d="M135.006,48.311c0.001,0,0.001,0,0.002,0c4.141,0,7.499-3.357,7.5-7.498l0.008-33.311c0.001-4.142-3.356-7.501-7.498-7.502
						c-0.001,0-0.001,0-0.001,0c-4.142,0-7.5,3.357-7.501,7.498l-0.008,33.311C127.507,44.951,130.864,48.31,135.006,48.311z"/>
				</g>
			</svg>';

		if ( ! $only_svg ) {

			echo '<div class="codevz-cart-is-empty"><h2>' . esc_html__( 'Looks like your cart is empty!', 'codevz-plus' ) . '</h2><span>' . esc_html__( 'Time to start your shopping', 'codevz-plus' ) . '</span></div>';

		}

	}

	/**
	 * Free shipping process bar for mini cart, cart, checkout pages.
	 * 
	 * @return string
	 */
	public function free_shipping_bar() {

		if ( Codevz_Plus::option( 'woo_free_shipping_bar' ) ) {

			$threshold = Codevz_Plus::option( 'woo_free_shipping_bar_threshold', 0 );
			$threshold = (int) filter_var( $threshold, FILTER_SANITIZE_NUMBER_INT );
			$cart_total = WC()->cart->subtotal;

			$remaining = $threshold - $cart_total;
			$percent = min( 100, ( $cart_total / $threshold) * 100 );
			$percent = max( $percent, 5 );

			if ( $remaining > 0 ) {

				echo '<div class="codevz-plus-free-shipping-bar">';
				echo '<span><i class="fas fa-dolly mr10"></i> ' . sprintf( esc_html__( 'Add %s more to get free shipping!', 'codevz-plus' ), '<strong>' . wc_price( $remaining ) . '</strong>' ) . '</span>';
				echo '<div><div style="width:' . esc_attr( $percent ) . '%;"></div></div>';
				echo '</div>';

			}
			
		}

	}

	/**
	 * Get WooCommerce cart in header.
	 * 
	 * @return string
	 */
	public function cart( $fragments ) {

		$wc = WC();
		$count = $wc->cart->cart_contents_count;
		$total = $wc->cart->get_cart_total();

		ob_start(); ?>
			<div class="cz_cart">
				<?php if ( $count > 0 || Codevz_Plus::option( 'woo_show_zero_count' ) ) { ?>
				<span class="cz_cart_count"><?php echo esc_html( $count ); ?></span>
				<?php } ?>
				<div class="cz_cart_items"><div>
					<?php if ( $wc->cart->cart_contents_count == 0 ) { ?>
						<div class="cart_list">

							<div class="item_small xtra-empty-cart">
								<?php $this->cart_empty_svg( true ); ?>
								<span><?php esc_html_e( "Your cart is empty — start shopping", 'codevz-plus' ); ?></span>
							</div>

						</div>
					<?php $fragments['.cz_cart'] = ob_get_clean(); return $fragments; } else { ?>
						<div class="cart_list">

							<?php foreach( $wc->cart->cart_contents as $cart_item_key => $cart_item ) {
								$id = $cart_item['product_id'];
								$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
							?>
								<div class="item_small">
									<a href="<?php echo esc_url( get_permalink( $id ) ); ?>">
										<?php echo wp_kses_post( (string) apply_filters( 'woocommerce_cart_item_thumbnail', $_product->get_image( 'codevz_600_600' ), $cart_item, $cart_item_key ) ); ?>
									</a>
									<div class="cart_list_product_title cz_tooltip_up">
										<h3><a href="<?php echo esc_url( get_permalink( $id ) ); ?>"><?php echo wp_kses_post( (string) get_the_title( $id ) ); ?></a></h3>
										<div class="cart_list_product_quantity"><?php echo wp_kses_post( (string) $cart_item['quantity'] ); ?> x <?php echo wp_kses_post( (string) $wc->cart->get_product_subtotal( $cart_item['data'], 1 ) ); ?> </div>
										<a href="<?php echo esc_url( wc_get_cart_remove_url( $cart_item_key ) ); ?>" class="remove" data-product_id="<?php echo esc_attr( $id ); ?>" data-title="<?php echo esc_attr__( 'Remove', 'codevz-plus' ); ?>"><i class="fa czico-198-cancel"></i></a>
									</div>
								</div>
							<?php } ?>
							</div>

							<div class="cz_cart_buttons clr">
								<a href="<?php echo esc_url( get_permalink( get_option('woocommerce_cart_page_id') ) ); ?>"><i class="fas fa-receipt"></i> <?php echo esc_html( do_shortcode( Codevz_Plus::option( 'woo_cart', esc_html__( 'Cart subtotal', 'codevz-plus' ) ) ) ); ?> <span><?php echo wp_kses_post( (string) $wc->cart->get_cart_total() ); ?></span></a>
							</div>
						<?php } ?>
					</div>

					<?php $this->free_shipping_bar(); ?>

					<a class="button" href="<?php echo esc_url( get_permalink( get_option('woocommerce_checkout_page_id') ) ); ?>"><?php echo esc_html( do_shortcode( Codevz_Plus::option( 'woo_checkout', esc_html__( 'Process to Checkout', 'codevz-plus' ) ) ) ); ?></a>

					<?php if ( Codevz_Plus::option( 'woo_cart_footer' ) ) { ?>
						<span class="cz_cart_footer"><?php echo esc_html( Codevz_Plus::option( 'woo_cart_footer' ) ); ?></span>
					<?php } ?>

				</div>
			</div>
		<?php 

		$fragments['.cz_cart'] = ob_get_clean();

		return $fragments;
	}

	/**
	 * WooCommerce products columns
	 * 
	 * @return string
	 */
	public function columns() {

		$site_id = ( function_exists( 'is_multisite' ) && is_multisite() ) ? get_current_blog_id() : '1';

		if ( isset( $_COOKIE[ 'codevz_columns_' . $site_id ] ) && Codevz_Plus::option( 'woo_columns_selector' ) ) {

			return intval( esc_html( $_COOKIE[ 'codevz_columns_' . $site_id ] ) );

		}

		return Codevz_Plus::option( 'woo_col', 4 );

	}

	/**
	 * WooCommerce products per page
	 * 
	 * @return object
	 */
	public function products_per_page( $query ) {

		$query->set( 'posts_per_page', $this->loop_shop_per_page() );

	}

	/**
	 * WooCommerce products per page
	 * 
	 * @return int
	 */
	public function loop_shop_per_page( $per_page = 6 ) {

		$ppp = Codevz_Plus::_POST( 'ppp' );

		$ppp = $ppp ? $ppp : Codevz_Plus::_COOKIE( 'woocommerce_products_per_page' );

		return intval( esc_html( $ppp ? $ppp : Codevz_Plus::option( 'woo_items_per_page', $per_page ) ) );

	}

	/**
	 * WooCommerce excluding variations and attributes from search results.
	 * 
	 * @return string
	 */
	public function posts_search( $search, $query ) {

		if ( $query->is_search() && ! is_admin() ) {

			global $wpdb;

			$search .= " AND {$wpdb->posts}.post_type != 'product_variation'";

		}

		return $search;

	}

	/**
	 * WooCommerce set posts per page for search result page.
	 */
	public function pre_get_posts( $query ) {

		if ( $query->is_search() && ! is_admin() && Codevz_Plus::_GET( 'post_type' ) === 'product' ) {

			$ppp = $this->loop_shop_per_page();

			$query->set( 'showposts', $ppp );
			$query->set( 'posts_per_page', $ppp );

		}

	}

	/**
	 * WooCommerce show products per page dropdown.
	 * 
	 * @return string
	 */
	public function products_per_page_dropdown() {

		// Only show on product categories
		if ( ! Codevz_Plus::option( 'woo_ppp_dropdown' ) || ! woocommerce_products_will_display() ) {
			return;
		}

		global $wp_query;

		$ppp = $this->loop_shop_per_page( 6 );

		// Set the products per page options.
		if ( $ppp % 2 == 0 ) {
			$numbers = [ 16, 32, 48, 64, 128 ];
		} else {
			$numbers = [ 18, 36, 45, 72, 90 ];
		}

		// Get action URL.
		$cat = $wp_query->get_queried_object();

		if ( isset( $cat->term_id ) && isset( $cat->taxonomy ) ) {
			$action = get_term_link( $cat->term_id, $cat->taxonomy );
		} else {
			$action = get_the_permalink( get_option( 'woocommerce_shop_page_id' ) );
		}

		// Set action url if option behaviour is true, Paste QUERY string after for filter and orderby support
		$query_string = ! empty( $_SERVER['QUERY_STRING'] ) ? '?' . add_query_arg( array( 'ppp' => false ), $_SERVER['QUERY_STRING'] ) : null;
		$action = $action . $query_string;

		?><form method="post" action="<?php echo esc_url( $action ); ?>" class="codevz-products-per-page">
			<select name="ppp" onchange="this.form.submit()">

				<?php

				$default_ppp = Codevz_Plus::option( 'woo_items_per_page', $ppp );

				if ( $numbers[0] > $default_ppp ) {

					echo '<option value="' . esc_attr( $default_ppp ) . '"' . ( in_array( $default_ppp, $numbers ) ? ' selected="selected"' : '' ) . '>' . sprintf( esc_html__( '%d Products', 'codevz-plus' ), esc_html( $default_ppp ) ) . '</option>';

				}

				foreach( $numbers as $key => $value ) :

					?><option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $ppp ); ?>><?php
						/* translators: %s is replaced with string */
						printf( esc_html__( '%d Products', 'codevz-plus' ), esc_html( $value ) );
					?></option><?php

				endforeach;

			?></select><?php

		?></form><?php

	}

	/**
	 * WooCommerce products per page
	 * 
	 * @return array
	 */
	public function related_products( $args ) {

		$columns = (int) Codevz_Plus::option( 'woo_related_col' );

		$args['columns'] 		= $columns;
		$args['posts_per_page'] = $columns * 2;

		return $args;

	}

	/**
	 * Wishlist container shortcode.
	 * 
	 * @return string
	 */
	public function wishlist_shortcode( $a, $c = '' ) {

		wp_enqueue_style( 'cz_text_marquee' );

		return '<div class="woocommerce xtra-wishlist xtra-icon-loading" data-col="3" data-empty="' . esc_html__( 'Your wishlist is currently empty', 'codevz-plus' ) . '" data-nonce="' . wp_create_nonce( 'xtra_wishlist_content' ) . '"></div>';

	}

	/**
	 * Compare container shortcode.
	 * 
	 * @return string
	 */
	public function compare_shortcode( $a, $c = '' ) {
		return '<div class="woocommerce xtra-compare xtra-icon-loading" data-empty="' . esc_html__( 'Your products compare list is empty.', 'codevz-plus' ) . '" data-nonce="' . wp_create_nonce( 'xtra_compare_content' ) . '"></div>';
	}

	/**
	 * Get wishlist products via AJAX.
	 * 
	 * @return string
	 */
	public function wishlist_content() {

		if ( empty( $_POST['ids'] ) && empty( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'xtra_wishlist_content' ) ) {
			wp_die( '<b>' . esc_html__( 'Server error, Please reload page ...', 'codevz-plus' ) . '</b>' );
		}

		if ( isset( $_POST['check'] ) ) {

			$new = '';

			$ids = explode( ',', $_POST['ids'] );

			foreach( $ids as $id ) {

				if ( $id && $id !== 'undefined' ) {

					$id = str_replace( ' ', '', $id );

					$post = get_post( $id );

					if ( ! empty( $post->post_title ) ) {

						$new .= $id . ',';

					}

				}

			}

			wp_die( esc_html( $new ) );

		}

		$col = isset( $_POST['col'] ) ? $_POST['col'] : '3';

		wp_die( do_shortcode( '[products ids="' . esc_html( $_POST['ids'] ) . '" columns="' . esc_html( $col ) . '"]' ) );

	}

	/**
	 * Get compare products via AJAX.
	 * 
	 * @return string
	 */
	public function compare_content() {

		if ( empty( $_POST['ids'] ) && empty( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'xtra_compare_content' ) ) {
			wp_die( '<b>' . esc_html__( 'Server error, Please reload page ...', 'codevz-plus' ) . '</b>' );
		}

		$out = '';

		if ( isset( $_POST['check'] ) ) {

			$ids = explode( ',', $_POST['ids'] );

			foreach( $ids as $id ) {

				if ( $id && $id !== 'undefined' ) {

					$id = str_replace( ' ', '', $id );

					$post = get_post( $id );

					if ( ! empty( $post->post_title ) ) {

						$out .= $id . ',';

					}

				}

			}

			wp_die( esc_html( $out ) );

		} else {

			$ids = array_filter( explode( ',', $_POST['ids'] ) );

			$out .= '<table class="cz-compare cz-compare-col-' . esc_attr( count( $ids ) - 1 ) . '"><tbody>';

			$tr = [

				'general' 			=> [ 'td' => '', 'title' => '' ],
				'price' 			=> [ 'td' => '', 'title' => esc_html__( 'Price', 'codevz-plus' ) ],
				'brand' 			=> [ 'td' => '', 'title' => esc_html__( 'Brand', 'codevz-plus' ) ],
				'desc' 				=> [ 'td' => '', 'title' => esc_html__( 'Description', 'codevz-plus' ) ],
				'sku' 				=> [ 'td' => '', 'title' => esc_html__( 'Product SKU', 'codevz-plus' ) ],
				'availablity' 		=> [ 'td' => '', 'title' => esc_html__( 'Availablity', 'codevz-plus' ) ],
				'sold_individually' => [ 'td' => '', 'title' => esc_html__( 'Individual sale', 'codevz-plus' ) ],
				'tax_status' 		=> [ 'td' => '', 'title' => esc_html__( 'Tax status', 'codevz-plus' ) ],
				'weight' 			=> [ 'td' => '', 'title' => esc_html__( 'Weight', 'codevz-plus' ) ],
				'length' 			=> [ 'td' => '', 'title' => esc_html__( 'Length', 'codevz-plus' ) ],
				'height' 			=> [ 'td' => '', 'title' => esc_html__( 'Height', 'codevz-plus' ) ],
				'width' 			=> [ 'td' => '', 'title' => esc_html__( 'Width', 'codevz-plus' ) ],
				'average_rating' 	=> [ 'td' => '', 'title' => esc_html__( 'Average rating', 'codevz-plus' ) ],
				'review_count' 		=> [ 'td' => '', 'title' => esc_html__( 'Review count', 'codevz-plus' ) ],

			];

			$no_found = true;

			foreach( $ids as $id ) {

				if ( $id && $id !== 'undefined' ) {

					$id = str_replace( ' ', '', $id );

					$product = wc_get_product( $id );

					if ( empty( $product ) ) {
						continue;
					}
					$no_found = false;

					$tr[ 'general' ][ 'td' ] .= '<td><a href="' . get_permalink( $id ) . '">' . $product->get_image() . '<h4 data-id="' . $id . '">' . get_the_title( $id ) . '</h4></a>' . do_shortcode( '[add_to_cart id=' . $id . ' show_price="false"]' ) . '</td>';
					$tr[ 'price' ][ 'td' ] .= '<td>' . $product->get_price_html() . '</td>';

					$brands  = (array) get_the_terms( $id, 'product_brand', true );

					if ( ! empty( $brands[ 0 ]->term_id ) ) {

						$tr[ 'brand' ][ 'td' ] .= '<td>';
						$term_meta = get_term_meta( $brands[ 0 ]->term_id, 'thumbnail_id', true );
						$term_value = empty( $term_meta ) ? $brands[ 0 ]->name : wp_get_attachment_image( $term_meta, 'full' );
						$tr[ 'brand' ][ 'td' ] .= '<a href="' . get_term_link( $brands[ 0 ]->term_id ) . '">' . do_shortcode( $term_value ) . '</a>';
						$tr[ 'brand' ][ 'td' ] .= '</td>';

					} else {
						$tr[ 'brand' ][ 'td' ] .= '';
					}

					$tr[ 'desc' ][ 'td' ] .= $product->get_short_description() ? '<td>' . $product->get_short_description() . '</td>' : '';
					$tr[ 'sku' ][ 'td' ] .= $product->get_sku() ? '<td>' . $product->get_sku() . '</td>' : '';

					// Product stock status.
					$get_stock_status = $product->get_stock_status();
					if ( $get_stock_status === 'instock' ) {
						$get_stock_status = esc_html__( 'Instock', 'codevz-plus' );
					} else if ( $get_stock_status === 'outofstock' ) {
						$get_stock_status = esc_html__( 'Sold out', 'codevz-plus' );
					}

					$tr[ 'availablity' ][ 'td' ] .= '<td>' . ( ( $product->get_stock_quantity() || ! $product->get_manage_stock() ) ? '<i class="fa fa-check"></i>' : '<i class="fa fa-times"></i>' ) . $product->get_stock_quantity() . ' ' . ucwords( $get_stock_status ) . '</td>';
					$tr[ 'sold_individually' ][ 'td' ] .= '<td>' . ( $product->get_sold_individually() ? '<i class="fa fa-check"></i>' : '<i class="fa fa-times"></i>' ) . '</td>';

					// Product tax status.
					$get_tax_status = $product->get_tax_status();

					if ( $get_tax_status === 'taxable' ) {

						$get_tax_status = esc_html__( 'Taxable', 'codevz-plus' );

					} else if ( $get_tax_status === 'shipping' ) {

						$get_tax_status = esc_html__( 'Shipping only', 'codevz-plus' );

					} else if ( $get_tax_status === 'none' ) {

						$get_tax_status = esc_html__( 'None', 'codevz-plus' );

					}

					$tr[ 'tax_status' ][ 'td' ] .= $get_tax_status ? '<td>' . ( $get_tax_status ? '<i class="fa fa-check"></i>' : '<i class="fa fa-times"></i>' ) . ucwords( $get_tax_status ) . '</td>' : '';

					$tr[ 'weight' ][ 'td' ] .= $product->get_weight() ? '<td>' . $product->get_weight() . ' ' . get_option( 'woocommerce_weight_unit' ) . '</td>' : '<td><i class="fa fa-times"></i></td>';
					$tr[ 'length' ][ 'td' ] .= $product->get_length() ? '<td>' . $product->get_length() . ' ' . get_option( 'woocommerce_dimension_unit' ) . '</td>' : '<td><i class="fa fa-times"></i></td>';
					$tr[ 'height' ][ 'td' ] .= $product->get_height() ? '<td>' . $product->get_height() . ' ' . get_option( 'woocommerce_dimension_unit' ) . '</td>' : '<td><i class="fa fa-times"></i></td>';
					$tr[ 'width' ][ 'td' ]  .= $product->get_width()  ? '<td>' . $product->get_width() . ' ' . get_option( 'woocommerce_dimension_unit' ) . '</td>' : '<td><i class="fa fa-times"></i></td>';
					$tr[ 'average_rating' ][ 'td' ] .= '<td>' . ( $product->get_average_rating() ? '<i class="fa fa-star"></i>' . $product->get_average_rating() : '<i class="fa fa-times"></i>' ) . '</td>';
					$tr[ 'review_count' ][ 'td' ] .= '<td>' . ( $product->get_review_count() ? $product->get_review_count() : '<i class="fa fa-times"></i>' ) . '</td>';

					$all_attributes = [];
					foreach ( $ids as $id ) {
						$id = trim( $id );
						if ( empty( $id ) || $id === 'undefined' ) {
							continue;
						}
						$product = wc_get_product( $id );
						if ( ! $product ) {
							continue;
						}
						foreach ( $product->get_attributes() as $attr ) {
							$name = $attr->get_name();
							if ( ! in_array( $name, $all_attributes, true ) ) {
								$all_attributes[] = $name;
							}
						}
					}

					foreach ( $all_attributes as $attr_name ) {
						$tr[ $attr_name ] = [
							'title' => wc_attribute_label( $attr_name ),
							'td' => '',
						];
					}

					foreach ( $ids as $id ) {
						$id = trim( $id );
						if ( empty( $id ) || $id === 'undefined' ) {
							continue;
						}
						$product = wc_get_product( $id );
						if ( ! $product ) {
							continue;
						}

						foreach ( $all_attributes as $attr_name ) {
							$value = $product->get_attribute( $attr_name );

							// output values or icon if empty
							$cell = $value ? esc_html( $value ) : '<i class="fa fa-times"></i>';

							$tr[ $attr_name ]['td'] .= '<td>' . $cell . '</td>';
						}
					}

				}

			}

			foreach( $tr as $class => $inner ) {

				if ( empty( $inner['td'] ) ) {
					$inner['td'] = '<td><i class="fa fa-times"></i></td>';
				}

				$out .= '<tr class="cz-compare-tr-' . esc_attr( $class ) . '">';

				if ( $class === 'general' ) {
					$inner['title'] = '';
				}

				$out .= '<th>' . esc_html( $inner['title'] ) . '</th>';
				$out .= $inner['td'];
				$out .= '</tr>';

			}

			$out .= '</tbody></table><ul class="hide"><li></li></ul>';

			wp_die( $no_found ? '' : do_shortcode( $out ) );

		}

	}

	/**
	 * Add wishlist icon into single product page.
	 * 
	 * @return string
	 */
	public function before_add_to_cart() {

		echo '<div class="codevz-woo-add-to-cart clr ' . esc_attr( Codevz_Plus::option( 'woo_product_flex_add_to_cart' ) ? 'codevz-woo-add-to-cart-flex' : '' ) . '">';

	}

	/**
	 * Add wishlist icon into single product page.
	 * 
	 * @return string
	 */
	public function after_add_to_cart() {

		$product_id  = get_the_id();

		if ( Codevz_Plus::option( 'woo_wishlist' ) ) {

			echo '<div class="xtra-product-icons xtra-product-icons-wishlist cz_tooltip_up" data-id="' . esc_attr( $product_id ) . '">';
			echo '<i class="fa fa-heart-o xtra-add-to-wishlist" data-title="' . esc_html__( 'Add to wishlist', 'codevz-plus' ) . '"></i>';
			echo '</div>';

		}

		if ( Codevz_Plus::option( 'woo_compare' ) ) {

			echo '<div class="xtra-product-icons xtra-product-icons-compare cz_tooltip_up" data-id="' . esc_attr( $product_id ) . '">';
			echo '<i class="fa czico-shuffle xtra-add-to-compare" data-title="' . esc_html__( 'Add to compare', 'codevz-plus' ) . '"></i>';
			echo '</div>';

		}

		echo '</div>';

		// Display custom button next to Add to Cart button on single product page
		$button_title = apply_filters( 'codevz/option/woo_product_custom_button_title', get_post_meta( $product_id, 'codevz_custom_button_title', true ) );
		$button_link  = apply_filters( 'codevz/option/woo_product_custom_button_link', get_post_meta( $product_id, 'codevz_custom_button_link', true ) );

		if ( $button_title && $button_link ) {
			echo do_shortcode( '<a href="' . esc_url( $button_link ) . '" onclick="window.open(\'' . esc_url( $button_link ) . '\', \'_blank\');return false;" target="_blank" class="codevz-product-second-button button alt">' . esc_html( $button_title ) . '</a>' );
		}

		echo '<div class="clr"></div>';

	}

	/**
	 * Add custom tabs to product single page tabs.
	 * 
	 * @return Array
	 */
	public function product_tabs( $tabs ) {

		if ( Codevz_Plus::$is_free ) {

			return $tabs;

		}

		// Product ID.
		$product_id = get_the_ID();

		// Size guide tab.
		if ( Codevz_Plus::option( 'woo_product_size_guide_tab' ) ) {

			$size_guide = get_post_meta( $product_id, 'codevz_size_guide', true );
			$size_guide = $size_guide ? $size_guide : Codevz_Plus::option( 'woo_product_size_guide_tab_content' );

			if ( $size_guide ) {

				$tabs[ 'codevz-size-guide' ] = array(
					'title'     => Codevz_Plus::option( 'woo_product_size_guide_tab_title', esc_html__( 'Size guide', 'codevz-plus' ) ),
					'priority'  => 50,
					'callback'  => function() use ( $size_guide ) {

						echo do_shortcode( is_numeric( $size_guide ) ? Codevz_Plus::get_page_as_element( $size_guide ) : $size_guide );

					}

				);

			}

		}

		// FAQ tab.
		if ( Codevz_Plus::option( 'woo_product_faq_tab' ) ) {

			$faq = get_post_meta( $product_id, 'codevz_faq', true );
			$faq = $faq ? $faq : Codevz_Plus::option( 'woo_product_faq_tab_content' );

			if ( $faq ) {

				$tabs[ 'codevz-faq' ] = array(
					'title'     => Codevz_Plus::option( 'woo_product_faq_tab_title', esc_html__( 'FAQ', 'codevz-plus' ) ),
					'priority'  => 60,
					'callback'  => function() use ( $faq ) {

						echo do_shortcode( is_numeric( $faq ) ? Codevz_Plus::get_page_as_element( $faq ) : $faq );

					}

				);

			}

		}

		// Brand tab.
		if ( Codevz_Plus::option( 'woo_product_brand_tab' ) ) {

			$terms = (array) get_the_terms( $product_id, 'product_brand' );

			if ( ! empty( $terms[0] ) ) {

				foreach( $terms as $term ) {

					$tabs[ 'codevz-' . $term->slug ] = array(
						'title'     => $term->name,
						'priority'  => 70,
						'callback'  => function() use ( $term ) {

							$logo = get_term_meta( $term->term_id, 'thumbnail_id', true );
							$logo = empty( $logo ) ? '' : wp_get_attachment_image( $logo, 'full' );
							$logo = do_shortcode( $logo ? '<div class="codevz-product-brands">' . $logo . '</div>' : '' );

							echo '<h2 class="codevz-section-title mb30">' . esc_html( $term->name ) . '</h2>';
							echo '<div class="clr">' . do_shortcode( $logo ) . '<p>' . do_shortcode( $term->description ) . '</p></div>';
							echo '<a href="' . esc_url( get_term_link( $term ) ) . '" class="button" style="margin-top:30px">' . esc_html__( 'View all Products', 'codevz-plus' ) . '</a>';

						}
					);

					continue;

				}

			}

		}

		// Shipping & Returns
		if ( Codevz_Plus::option( 'woo_product_shipping_returns_tab' ) ) {

			$tabs[ 'codevz-shipping-returns' ] = array(
				'title'     => Codevz_Plus::option( 'woo_product_shipping_returns_tab_title', esc_html__( 'Shipping & Returns' , 'codevz-plus' ) ),
				'priority'  => 80,
				'callback'  => function() {

					echo do_shortcode( Codevz_Plus::option( 'woo_product_shipping_returns_tab_content', '...' ) );

				}
			);

		}

		// Adding icon to all tabs.
		if ( Codevz_Plus::option( 'woo_product_tabs_icons' ) ) {

			foreach( $tabs as $key => &$tab ) {

				switch ( $key ) {
					case 'description':
						$icon_class = 'fas fa-info-circle';
						break;
					case 'reviews':
						$icon_class = 'fas fa-star';
						break;
					case 'additional_information':
						$icon_class = 'fas fa-list';
						break;
					case 'specifications':
						$icon_class = 'fas fa-cogs';
						break;
					case 'codevz-shipping-returns':
						$icon_class = 'fas fa-truck';
						break;
					case 'codevz-faq':
						$icon_class = 'fas fa-question-circle';
						break;
					case 'codevz-size-guide':
						$icon_class = 'fas fa-ruler';
						break;
					default:
						$icon_class = 'fas fa-tag';
				}

				$tab['title'] = '<i class="' . esc_attr( $icon_class ) . ' mr10"></i> ' . wp_kses_post( $tab['title'] );
			}

		}

		return $tabs;

	}

	/**
	 * Add extra custom classes to products.
	 * 
	 * @return array
	 */
	public function product_classes( $classes, $product ) {

		$is_single = ( is_singular( 'product' ) && ! $this->singular_product_id );

		// Hover effect name.
		$hover = Codevz_Plus::option( 'woo_hover_effect' );

		if ( $hover && ! $is_single ) {
			$classes[] = 'cz_image cz_image_' . esc_attr( $hover );
		}

		// Custom badge.
		if ( get_post_meta( $product->get_id(), 'codevz_custom_badge', true ) ) {
			$classes[] = 'codevz-product-custom-badge';
		}

		if ( $is_single ) {
			$classes[] = Codevz_Plus::option( 'woo_product_ajax_add_to_cart' ) ? 'codevz-woo-ajax-add-to-cart' : '';
			$classes[] = 'codevz-woo-gallery-count-' . ( max( 5, min( 7, count( $product->get_gallery_image_ids() ) ) ) - 1 );
		}

		return $classes;

	}

	public function quickview() {
		if ( ! isset( $_POST['id'] ) && ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'xtra_quick_view' ) ) {
			wp_die( '<b>' . esc_html__( 'Server error, Please reload page ...', 'codevz-plus' ) . '</b>' );
		}

		$ajax_button = Codevz_Plus::option( 'woo_product_ajax_add_to_cart' ) ? ' codevz-woo-ajax-add-to-cart' : '';

		echo '<div class="xtra-qv-product-content' . esc_attr( $ajax_button ) . '">';
		echo do_shortcode( '[product_page id="' . esc_attr( $_POST['id'] ) . '"]' );
		echo '</div>';

		wp_die();
	}

	public function woocommerce_before_shop_loop_item_title_low() {
		echo '<div class="xtra-product-thumbnail' . esc_attr( Codevz_Plus::option( 'woo_sale_marquee' ) ? ' codevz-woo-sale-marquee' : '' ) . '">';

		$product_id = get_the_ID();

		// Countdown.
		$date = get_post_meta( $product_id, '_sale_price_dates_to', true );

		if ( $date ) {

			$time = current_time( 'timestamp' );
			$day  = ( ( $date - $time ) > 86400 ) ? '.' : '';

			if ( $date > $time ) {
				echo do_shortcode( '[cz_countdown date="' . esc_attr( gmdate( 'Y/m/d', $date + 86400 ) ) . '" day="' . esc_attr( $day ) . '"]' );
			}

		}

		// Icons.
		$wishlist 		= Codevz_Plus::option( 'woo_wishlist' );
		$compare 		= Codevz_Plus::option( 'woo_compare' );
		$quick_view 	= Codevz_Plus::option( 'woo_quick_view' );

		if ( $wishlist || $quick_view ) {

			$center = Codevz_Plus::option( 'woo_wishlist_qv_center' ) ? ' xtra-product-icons-center' : '';
			$center .= $center ? ' cz_tooltip_up' : ( ( Codevz_Plus::$is_rtl || is_rtl() ) ? ' cz_tooltip_right' : ' cz_tooltip_left' );

			echo '<div class="xtra-product-icons' . esc_attr( $center ) . '" data-id="' . esc_attr( $product_id ) . '">';
			echo $wishlist 		? '<i class="fa fa-heart-o xtra-add-to-wishlist" data-title="' . esc_html__( 'Add to wishlist', 'codevz-plus' ) . '"></i>' : '';
			echo $compare 		? '<i class="fa czico-shuffle xtra-add-to-compare" data-title="' . esc_html__( 'Add to compare', 'codevz-plus' ) . '" data-nonce="' . esc_attr( wp_create_nonce( 'xtra_compare' ) ) . '"></i>' : '';
			echo $quick_view 	? '<i class="fa czico-146-search-4 xtra-product-quick-view" data-title="' . esc_html__( 'Quick view', 'codevz-plus' ) . '" data-nonce="' . esc_attr( wp_create_nonce( 'xtra_quick_view' ) ) . '"></i>' : '';
			echo '</div>';

		}

		echo '<div class="cz_image_in">';
		echo '<div class="cz_main_image">';

	}

	// Calculate discount percentage for simple + variable products.
	public function get_product_discount_percentage( $product ) {

	    // SIMPLE
	    if ( $product->is_type( 'simple' ) ) {
	        $regular = (float) $product->get_regular_price();
	        $sale    = (float) $product->get_sale_price();
	        if ( $regular > 0 && $sale < $regular ) {
	            return round( 100 - ( $sale / $regular * 100 ) );
	        }
	        return 0;
	    }

	    // VARIABLE
	    if ( $product->is_type( 'variable' ) ) {
	        $prices = $product->get_variation_prices();
	        $max = 0;

	        foreach ( $prices['regular_price'] as $key => $regular ) {
	            $sale = $prices['sale_price'][ $key ] ?? 0;

	            if ( $regular > 0 && $sale < $regular ) {
	                $p = round( 100 - ( $sale / $regular * 100 ) );
	                if ( $p > $max ) {
	                    $max = $p;
	                }
	            }
	        }
	        return $max;
	    }

	    return 0;
	}

	public function woocommerce_before_shop_loop_item_title_high() {

		$hover = Codevz_Plus::option( 'woo_hover_effect' );
		$product = wc_get_product( get_the_ID() );

		if ( $hover && $hover !== 'simple_zoom' && ! Codevz_Plus::contains( $hover, 'no_fx' ) && $product ) {

			$fid  = $product->get_image_id();
			$gids = array_values( array_filter( $product->get_gallery_image_ids(), fn( $id ) => $id != $fid ) );

			echo '</div><div class="cz_hover_image">';
			echo do_shortcode(Codevz_Plus::get_image( $gids[0] ?? $fid, 'woocommerce_thumbnail' ) );

		}

		echo '</div></div>';

		// Discount text marquee.
		if ( Codevz_Plus::option( 'woo_sale_marquee' ) && $product->is_on_sale() ) {

			$marquee_text = sprintf(
				do_shortcode( wp_kses_post( Codevz_Plus::option( 'woo_sale_marquee_text', esc_html__( 'HOT SALE %s%% OFF ⚡', 'codevz-plus' ) ) ) ),
				$this->get_product_discount_percentage( $product )
			);
			$marquee_text = trim( str_repeat( $marquee_text . ' ', 10 ) );

			echo do_shortcode( '[cz_text_marquee marquee="' . esc_attr( $marquee_text ) . '" duration="25" duplicate="true" gap="5" direction="' . ( is_rtl() ? 'right' : 'left' ) . '"]' );

		}

		echo '</div>';

	}

	/**
	 * Quick view, wishlist and compare added popup content.
	 * 
	 * @return string
	 */
	public function popup( $content = '' ) {

		// Quickview popup.
		if ( Codevz_Plus::option( 'woo_quick_view' ) ) {

			wp_enqueue_script( 'wc-single-product' );
			wp_enqueue_script( 'wc-add-to-cart-variation' );
			wp_enqueue_script( 'wc-zoom' );
			wp_enqueue_script( 'wc-flexslider' );
			wp_enqueue_script( 'wc-photoswipe-ui-default' );
			wp_enqueue_script( 'wc-photoswipe' );
			wp_enqueue_style( 'wc-photoswipe' );
			wp_enqueue_style( 'wc-photoswipe-default-skin' );

			$content .= do_shortcode( '[cz_popup id_popup="xtra_quick_view" id="cz_xtra_quick_view" icon="fa czico-198-cancel" sk_icon="color:#ffffff;"][/cz_popup]' );

		}

		// Wishlist & Compare popup.
		if ( Codevz_Plus::option( 'woo_wishlist' ) || Codevz_Plus::option( 'woo_compare' ) ) {

			$content .= do_shortcode( '[cz_popup id_popup="xtra_wish_compare" id="cz_xtra_wish_compare" icon="fa czico-198-cancel" sk_icon="color:#ffffff;"]<i class="fa fa-check"></i><h3>...</h3><span>' . esc_html__( 'Product has been added to your list.', 'codevz-plus' ) . '</span><a href="#" class="button"><span><strong></strong><i class="fas fa-chevron-' . ( is_rtl() ? 'left' : 'right' ) . '"></i></span></a>[/cz_popup]' );

		}

		// Added to cart notification for AJAX buttons.
		if ( Codevz_Plus::option( 'woo_added_to_cart_notification' ) ) {

			$content .= '<div class="codevz-added-to-cart-notif"><i class="fa fa-check"></i><span>"<strong></strong>" ' . esc_html__( 'has been added to your cart.', 'codevz-plus' ) . '<a class="button" href="' . esc_url( get_permalink( get_option( 'woocommerce_cart_page_id' ) ) ) . '">' . esc_html__( 'View cart', 'codevz-plus' ) . '</a></span></div>';

		}

		return $content;

	}

	/**
	 * Modify checkout page and add wrap to order details.
	 * 
	 * @return string
	 */
	public function checkout_order_review_before() {
		echo '<div class="xtra-woo-checkout-details cz_sticky_col"><div class="codevz-checkout-details">';
	}

	/**
	 * Modify checkout page and add wrap to order details.
	 * 
	 * @return string
	 */
	public function checkout_order_review_after() {
		echo '</div></div>';
	}

	/**
	 * Single product add wrap div.
	 * 
	 * @return string
	 */
	public function before_single() {

		// Remove stock HTML in single page.
		add_filter( 'woocommerce_get_stock_html', '__return_null' );

		// Get current product ID.
		if ( is_singular( 'product' ) ) {

			$this->singular_product_id = get_the_ID();

		}

		// Product container.
		echo '<div class="xtra-single-product clr">';

	}

	public function after_single() {

		echo '</div>';

		// Sticky add to cart row.
		if ( is_singular( 'product' ) && Codevz_Plus::option( 'woo_product_sticky_add_to_cart' ) ) {

			echo '<div class="cz-sticky-add-to-cart" data-title="' . esc_attr( get_the_title() ) . '"><div class="row clr"></div></div>';

		}

	}

	/**
	 * Add product to recently viewed list on single product page.
	 * 
	 * @return string
	 */
	public function recently_viewed_products() {

		if ( Codevz_Plus::option( 'woo_recently_viewed_products' ) ) {

			$product_id = get_the_ID();
			$columns = (int) Codevz_Plus::option( 'woo_related_col' );

			// Get recently viewed products from cookie.
			$rvp = Codevz_Plus::_COOKIE( 'codevz_rvp' );
			$rvp = $rvp ? json_decode( wp_unslash( $rvp ), true ) : [];

			if ( is_array( $rvp ) ) {

				// Remove product ID if already exists
				$key = array_search( $product_id, $rvp );
				if ( isset( $rvp[ $key ] ) && $key !== false ) {
					unset( $rvp[ $key ] );
				}

				// Add new product ID to the beginning
				array_unshift( $rvp, $product_id );

				// Keep only unique product IDs and limit to 5
				$rvp = array_unique( $rvp );
				$rvp = array_slice( $rvp, 0, 50 );

				echo '<script>document.cookie = "codevz_rvp=' . wp_json_encode( $rvp ) . '; expires=' . esc_attr( gmdate( 'D, d M Y H:i:s \G\M\T', time() + 3600 * 24 * 30 ) ) . '; path=/";</script>';

				// Show products.
				if ( ! empty( $rvp ) ) {

					ob_start();
					echo '<div class="codevz-recently-viewed-products related products mt50">';
					echo '<h2>' . esc_html__( 'Recently viewed products', 'codevz-plus' ) . '</h2>';
					echo do_shortcode( '[products ids="' . implode( ',', $rvp ) . '" columns="' . esc_attr( $columns ) . '" limit="' . esc_attr( $columns * 2 ) . '"]' );
					echo '</div>';
					$content = ob_get_clean();

					// Render template.
					Codevz_Plus::tpl( 'related_posts', $content );

				}

			}

		}

	}

	/**
	 * Continue shopping button in cart page.
	 * 
	 * @return string
	 */
	public function continue_shopping() {
		echo '<a class="button wc-backward" href="' . esc_url( get_the_permalink( get_option( 'woocommerce_shop_page_id' ) ) ) . '">' . esc_html__( 'Continue shopping', 'codevz-plus' ) . '</a>';
	}

	/**
	 * Single product page countdown
	 * 
	 * @return string
	 */
	public function countdown() {

		$date = get_post_meta( get_the_ID(), '_sale_price_dates_to', true );

		if ( $date ) {

			$time = current_time( 'timestamp' );
			$day  = ( ( $date - $time ) > 86400 ) ? '.' : '';

			if ( $date > $time ) {

				$countdown = do_shortcode( '[cz_countdown date="' . esc_attr( gmdate( 'Y/m/d', $date + 86400 ) ) . '" day="' . esc_attr( $day ) . '"]' );
				echo '<div class="woocommerce-notices-wrapper codevz-plus-woo-countdown"><div class="woocommerce-message" role="alert" tabindex="-1">' . Codevz_Plus::option( 'woo_product_countdown', esc_html__( 'Limited-time offer – Don’t miss out! Sale ending soon!', 'codevz-plus' ) ) . ' ' . $countdown . '</div></div>';

			}

		}

	}

	/**
	 * Single product page taxonomy meta box getting from its post type.
	 * 
	 * @return string
	 */
	public function add_meta_boxes() {

		add_meta_box( 'codevz_woo_meta_box', esc_html__( 'Advanced', 'codevz-plus' ), [ $this, 'metabox_content' ], 'product', 'side', 'default' );

	}

	/**
	 * Custom product options as side meta box.
	 * 
	 * @return string
	 */
	public function metabox_content( $post ) {

		$is_free = Codevz_Plus::is_free();
		$post_id = $post->ID;

		codevz_add_field( [
			'echo'    		=> true,
			'id'    		=> 'codevz_custom_badge',
			'type'  		=> $is_free ? 'content' : 'text',
			'title' 		=> esc_html__( 'Custom Badge', 'codevz-plus' ),
			'content' 		=> Codevz_Plus::pro_badge()
		], get_post_meta( $post_id, 'codevz_custom_badge', true ) );

		codevz_add_field( [
			'echo'    		=> true,
			'id'    		=> 'codevz_custom_button_title',
			'type'  		=> $is_free ? 'content' : 'text',
			'title' 		=> esc_html__( 'Custom button title', 'codevz-plus' ),
			'content' 		=> Codevz_Plus::pro_badge()
		], get_post_meta( $post_id, 'codevz_custom_button_title', true ) );

		codevz_add_field( [
			'echo'    		=> true,
			'id'    		=> 'codevz_custom_button_link',
			'type'  		=> $is_free ? 'content' : 'text',
			'title' 		=> esc_html__( 'Custom button link', 'codevz-plus' ),
			'content' 		=> Codevz_Plus::pro_badge()
		], get_post_meta( $post_id, 'codevz_custom_button_link', true ) );

		codevz_add_field( [
			'echo'    		=> true,
			'id'    		=> 'codevz_custom_meta',
			'type'  		=> $is_free ? 'content' : 'group',
			'title' 		=> esc_html__( 'Custom product meta', 'codevz-plus' ),
			'button_title' 	=> esc_html__( 'Add', 'codevz-plus' ),
			'content' 		=> Codevz_Plus::pro_badge(),
			'fields' 		=> array(
				array(
					'id' 			=> 'title',
					'type' 			=> 'text',
					'title' 		=> esc_html__( 'Title', 'codevz-plus' )
				),
				array(
					'id' 			=> 'value',
					'type' 			=> 'textarea',
					'title' 		=> esc_html__( 'Value', 'codevz-plus' )
				),
			)
		], get_post_meta( $post_id, 'codevz_custom_meta', true ) );

		codevz_add_field( [
			'echo'    		=> true,
			'id'    		=> 'codevz_size_guide',
			'type'  		=> $is_free ? 'content' : 'select',
			'title' 		=> esc_html__( 'Size guide tab', 'codevz-plus' ),
			'options' 		=> 'posts',
			'query_args' 	=> [ 'post_type' => 'codevz_size_guide', 'posts_per_page' => -1 ],
			'default_option'=> esc_html__( '~ Default ~', 'codevz-plus' ),
			'content' 		=> Codevz_Plus::pro_badge()
		], get_post_meta( $post_id, 'codevz_size_guide', true ) );

		codevz_add_field( [
			'echo'    		=> true,
			'id'    		=> 'codevz_faq',
			'type'  		=> $is_free ? 'content' : 'select',
			'title' 		=> esc_html__( 'FAQ tab', 'codevz-plus' ),
			'options' 		=> 'posts',
			'query_args' 	=> [ 'post_type' => 'codevz_faq', 'posts_per_page' => -1 ],
			'default_option'=> esc_html__( '~ Default ~', 'codevz-plus' ),
			'content' 		=> Codevz_Plus::pro_badge()
		], get_post_meta( $post_id, 'codevz_faq', true ) );

	}

	/**
	 * Update taxonomy value on product save.
	 * 
	 * @return -
	 */
	public function save_post_product( $post_id ) {

		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
			return;
		}

		$badge 	= Codevz_Plus::_POST( 'codevz_custom_badge' );
		$title 	= Codevz_Plus::_POST( 'codevz_custom_button_title' );
		$link 	= Codevz_Plus::_POST( 'codevz_custom_button_link' );
		$meta 	= filter_input( INPUT_POST, 'codevz_custom_meta', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
		$guide 	= Codevz_Plus::_POST( 'codevz_size_guide' );
		$faq 	= Codevz_Plus::_POST( 'codevz_faq' );

		update_post_meta( $post_id, 'codevz_custom_badge', $badge );
		update_post_meta( $post_id, 'codevz_custom_button_title', $title );
		update_post_meta( $post_id, 'codevz_custom_button_link', $link );
		update_post_meta( $post_id, 'codevz_custom_meta', $meta );
		update_post_meta( $post_id, 'codevz_size_guide', $guide );
		update_post_meta( $post_id, 'codevz_faq', $faq );

	}

	// Post state for compare and wishlist in admin.
	public function display_post_states( $states, $post ) {

		$original_slugs = [ 'products-compare', 'wishlist' ];

		// WPBakery pages.
		if ( Codevz_Plus::contains( $post->post_content, 'vc_row' ) ) {
			$states[] = esc_html__( 'WPBakery', 'codevz-plus' );
		}

		// Post state for custom woocommerce pages.
		if ( in_array( $post->post_name, $original_slugs, true ) ) {
			$states[] = esc_html__( 'WooCommerce', 'codevz-plus' );
		}

		if ( $post->post_title ) {

			// Post state for custom maintenance.
			if ( $post->post_title === Codevz_Plus::option( 'maintenance_mode' ) ) {
				$states[] = esc_html__( 'Template set for: ', 'codevz-plus' ) . ' ' . esc_html__( 'Maintenance', 'codevz-plus' );
			}

			// Post state for custom header.
			if ( $post->post_title === Codevz_Plus::option( 'header_elementor' ) ) {
				$states[] = esc_html__( 'Template set for: ', 'codevz-plus' ) . ' ' . esc_html__( 'Custom Header', 'codevz-plus' );
			}

			// Post state for footer.
			if ( $post->post_title === Codevz_Plus::option( 'footer_elementor' ) ) {
				$states[] = esc_html__( 'Template set for: ', 'codevz-plus' ) . ' ' . esc_html__( 'Custom Footer', 'codevz-plus' );
			}

			// Post state for quick contact.
			if ( $post->post_title === Codevz_Plus::option( 'cf7_beside_backtotop' ) ) {
				$states[] = esc_html__( 'Template set for: ', 'codevz-plus' ) . ' ' . esc_html__( 'Quick Contact', 'codevz-plus' );
			}

		}

		return $states;
	}

	/**
	 * Move new post types menu under products menu.
	 * 
	 * @return -
	 */
	public function admin_menu() {

		// Remove post type menu.
		remove_menu_page( 'edit.php?post_type=codevz_size_guide' );
		remove_menu_page( 'edit.php?post_type=codevz_faq' );

		global $submenu;

		// Add post type as taxonomy menu under products menu.
		$submenu['edit.php?post_type=product'][] = [ esc_html__( 'Size guide', 'codevz-plus' ), 'manage_options', 'edit.php?post_type=codevz_size_guide' ];
		$submenu['edit.php?post_type=product'][] = [ esc_html__( 'FAQ', 'codevz-plus' ), 'manage_options', 'edit.php?post_type=codevz_faq' ];

	}

	// Remove duplicated custom woo pages.
	public function admin_init() {

		if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'page' ) {

			$base_slugs = [ 'products-compare', 'wishlist' ];

			foreach ( $base_slugs as $base ) {

				// Delete duplicates like -2, -3, -2-2 etc.
				foreach ( range( 2, 6 ) as $i ) {

					$slug = $base . '-' . $i;
					$page = get_page_by_path( $slug, OBJECT, 'page' );
					if ( $page && ! empty( $page->post_name ) && $page->post_name === $slug ) {
						wp_trash_post( $page->ID );
					}

					// retry again -ID
					$slug_sub = $slug . '-' . $i;
					$page_sub = get_page_by_path( $slug_sub, OBJECT, 'page' );
					if ( $page_sub && ! empty( $page_sub->post_name ) && $page_sub->post_name === $slug_sub ) {
						wp_trash_post( $page_sub->ID );
					}

				}

				// Create original page if not exists
				$original_page = get_page_by_path( $base, OBJECT, 'page' );

				if ( empty( $original_page ) ) {

					// Set correct shortcode
					$shortcode = ( $base === 'wishlist' ) ? '[cz_wishlist]' : '[cz_compare]';

					// Create new page
					wp_insert_post( [
						'post_title'   => ucwords( str_replace( '-', ' ', $base ) ),
						'post_name'    => $base,
						'post_status'  => 'publish',
						'post_type'    => 'page',
						'post_content' => $shortcode,
					] );

				}

			}

		}

	}

	/**
	 * Customize product meta HTML tags for styling.
	 * 
	 * @return string
	 */
	public function product_meta() {

		global $product;

		$id = $product->get_id();

		$sku 	= $product->get_sku();
		$cats 	= wc_get_product_category_list( $id, ', ', '', '' );
		$tags 	= wc_get_product_tag_list( $id, ', ', '', '' );
		$stock 	= $product->get_availability();
		$stock 	= isset( $stock[ 'availability' ] ) ? $stock[ 'availability' ] : '';
		$status = $product->get_stock_status();
		$icon 	= ( $status === 'outofstock' ? '<i class="fa fa-shop-lock" aria-hidden="true"></i>' : '<i class="fa fa-check" data-title="' . esc_attr( $stock ) . '" aria-hidden="true"></i>' );
		$custom_meta = get_post_meta( $id, 'codevz_custom_meta', true );

		// Show brand name and logo in single product page.
		$brands = (array) get_the_terms( $id, 'product_brand', true );
		foreach( $brands as $term ) {

			if ( ! empty( $term->name ) ) {

				$name = $term->name;
				$link = get_term_link( $term );

				// Logo.
				$logo = get_term_meta( $term->term_id, 'thumbnail_id', true );
				$logo = empty( $logo ) ? '' : wp_get_attachment_image( $logo, 'full' );
				$logo = $logo ? '<div class="codevz-product-brands cz_tooltip_up"><a href="' . esc_url( $link ) . '" data-title="' . esc_attr( $term->name ) . '">' . do_shortcode( $logo ) . '</a></div>' : '';

				// Brand name.
				//$brand =  $logo ? esc_html( $name ) : '<a href="' . esc_url( $link ) . '">' . esc_html( $name ) . '</a>';
				$brand =  '<a href="' . esc_url( $link ) . '">' . esc_html( $name ) . '</a>';

				break;

			}

		}

		echo '<div class="product_meta clr">';

		echo !empty( $logo ) ? do_shortcode( $logo ) : ''; // brand logo.

		echo !empty( $brand ) ? '<div class="codevz-pm-brand"><strong>' . esc_html__( 'Brand', 'codevz-plus' ) . '</strong><span>' . do_shortcode( $brand ) . '</span></div>' : '';
		echo $sku  ? '<div class="codevz-pm-sku"><strong>' . esc_html__( 'SKU', 'codevz-plus' ) . '</strong><span>' . esc_html( $sku ) . '</span></div>' : '';
		echo $stock? '<div class="codevz-pm-status codevz-pm-status-' . esc_attr( $status ) . '"><strong>' . esc_html__( 'Status', 'codevz-plus' ) . '</strong><span>' . do_shortcode( preg_replace( '/(\d+)/', '<b>$1</b>', $stock ) . $icon ) . '</span></div>' : '';

		// Custom meta.
		foreach( (array) $custom_meta as $key => $meta ) {

			if ( isset( $meta[ 'title' ] ) && isset( $meta[ 'value' ] ) ) {

				echo '<div class="codevz-pm-' . esc_attr( sanitize_title_with_dashes( $meta[ 'title' ] ) ) . ' clr"><strong>' . esc_html( $meta[ 'title' ] ) . '</strong><span>' . esc_html( $meta[ 'value' ] ) . '</span></div>';

			}

		}

		// Category and tags.
		echo $tags ? '<div class="codevz-pm-tags clr"><strong>' . esc_html__( 'Tags', 'codevz-plus' ) . '</strong><span>' . do_shortcode( $tags ) . '</span></div>' : '';
		echo $cats ? '<div class="codevz-pm-cats clr"><strong>' . esc_html__( 'Categories', 'codevz-plus' ) . '</strong><span>' . do_shortcode( $cats ) . '</span></div>' : '';

		echo '</div>';

		// Check.
		if ( Codevz_Plus::$is_free ) {
			return;
		}

		// Custom text after meta.
		if ( Codevz_Plus::option( 'woo_after_product_meta' ) ) {

			echo '<div class="codevz-custom-product-meta">';

			if ( Codevz_Plus::$preview ) {
				echo '<i class="codevz-section-focus fas fa-cog" data-section="product"></i>';
			}

			echo do_shortcode( Codevz_Plus::option( 'woo_after_product_meta' ) );

			echo '</div>';

		}

		// Secure cards payment section.
		$secure_title = Codevz_Plus::option( 'woo_secure_payment' );
		$secure_image = Codevz_Plus::option( 'woo_secure_payment_image' );

		if ( $secure_title || $secure_image ) {

			echo '<div class="codevz-woo-secure-payment">';
			echo $secure_title ? '<div>' . esc_html( $secure_title ) . '</div>' : '';
			echo $secure_image ? '<img src="' . esc_url( $secure_image ) . '" alt="Secure payment cards" />' : '';
			echo '</div>';

		}

	}

	/**
	 * Hook to add custom option field to product attribute settings
	 * 
	 * @return string
	 */
	public function attribute_add_fields() {

		$id = absint( Codevz_Plus::_GET( 'edit' ) );
		$value = get_option( 'codevz_pa_' . $id );

		$types = [
			'select' 	=> esc_html__( 'Dropdown', 'codevz-plus' ),
			'button' 	=> esc_html__( 'Button', 'codevz-plus' ),
			'thumbnail' => esc_html__( 'Thumbnail', 'codevz-plus' ),
			'color' 	=> esc_html__( 'Color', 'codevz-plus' ),
		];

		echo '<' . ( $id ? 'tr' : 'div' ) . ' class="form-field" style="position: relative">';
		echo '<th scope="row" valign="top">';
		echo '<label for="my-field">' . esc_html__( 'Attribute swatch type', 'codevz-plus' ) . '</label>';
		echo '</th><td>';

		if ( Codevz_Plus::is_free() ) {

			echo do_shortcode( Codevz_Plus::pro_badge() );

		} else {

			echo '<select name="codevz_pa" id="codevz_pa">';

			foreach ( $types as $key => $title ) {
				echo '<option value="' . esc_attr( $key ) . '"' . ( $value === $key ? ' selected' : '' ) . '>' . esc_html( $title ) . '</option>';
			}

			echo '</select>';

		}

		echo '<p class="description">' . esc_html__( "Default output for product attributes' variations in HTML: dropdowns or swatches.", 'codevz-plus' ) . '</p>';

		echo '</td></' . ( $id ? 'tr' : 'div' ) . '>';

	}

	/**
	 * Save option value when adding/editing product attribute term.
	 * 
	 * @return -
	 */
	public function attribute_added_updated( $id ) {

		update_option( 'codevz_pa_' . $id, Codevz_Plus::_POST( 'codevz_pa' ), false );

	}

	/**
	 * Add color picker to attribute taxonomy configuration.
	 * 
	 * @return string
	 */
	public function add_variation_colorpicker( $term ) {

		wp_enqueue_style( 'wp-color-picker' );
		wp_enqueue_script( 'wp-color-picker' );

		?>
			<tr class="form-field">
				<th scope="row" valign="top">
					<label for="codevz_attribute_color"><?php esc_html_e( 'Attribute Color', 'codevz-plus' ); ?></label>
				</th>
				<td>
					<input type="text" class="codevz-color-picker" name="codevz_attribute_color" id="codevz_attribute_color" value="<?php echo esc_attr( empty( $term->term_id ) ? '' : get_term_meta( $term->term_id, 'codevz_attribute_color', true ) ); ?>" />
					<p class="description"><?php esc_html_e( 'Select a color for this attribute.', 'codevz-plus' ); ?></p>
				</td>
			</tr>
			<script type="text/javascript">
				jQuery( function( $ ) {
					$( '.codevz-color-picker' ).wpColorPicker();
				});
			</script>
		<?php

	}

	/**
	 * Save custom color field value when adding product attribute term
	 * 
	 * @return string
	 */
	public function save_variation_colorpicker( $term_id, $tt_id, $taxonomy ) {

		update_term_meta( $term_id, 'codevz_attribute_color', Codevz_Plus::_POST( 'codevz_attribute_color' ) );

	}

	/**
	 * Add custom column to the term list table e.g. 'pa_color'
	 * 
	 * @return array
	 */
	public function add_variation_color_column( $columns ) {

		// Create an array of columns to add
		$new_columns = array(
			'codevz_attribute_color' => esc_html__( 'Color', 'codevz-plus' ),
		);

		// Split the columns array into two parts
		$first_columns = array_slice( $columns, 0, 1 ); // Get the first column
		$last_columns = array_slice( $columns, 1 ); // Get the rest of the columns

		// Merge the new column after the first position
		$columns = array_merge( $first_columns, $new_columns, $last_columns );

		return $columns;

	}

	/**
	 * Display content for the custom column in pa_* taxonomy.
	 * 
	 * @return array
	 */
	public function show_variation_color_column( $content, $column_name, $term_id ) {

		if ( 'codevz_attribute_color' === $column_name ) {
			$custom_color = get_term_meta( $term_id, 'codevz_attribute_color', true );
			$content .= '<div style="border-radius:100px;width: 20px; height: 20px;border-radius: 100px; margin: 1px auto 0; background-color: ' . esc_attr( $custom_color ) . '"></div>';
		}

		return $content;

	}

	/**
	 * Modify variations output HTML and add swatches like color, thumbnail and button type.
	 * 
	 * @return array
	 */
	public function variations_output( $html, $args ) {

		global $product;

		// Attribute type.
		$type = get_option( 'codevz_pa_' . wc_attribute_taxonomy_id_by_name( $args[ 'attribute' ] ) );

		// Default variation selected for product.
		$default = $product->get_default_attributes();

		// Get all product variations.
		$variations = $product->get_available_variations();

		ob_start();

		echo '<div class="codevz-variations codevz-variations-' . esc_attr( $type ) . '" cz_tooltip_up>';

		// Get terms.
		$terms = wc_get_product_terms(
			$product->get_id(),
			$args[ 'attribute' ],
			array(
				'fields' => 'all',
			)
		);

		if ( $type === 'color' ) {

			foreach( $terms as $term ) {

				$selected = ( isset( $default[ $args[ 'attribute' ] ] ) && $default[ $args[ 'attribute' ] ] === $term->slug ) ? ' checked="checked"' : '';

				echo '<input type="radio" id="codevz_' . esc_attr( $term->term_id ) . '" name="attribute_' . esc_attr( $args[ 'attribute' ] ) . '" value="' . esc_attr( $term->slug ) . '"' . wp_kses_post( (string) $selected ) . ' />';
				echo '<label data-title="' . esc_attr( $term->name ) . '" for="codevz_' . esc_attr( $term->term_id ) . '" style="background-color:' . esc_attr( get_term_meta( $term->term_id, 'codevz_attribute_color', true ) ) . '">' . esc_html( $term->name ) . '</label>';

			}

		} else if ( $type === 'button' ) {

			foreach( $terms as $term ) {

				$selected = ( isset( $default[ $args[ 'attribute' ] ] ) && $default[ $args[ 'attribute' ] ] === $term->slug ) ? 'checked="checked"' : '';

				echo '<input type="radio" id="codevz_' . esc_attr( $term->term_id ) . '" name="attribute_' . esc_attr( $args[ 'attribute' ] ) . '" value="' . esc_attr( $term->slug ) . '"' . wp_kses_post( (string) $selected ) . ' />';
				echo '<label for="codevz_' . esc_attr( $term->term_id ) . '">' . esc_html( $term->name ) . '</label>';

			}

		} else if ( $type === 'thumbnail' ) {

			foreach( $terms as $term ) {

				foreach( $variations as $key => $variation ) {

					if ( isset( $variation[ 'attributes' ][ 'attribute_' . $args[ 'attribute' ] ] ) && $variation[ 'attributes' ][ 'attribute_' . $args[ 'attribute' ] ] === $term->slug ) {

						if ( ! empty( $variation[ 'image' ][ 'url' ] ) ) {

							$image = $variation[ 'image' ][ 'url' ];

							break;

						}

					}

				}

				$selected = ( isset( $default[ $args[ 'attribute' ] ] ) && $default[ $args[ 'attribute' ] ] === $term->slug ) ? 'checked="checked"' : '';

				echo '<input type="radio" id="codevz_' . esc_attr( $term->term_id ) . '" name="attribute_' . esc_attr( $args[ 'attribute' ] ) . '" value="' . esc_attr( $term->slug ) . '"' . wp_kses_post( (string) $selected ) . ' />';
				echo '<label data-title="' . esc_attr( $term->name ) . '" for="codevz_' . esc_attr( $term->term_id ) . '"><img loading="lazy" src="' . esc_attr( isset( $image ) ? $image : '' ) . '" alt="thumbnail" /></label>';

				$image = '';

			}

		}

		echo do_shortcode( $html );

		echo '</div>';

		$html = ob_get_clean();

		return $html;

	}

	/**
	 * Display cart page related products according to cart items.
	 * 
	 * @return string
	 */
	public function cart_related_products() {

		echo '</div>'; // Close cart total div.

		$cart = wc()->cart->get_cart();

		$linked_producs = false;

		// Find linked products.
		foreach( $cart as $cart_item_key => $cart_item ) {

			$linked_producs = empty( get_post_meta( $cart_item['product_id'], '_crosssell_ids', true ) ) ? false : true;

		}

		// Check and find related.
		if ( ! $linked_producs ) {

			$related = array_reduce( $cart, function( $result, $cart_item ) {
				return array_merge( $result, wc_get_related_products( $cart_item[ 'product_id' ] ) );
			}, [] );

			$related = array_slice( array_diff( array_unique( $related ), array_column( $cart, 'product_id' ) ), 0, 2 );

			if ( ! empty( $related ) ) {

				echo '<div class="codevz-cart-related related products"><h2>' . esc_html__( 'You may be interested in ...', 'codevz-plus' ) . '</h2>';
				echo do_shortcode( '[products ids="' . implode( ',', $related ) . '" columns="2"]' );
				echo '</div>';

			}

		}

	}

	/**
	 * Display cart page interested products when cart is empty.
	 * 
	 * @return string
	 */
	public function cart_empty_products_section() {

		$products = wc_get_products( [ 'limit' => 4, 'orderby' => 'rand' ] );

		if ( ! empty( $products ) ) {

			$ids = [];

			foreach( $products as $product ) {
				$ids[] = $product->get_id();
			}

			echo '<div class="related products mt50"><h2>' . esc_html__( 'You may be interested in ...', 'codevz-plus' ) . '</h2>';
			echo do_shortcode( '[products ids="' . implode( ',', $ids ) . '" columns="4"]' );
			echo '</div>';

		}

	}

	/**
	 * Show 3 steps above on cart, checkout and order completion pages
	 * 
	 * @return string
	 */
	public function cart_checkout_steps() {

		$steps = [
			[
				'name' => esc_html__( 'Shopping cart', 'codevz-plus' ),
				'link' => wc_get_cart_url()
			],
			[
				'name' => esc_html__( 'Checkout details', 'codevz-plus' ),
				'link' => wc_get_checkout_url()
			],
			[
				'name' => esc_html__( 'Order complete', 'codevz-plus' ),
				'link' => '#'
			]
		];

		echo '<div class="codevz-cart-checkout-steps' . ( Codevz_Plus::option( 'woo_cart_checkout_steps' ) === 'vertical' ? ' codevz-cart-checkout-steps-col' : '' ) . '">';

		foreach( $steps as $index => $step ) {

			$active_class = ( is_cart() && ! $index ) || ( is_checkout() && $index === 1 ) || ( is_order_received_page() && $index === 2 ) ? 'codevz-current-step' : '';

			echo '<a href="' . esc_url( $step['link'] ) . '" class="' . esc_attr( $active_class ) . '"><span>' . esc_html( $index + 1 ) . '</span><strong>' . esc_html( $step['name'] ) . '</strong></a>';
			echo $index !== 2 ? '<i class="fa czico-Icon-Navigation-Arrow-Right" aria-hidden="true"></i>' : '';

		}

		echo '</div>';

	}

	// Avatar above the account nav.
	public function my_account_avatar() {

		$user = wp_get_current_user();

		echo '<div class="codevz-account-avatar">';
		echo '<img loading="lazy" src="' . esc_url( get_avatar_url( $user->ID, [ 'size' => 150 ] ) ) . '">';
		echo '<strong><small style="font-size: 15px; opacity: .6;">' . esc_html__( 'Welcome', 'codevz-plus' ) . ',</small>' . esc_html( ucwords( $user->first_name . ' ' . $user->last_name ) ) . '</strong>';
		echo '<span>' . esc_html( $user->user_email ) . '</span>';
		echo '</div>';

		echo '<script>document.addEventListener("DOMContentLoaded",function(){const a=document.querySelector(".codevz-account-avatar"),b=document.querySelector(".woocommerce-MyAccount-navigation");a&&b&&(b.prepend(a),a.style.display="block");});</script>';

	}

	// Fix new links in my account nav.
	public function my_account_endpoints() {

		add_rewrite_endpoint( 'tracking', EP_PAGES );
		add_rewrite_endpoint( 'wishlist', EP_PAGES );
		add_rewrite_endpoint( 'viewed',   EP_PAGES );
		add_rewrite_endpoint( 'reviews',  EP_PAGES );

		flush_rewrite_rules();

	}

	// Add a new menus to my account page.
	public function my_account_menus( $items ) {

		$menus = [
			'tracking' 	=> esc_html__( 'Tracking orders', 'codevz-plus' ),
			'wishlist' 	=> esc_html__( 'Wishlist', 'codevz-plus' ),
			'viewed' 	=> esc_html__( 'Recently viewed', 'codevz-plus' ),
			'reviews' 	=> esc_html__( 'Reviews', 'codevz-plus' ),
		];

		return array_slice( $items, 0, 2, true ) + $menus + array_slice( $items, 2, null, true );

	}

	// My account wishlist tab content.
	public function my_account_wishlist() {

		echo '<div class="codevz-my-account-wishlist">';
		echo '<h3 class="codevz-section-title">' . esc_html__( 'Wishlist', 'codevz-plus' ) . '</h3><br />';
		echo '<div class="woocommerce xtra-wishlist xtra-icon-loading" data-col="2" data-empty="' . esc_html__( 'Your wishlist is currently empty', 'codevz-plus' ) . '" data-nonce="' . esc_attr( wp_create_nonce( 'xtra_wishlist_content' ) ) . '"></div>';
		echo '</div>';

	}

	// My account recently viewed tab content.
	public function my_account_viewed() {

		$product_id = get_the_ID();

		// Get recently viewed products from cookie.
		$rvp = Codevz_Plus::_COOKIE( 'codevz_rvp' );
		$rvp = $rvp ? json_decode( wp_unslash( $rvp ), true ) : null;

		if ( is_array( $rvp ) ) {

			// Remove product ID if already exists
			$key = array_search( $product_id, $rvp );
			if ( isset( $rvp[ $key ] ) && $key !== false ) {
				unset( $rvp[ $key ] );
			}

			// Add new product ID to the beginning
			array_unshift( $rvp, $product_id );

			// Keep only unique product IDs and limit to 5
			$rvp = array_slice( array_unique( $rvp ), 0, 20 );

			// Show products.
			if ( ! empty( $rvp ) ) {

				echo '<div class="codevz-recently-viewed-products">';
				echo '<h3 class="codevz-section-title">' . esc_html__( 'Recently viewed products', 'codevz-plus' ) . '</h3>';
				echo do_shortcode( '[products ids="' . esc_attr( implode( ',', $rvp ) ) . '" columns="2"]' );
				echo '</div>';

			}

		} else {
			echo '<h3 class="codevz-section-title">' . esc_html__( 'Recently viewed products', 'codevz-plus' ) . '</h3>';
			echo 'No products found here. Please explore some products.';
		}

	}

	// My account reviews tab content.
	public function my_account_reviews() {
		$user_id = get_current_user_id();
		$reviews = get_comments(array(
			'user_id' => $user_id,
			'status' => 'approve',
			'post_type' => 'product',
		));

		echo '<h3 class="codevz-section-title">' . esc_html__( 'Your rating and reviews', 'codevz-plus' ) . '</h3>';

		if ( $reviews ) {

			foreach( $reviews as $review ) {

				echo '<div class="codevz-my-account-reviews">';

				$product = wc_get_product( $review->comment_post_ID );

				echo '<div class="codevz-my-account-reviews-title">';
				echo do_shortcode( $product->get_image() );
				echo '<span><strong>' . esc_html( $product->get_name() ) . '</strong><span>' . esc_html( $review->comment_date ) . '</span></span></div>';

				echo '<div>';
				echo do_shortcode( wc_get_rating_html( get_comment_meta( $review->comment_ID, 'rating', true ) ) );
				echo '<p>' . esc_html( $review->comment_content ) . '</p>';
				echo '</div>';

				echo '</div>';

			}

		} else {
			echo '<p>' . esc_html__( 'You haven\'t submitted any reviews yet, Please visit the product pages of your orders to share your feedback.', 'codevz-plus' ) . '</p>';
		}
	}

	// My account order tracking tab content.
	public function my_account_tracking() { ?>

		<div class="codevz-order-tracking-form">
			<h3 class="codevz-section-title"><?php esc_html_e( 'Tracking order', 'codevz-plus' ); ?></h3>
			<form action="" method="post">
				<p>
					<label for="order_id"><?php esc_html_e( 'Order ID', 'codevz-plus' ); ?></label>
					<input type="text" name="order_id" id="order_id" required>
				</p>
				<p>
					<label for="customer_email"><?php esc_html_e( 'Your email', 'codevz-plus' ); ?></label>
					<input type="email" name="customer_email" id="customer_email" required>
				</p>
				<input type="submit" value="<?php esc_html_e( 'Track order', 'codevz-plus' ); ?>">
			</form>
		</div>

		<?php

		$order_id 		= Codevz_Plus::_POST( 'order_id' );
		$customer_email = Codevz_Plus::_POST( 'customer_email' );

		// Display order tracking results
		if ( $order_id && $customer_email ) {

			// Check if wc_get_order function is available
			$order = wc_get_order( intval( $order_id ) );

			if ( $order && $order->get_billing_email() === sanitize_email( $customer_email ) ) {

				echo '<div class="codevz-order-tracking related products mt50">';

				// Display order status as steps
				$order_status = $order->get_status();

				echo '<h2>' . esc_html__( 'Tracking info of order #', 'codevz-plus' ) . wp_kses_post( (string) $order->get_order_number() ) . '</h2>';
				echo '<table>';

				echo '<thead><tr><th>' . esc_html__( 'Order number', 'codevz-plus' ) . '</th><th>' . wp_kses_post( (string) $order->get_order_number() ) . '</th></tr></thead>';

				echo '<tr><th>' . esc_html__( 'Order date', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) wc_format_datetime($order->get_date_created()) ) . '</td></tr>';
				echo '<tr><th>' . esc_html__( 'Order status', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) wc_get_order_status_name($order_status) ) . '</td></tr>';
				echo '<tr><th>' . esc_html__( 'Last update', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) wc_format_datetime($order->get_date_modified()) ) . '</td></tr>';

				// Products List
				$items = $order->get_items();
				echo '<tr><th>' . esc_html__( 'Product(s)', 'codevz-plus' ) . '</th><td>';
				foreach( $items as $item ) {
					echo '<strong>' . wp_kses_post( (string) $item->get_name() ) . '</strong> x ' . wp_kses_post( (string) $item->get_quantity() ) . ' = ' . wp_kses_post( (string) wc_price( $item->get_subtotal() ) ) . '<br />';
				}
				echo '</td></tr>';

				echo '<tr><th>' . esc_html__( 'Total amount', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) wc_price($order->get_total()) ) . '</td></tr>';
				echo '<tr><th>' . esc_html__( 'Name', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ) . '</td></tr>';
				echo '<tr><th>' . esc_html__( 'Phone', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) $order->get_billing_phone() ) . '</td></tr>';
				echo '<tr><th>' . esc_html__( 'Email', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) $order->get_billing_email() ) . '</td></tr>';
				echo '<tr><th>' . esc_html__( 'Address', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) $order->get_formatted_billing_address() ) . '</td></tr>';
				echo '<tr><th>' . esc_html__( 'Order note', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) $order->get_customer_note() ) . '</td></tr>';
				echo '<tr><th>' . esc_html__( 'Delivery method', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) $order->get_shipping_method() ) . '</td></tr>';
				echo '<tr><th>' . esc_html__( 'Payment method', 'codevz-plus' ) . '</th><td>' . wp_kses_post( (string) $order->get_payment_method_title() ) . '</td></tr>';

				echo '</table>';
			} else {
				echo '<strong class="mt50" style="display:block;color:red">' . esc_html__( 'Invalid Order ID or Email. Please try again.', 'codevz-plus' ) . '</strong>';
			}

			echo '</div>';

		}

	}

	// Fix my account login form container div.
	public static function before_my_account_form() {
		echo '<div class="codevz-woo-login-form">';
	}

	// Fix my account login form container div.
	public static function after_my_account_form() {
		echo '</div>';
	}

	// Modify products title and add category name under it.
	public function category_under_title( $title, $product_id ) {

		if ( ! $product_id ) {
			return $title;
		}

		$subtitle = Codevz_Plus::option( 'woo_category_under_title' );

		if ( $subtitle === 'brand' ) {

			$brands = (array) get_the_terms( $product_id, 'product_brand', true );

			foreach( $brands as $term ) {

				if ( ! empty( $term->name ) ) {

					$title .= '<span class="codevz-product-category-after-title">' . esc_html( $term->name ) . '</span>';

					break;

				}

			}

		} else if ( $subtitle ) {

			$post = get_post( $product_id );

			if ( $post && $post->post_type === 'product' && $product_id !== $this->singular_product_id ) {

				// Get product categories
				$categories = get_the_terms( $product_id, 'product_cat' );

				// Check if there are categories and get the first one
				if ( $categories && ! is_wp_error( $categories ) ) {

					$category = array_shift( $categories );

					$title .= '<span class="codevz-product-category-after-title">' . $category->name . '</span>';

				}

			}

		}

		return $title;

	}

	// Product gallery navigation arrows.
	public function woo_flexslider_options( $options ) {

		$options['directionNav'] = true;

		return $options;

	}

	// Add brand logo to taxonomy page.
	public function term_description() {

		if ( is_product_taxonomy() ) {

			$term = get_queried_object();

			if ( $term && ! is_wp_error( $term ) ) {

				$desc_text 	 = get_term_field( 'description', $term, $term->taxonomy );
				$description = $desc_text;

				// Logo for brand page.
				if ( is_tax( 'product_brand' ) ) {

					$logo = get_term_meta( $term->term_id, 'thumbnail_id', true );
					$logo = empty( $logo ) ? '' : wp_get_attachment_image( $logo, 'full' );
					$logo = do_shortcode( $logo ? '<div class="codevz-product-brands">' . $logo . '</div>' : '' );

					$description = $logo . $description;

				}

				// Add category cover to cat page.
				if ( is_tax( 'product_cat' ) ) {

					$cover = get_term_meta( $term->term_id, 'thumbnail_id', true );

					if ( $cover ) {
						$description = '<div class="codevz-product-brands">' . wp_get_attachment_image( $cover, 'full' ) . '</div>' . $description;
					}

				}

				// Term description limit height.
				if ( $desc_text && Codevz_Plus::option( 'term_desc_limit' ) ) {

					$description = do_shortcode( '[cz_show_more_less height="' . esc_attr( Codevz_Plus::option( 'term_desc_limit_height', '200px' ) ) . '" gradient="' . esc_attr( Codevz_Plus::option( 'term_desc_limit_color' ) ) . '" more="" icon_more="fa czico-200-down-arrow" less="" icon_less="fa czico-199-up-arrow" center="true"]' . $description . '[/cz_show_more_less]' );

				}

				// Show desc.
				if ( $desc_text ) {
					echo '<div class="term-description">' . do_shortcode( $description ) . '</div>';
				}

			}

		}

	}

	// Add short description under product rating on shop archive pages
	public function products_short_desc() {

		if ( Codevz_Plus::option( 'woo_products_short_desc' ) ) {

			global $product;

			$desc = $product->get_short_description();

			if ( $desc ) {

				$desc = wp_strip_all_tags( $desc );

				echo '<div class="codevz-product-short-desc">';
				echo wp_kses_post( (string) mb_strimwidth( $desc, 0, 90, '...' ) );
				echo '</div>';

			}

		}

	}

	// Move outofstock to end of loop.
	public function move_outofstock_end_of_loop( $posts_clauses ) {

		if ( ! is_admin() && is_woocommerce() && ( is_shop() || is_product_category() || is_product_tag() ) ) {

			if ( Codevz_Plus::contains( $posts_clauses['where'], "product" ) ) {

				$db = Codevz_Plus::database();

				$posts_clauses['join'] .= " INNER JOIN $db->postmeta istockstatus ON ($db->posts.ID = istockstatus.post_id) ";
				$posts_clauses['orderby'] = " istockstatus.meta_value ASC, " . $posts_clauses['orderby'];
				$posts_clauses['where'] = " AND istockstatus.meta_key = '_stock_status' AND istockstatus.meta_value <> '' " . $posts_clauses['where'];

			}

		}

		return $posts_clauses;
	}

	// Change product title tag to another tag.
	public function add_new_product_title_tag() {

		$tag = Codevz_Plus::option( 'woo_product_title_tag', 'h2' );

		if ( $tag !== 'hide' ) {

			the_title( '<' . esc_attr( $tag ) . ' class="product_title entry-title">', '</' . esc_attr( $tag ) . '>' );

		}
	}

	// How many people viewing this product.
	public function log_sessions_script() {

		$live = Codevz_Plus::option( 'woo_live' );

		if ( $live === 'cart' ) {

			global $product;

			$product_id = $product->get_id();
			$transient_name = 'codevz_track_user_cart_count_' . $product_id;

			$count = get_transient( $transient_name );
			$count = $count ? $count : 0;

			if ( $count === 1 ) {
				$message = sprintf(
					esc_html__( '%s person has added this product to their cart', 'codevz-plus' ),
					'<span>' . esc_html( $count ) . '</span>'
				);
			} else {
				$message = sprintf(
					esc_html__( '%s people have added this product to their cart', 'codevz-plus' ),
					'<span>' . esc_html( $count ) . '</span>'
				);
			}

			echo '<div class="codevz-product-live codevz-product-live-cart' . ( $count > 0 ? '' : ' hidden' ) . '"><i class="fa czico-039-commerce-3 mr8"></i>' . wp_kses_post( $message ) . '</div>';

			echo '<script>
				function codevzWooLiveCart() {

					var element = jQuery( ".codevz-product-live-cart" );

					jQuery.ajax({
						url: "' . esc_url( admin_url( 'admin-ajax.php' ) ) . '",
						type: "POST",
						data: {
							action: "codevz_woo_live_cart",
							product_id: jQuery( ".cart .button" ).val(),
							nonce: "' . esc_html( wp_create_nonce( 'codevz_woo_live' ) ) . '"
						},
						success: function( response ) {

							element[ response > 0 ? "removeClass" : "addClass" ]( "hidden" );

							element.find( "span" ).html( response );
						}
					});
				}

				setInterval( codevzWooLiveCart, 20000 );

			</script>';

		} else if ( $live === 'sessions' ) {

			$sessions = get_transient( 'codevz_active_sessions' );
			$sessions = $sessions ? $sessions : [ 1 ];
			$sessions = count( $sessions );

			if ( $sessions === 1 ) {
				$message = sprintf(
					esc_html__( '%s person is viewing this product right now', 'codevz-plus'),
					'<span>' . esc_html( $sessions ) . '</span>'
				);
			} else {
				$message = sprintf(
					esc_html__( '%s people are viewing this product right now', 'codevz-plus'),
					'<span>' . esc_html( $sessions ) . '</span>'
				);
			}

			echo '<div class="codevz-product-live codevz-product-live-sessions' . ( $sessions <= 1 ? ' hidden' : '' ) . '"><i class="fa fa-eye mr8"></i>' . wp_kses_post( $message ) . '</div>';

			echo '<script>
				function codevzWooSessions() {

					var element = jQuery( ".codevz-product-live-sessions" );

					jQuery.ajax({
						url: "' . esc_url( admin_url( 'admin-ajax.php' ) ) . '",
						type: "POST",
						data: {
							action: "codevz_woo_sessions",
							nonce: "' . esc_html( wp_create_nonce( 'codevz_woo_live' ) ) . '"
						},
						success: function( response ) {

							element[ response <= 1 ? "addClass" : "removeClass" ]( "hidden" );

							element.find( "span" ).html( response );
						}
					});
				}

				setInterval( codevzWooSessions, 20000 );

			</script>';

		}

	}

	// Get and set active session.
	public function codevz_woo_sessions() {

		if ( ! wp_verify_nonce( Codevz_Plus::_POST( 'nonce' ), 'codevz_woo_live' ) ) {
			wp_die();
		}

		if ( session_status() == PHP_SESSION_NONE ) {
			session_start();
		}

		if ( session_status() == PHP_SESSION_ACTIVE ) {

			$session_id = session_id();
			$active_sessions = get_transient( 'codevz_active_sessions' );

			if ( ! $active_sessions ) {

				$active_sessions = [ $session_id => time() ];
				set_transient( 'codevz_active_sessions', $active_sessions, 60 );

			} else {

				foreach( $active_sessions as $key => $value ) {

					if ( isset( $active_sessions[ $key ] ) && time() - $value > 60 ) {
						unset( $active_sessions[ $key ] );
					}

				}

				$active_sessions[ $session_id ] = time();

				set_transient( 'codevz_active_sessions', $active_sessions, 60 );

			}

			// Log count of active sessions array
			$active_sessions_count = count( $active_sessions );

			echo esc_html( $active_sessions_count );

		} else {

			echo 1;

		}

		wp_die();

	}

	// Track carts count on add to cart.
	public function track_cart_addition( $cart_item_key, $product_id ) {

		$transient_name = 'codevz_track_user_cart_count_' . $product_id;

		$count = get_transient( $transient_name );
		$count = $count ? $count : 0;

		$count++;

		set_transient( $transient_name, $count, DAY_IN_SECONDS * 2 );

	}

	// Track carts count on remove.
	public function track_cart_removal( $cart_item_key, $cart ) {

		$product_id 	= $cart->removed_cart_contents[ $cart_item_key ][ 'product_id' ];
		$transient_name = 'codevz_track_user_cart_count_' . $product_id;

		$count = get_transient( $transient_name );
		$count = $count ? $count : 0;

		if ( $count > 0 ) {

			$count--;

			set_transient( $transient_name, $count, DAY_IN_SECONDS * 2 );

		}

	}

	// Get all users carts count.
	public function codevz_woo_live_cart() {

		if ( empty( $_POST['product_id'] ) || empty( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'codevz_woo_live' ) ) {
			wp_die();
		}

		$transient_name = 'codevz_track_user_cart_count_' . esc_html( $_POST['product_id'] );

		$count = get_transient( $transient_name );
		$count = $count ? $count : 0;

		echo esc_html( $count );

		wp_die();

	}

	// Add phone assistance box after Add to Cart and before Product Meta
	public function phone_assistance() {

		if ( Codevz_Plus::option( 'woo_phone_assist' ) ) {

			?><a class="codevz-woo-assistance" href="<?php echo esc_url( Codevz_Plus::option( 'woo_phone_assist_link', '#' ) ); ?>">
				<i class="<?php echo Codevz_Plus::option( 'woo_phone_assist_icon', 'fa fa-phone' ); ?>"></i>
				<div>
					<span>
						<?php echo do_shortcode( Codevz_Plus::option( 'woo_phone_assist_title', esc_html__( 'Our customer representative is waiting for you', 'codevz-plus' ) ) ); ?>
					</span>
					<br>
					<strong>
						<?php echo do_shortcode( Codevz_Plus::option( 'woo_phone_assist_subtitle', esc_html__( 'Call for immediate assistance at 1-312-800-0000', 'codevz-plus' ) ) ); ?>
					</strong>
				</div>
			</a><?php

		}

	}

	// Protect wishlist and compare page slug.
	public function prevent_slug_change( $data, $postarr ) {

		// Check if the post type is 'page' or 'elementor' and if the ID is set
		if ( ! empty( $postarr['ID'] ) && Codevz_Plus::contains( $data['post_type'], [ 'page', 'elementor' ] ) ) {

			// Array of slugs you want to protect
			$protected_slugs = [ 'products-compare', 'wishlist' ];

			// Get the current post slug
			$current_slug = get_post_field( 'post_name', $postarr['ID'] );

			// If the current slug is in the protected list and it is being changed, revert it
			if ( $current_slug && in_array( $current_slug, $protected_slugs ) && $data['post_name'] != $current_slug ) {
				$data['post_name'] = $current_slug;
			}

			// Array of slugs you want to protect
			$protected_titles   = [];
			$protected_titles[] = Codevz_Plus::option( 'header_elementor' );
			$protected_titles[] = Codevz_Plus::option( 'footer_elementor' );
			$protected_titles[] = Codevz_Plus::option( 'cf7_beside_backtotop' );
			$protected_titles   = array_filter( $protected_titles );

			// Get the current post title
			$current_title = get_post_field( 'post_title', $postarr['ID'] );

			// If the current slug is in the protected list and it is being changed, revert it
			if ( $current_title && in_array( $current_title, $protected_titles ) && $data['post_title'] != $current_title ) {
				$data['post_title'] = $current_title;
			}

		}

		return $data;

	}

	// Cart page product SKU under title.
	public function cart_product_sku( $cart_item, $cart_item_key ) {

		if ( ! empty( $cart_item['data'] ) ) {

			$sku = $cart_item['data']->get_sku();

			if ( $sku ) {
				echo '<small style="opacity:.6">' . esc_html__( 'SKU', 'codevz-plus' ) . ': ' . esc_html( $sku ) . '</small>';
			}

		}

	}

	// Calculate and store discount percentage
	public function calculate_and_store_discount_percentage( $post_id ) {

		if ( get_post_type( $post_id ) != 'product' ) {
			return;
		}

		$regular_price  = get_post_meta( $post_id, '_regular_price', true );
		$sale_price 	= get_post_meta( $post_id, '_sale_price', true );
		
		if ( $regular_price && $sale_price && $regular_price > 0 ) {
			$discount_percentage = (( $regular_price - $sale_price ) / $regular_price ) * 100;
			update_post_meta( $post_id, '_discount_percentage', $discount_percentage );
		} else {
			delete_post_meta( $post_id, '_discount_percentage' );
		}

	}

	// Add custom sorting options to WooCommerce Sort
	public function woocommerce_catalog_orderby( $sortby ) {

		$sortby = array(
			'menu_order' 		=> esc_html__('Default', 'codevz-plus'),
			'popularity' 		=> esc_html__('Popular', 'codevz-plus'),
			'featured' 			=> esc_html__('Featured', 'codevz-plus'),
			'rating' 			=> esc_html__('Top Rated', 'codevz-plus'),
			'relevance' 		=> esc_html__('Relevance', 'codevz-plus'),
			'date' 				=> esc_html__('Sort by latest', 'codevz-plus'),
			'date-asc' 			=> esc_html__('Sort by oldest', 'codevz-plus'),
			'price' 			=> esc_html__('Price low to high', 'codevz-plus'),
			'price-desc' 		=> esc_html__('Price high to low', 'codevz-plus'),
			'discount-desc' 	=> esc_html__('Highest Discount', 'codevz-plus'),
			'discount-asc' 		=> esc_html__('Lowest Discount', 'codevz-plus'),
			'title' 			=> esc_html__('Alphabetically, A-Z', 'codevz-plus'),
			'title-desc' 		=> esc_html__('Alphabetically, Z-A', 'codevz-plus')
		);

		return $sortby;

	}

	// Add custom sorting functionality
	public function woocommerce_get_catalog_ordering_args( $args, $orderby, $order ) {

		switch( $orderby ) {

			case 'featured':
				add_filter( 'posts_clauses', function( $clauses ) {
					$db = Codevz_Plus::database();

					// Get the IDs of featured products
					$featured_ids = wc_get_featured_product_ids();
					if ( empty( $featured_ids ) ) {
						return $clauses;
					}

					// Modify the ORDER BY clause to prioritize featured products
					$ids = implode( ',', array_map( 'intval', $featured_ids ) );
					$clauses['orderby'] = "FIELD( {$db->posts}.ID, {$ids} ) DESC, {$db->posts}.post_date DESC";

					return $clauses;
				});
				break;

			case 'discount':
				$args['orderby']  = 'meta_value_num';
				$args['meta_key'] = '_discount_percentage';
				$args['order']    = ( 'ASC' === $order ) ? 'ASC' : 'DESC';
				break;

		}

		return $args;

	}


	// Shop columns grid icons.
	public function shop_columns_grid_icons() {

		if ( ! Codevz_Plus::option( 'woo_columns_selector' ) ) {
			return;
		}

		$site_id = ( function_exists( 'is_multisite' ) && is_multisite() ) ? get_current_blog_id() : '1';

		?>

		<div class="codevz-woo-columns cz_tooltip_up">
			<span data-col="2" data-title="<?php esc_html_e( 'Two columns', 'codevz-plus' ); ?>">
				<div><i></i><i></i></div>
				<div><i></i><i></i></div>
			</span>

			<span data-col="3" data-title="<?php esc_html_e( 'Three columns', 'codevz-plus' ); ?>">
				<div><i></i><i></i><i></i></div>
				<div><i></i><i></i><i></i></div>
			</span>

			<span data-col="4" data-title="<?php esc_html_e( 'Four columns', 'codevz-plus' ); ?>">
				<div><i></i><i></i><i></i><i></i></div>
				<div><i></i><i></i><i></i><i></i></div>
			</span>
		</div>

		<script type="text/javascript">
			document.addEventListener('DOMContentLoaded', function() {
				const gridIcons = document.querySelectorAll('.codevz-woo-columns span');
				const productList = document.querySelector('.products');
				const products = productList.querySelectorAll('.product');
				const currentClass = 'codevz-current';
				const productsPerPage = <?php echo esc_html( $this->columns() ); ?>;

				function updateProductClasses(columns) {
					productList.className = `products columns-${columns}`;
					products.forEach((product, index) => {
						product.classList.toggle('first', index % columns === 0);
						product.classList.toggle('last', (index + 1) % columns === 0);
					});

					// Set the current class on the selected icon
					gridIcons.forEach(icon => {
						icon.classList.toggle(currentClass, icon.dataset.col == columns);
					});

					// Set a cookie to remember the selected columns
					document.cookie = `codevz_columns_<?php echo esc_html( $site_id ); ?>=${columns}; path=/; max-age=${60 * 60 * 24 * 30}`;

					// If clicked on 4 or 5 then hide sidebar.
					const sidebars = document.querySelectorAll('.sidebar_primary, .sidebar_secondary');
					if ( sidebars.length === 'XXXXXX' ) {
						const wrapper = document.querySelector('.s6, .s8, .s9, .s12');
						const orderingContainer = document.querySelector('.woocommerce-ordering');
						const colButtons = document.querySelectorAll('.codevz-woo-columns [data-col]');

						const originalWrapperClass = wrapper.classList.contains('s6') ? 's6' : (wrapper.classList.contains('s8') ? 's8' : 's9');

						const hideSidebars = () => {
						    sidebars.forEach(el => el.style.display = 'none');
						    wrapper.classList.remove('s6', 's8', 's9');
						    wrapper.classList.add('s12');
						};

						const showSidebars = () => {
						    sidebars.forEach(el => el.style.display = '');
						    wrapper.classList.remove('s12');
						    wrapper.classList.add(originalWrapperClass);
						};

						if (!document.querySelector('.codevz-woo-sidebar-toggle')) {
							const toggleIcon = document.createElement('i');
							toggleIcon.className = 'fa czico-add-compare codevz-woo-sidebar-toggle';
							toggleIcon.setAttribute('role', 'button');
							toggleIcon.setAttribute('tabindex', '0');
							toggleIcon.setAttribute('aria-label', 'Toggle Sidebar');

							const isRTL = document.body.classList.contains('rtl');
							toggleIcon.style.float = isRTL ? 'left' : 'right';
							toggleIcon.style.cursor = 'pointer';
							toggleIcon.style.display = 'flex';
							toggleIcon.style.alignItems = 'center';
							toggleIcon.style.justifyContent = 'center';

							const wooColumns = document.querySelector('.codevz-woo-columns');
							if (orderingContainer && wooColumns) {
							    let targetHeight = orderingContainer.offsetHeight;
							    if (targetHeight === 45) {
							        targetHeight = 40;
							    } else {
							        targetHeight = targetHeight;
							    }
							    toggleIcon.style.height = targetHeight + 'px';
							    toggleIcon.style.lineHeight = targetHeight + 'px';
							    toggleIcon.style.width = targetHeight + 'px';
							    
							    if (isRTL) {
							        toggleIcon.style.marginRight = '10px';
							    } else {
							        toggleIcon.style.marginLeft = '10px';
							    }
							}

						    orderingContainer.parentNode.insertBefore(toggleIcon, orderingContainer);

						    toggleIcon.addEventListener('click', () => {
						        if (wrapper.classList.contains('s12')) {
						            showSidebars();
						            const col3Btn = document.querySelector('.codevz-woo-columns [data-col="3"]');
						            if (col3Btn) col3Btn.click();
						        } else {
						            hideSidebars();
						        }
						    });
						}

						const handleLayout = (cols) => {
						    if (cols > 3) {
						        hideSidebars();
						    } else {
						        showSidebars();
						    }
						};

						colButtons.forEach(btn => {
						    btn.addEventListener('click', () => {
						        const clickedCol = parseInt(btn.getAttribute('data-col'));
						        handleLayout(clickedCol);
						    });
						});
					} // Sidebar toggle.

				} // updateProductClasses

				// Each icon click.
				gridIcons.forEach(icon => {
					icon.addEventListener('click', () => {
						updateProductClasses(icon.dataset.col);
					});
				});

				// Check for the cookie and use its value if it exists
				const columns = document.cookie.split('; ').find(row => row.startsWith('codevz_columns_<?php echo esc_html( $site_id ); ?>='));
				const defaultColumns = columns ? columns.split('=')[1] : productsPerPage;

				if ( defaultColumns < 5 ) {
					updateProductClasses(defaultColumns);
				}

				// Fix shop grid icons align with select fields.
				var wooColumns = document.querySelector('.codevz-woo-columns'),
					wooOrdering = document.querySelector('.woocommerce-ordering');

				if (wooColumns && wooOrdering) {

					if ( wooOrdering.offsetHeight == 45 ) {
						wooOrdering.offsetHeight = 40;
					} else {
						wooOrdering.offsetHeight = wooOrdering.offsetHeight - 1;
					}

					wooColumns.style.height = wooOrdering.offsetHeight + 'px';

				}

			});
		</script>
		<?php

	}

	// Change add to cart text on single product page
	public function add_to_cart_title( $title ) {

		global $product;

		if ( $product && $product->get_stock_status() === 'instock' ) {

			$add_to_cart = Codevz_Plus::option( 'woo_add_to_cart_title' );

			return esc_html( $add_to_cart ? $add_to_cart : $title ); 

		}

		return $title;
	}

	// Shop archive title tag.
	public function archive_title_tag() {

		$tag = Codevz_Plus::option( 'woo_shop_title_tag', 'h2' );

		if ( $tag !== 'hide' && ! Codevz_Plus::contains( Codevz_Plus::option( 'page_title' ), [ '3', '4', '5', '6' ] ) ) {

			echo '<' . esc_attr( $tag ) . ' class="page-title">';
			woocommerce_page_title();
			echo '</' . esc_attr( $tag ) . '>';

		}

	}

	// Waitlist system box HTML form.
	public function waitlist_box() {
		global $product;
		if ( $product->is_in_stock() ) {
			return;
		}
		?>
		<div id="codevz-waitlist-box" style="border:1px solid #67676733;padding:30px 40px;margin-top:1.5em;border-radius:10px">
			<h3 style="margin:5px 0;"><?php echo esc_html__( 'This product is currently out of stock', 'codevz-plus' ); ?></h3>
			<p style="margin:5px 0 25px;color:#666;"><?php echo esc_html__( "Don't worry! Enter your email address and we'll notify you when it's available again", 'codevz-plus' ); ?></p>
			<form id="codevz-waitlist-form">
				<input type="email" name="email" placeholder="youremail@gmail.com" required
					   style="padding:8px;width:65%;max-width:280px">
				<button type="submit" style="margin: 0 10px;padding:9px 15px">
					<?php echo esc_html__( 'Add me to waitlist', 'codevz-plus' ); ?>
				</button>
				<input type="hidden" name="product_id" value="<?php echo esc_attr( $product->get_id() ); ?>">
				<input type="hidden" name="action" value="codevz_waitlist">
				<?php wp_nonce_field( 'codevz_waitlist', 'nonce' ); ?>
			</form>
		</div>

		<script>
		jQuery(function($){
			$('#codevz-waitlist-form').on('submit', function(e){
				e.preventDefault();
				$.post('<?php echo admin_url("admin-ajax.php"); ?>', $(this).serialize(), function(r){
					if(r.success){
						$('#codevz-waitlist-box').html('<p style="font-weight:bold">'+r.data.message+'</p>');
					} else {
						alert(r.data.message);
					}
				}, 'json');
			});
		});
		</script>
		<?php
	}

	// Waitlist system Handle AJAX
	public function waitlist_handle() {
		check_ajax_referer( 'codevz_waitlist', 'nonce' );
		$email = sanitize_email( $_POST['email'] ?? '' );
		$pid   = absint( $_POST['product_id'] ?? 0 );
		if ( ! $pid || ! is_email( $email ) ) {
			wp_send_json_error( ['message'=> esc_html__( 'Invalid email address', 'codevz-plus' ) ] );
		}

		$list = (array) get_post_meta( $pid, '_codevz_waitlist', true );
		if ( ! in_array( $email, $list ) ) {
			$list[] = $email;
			update_post_meta( $pid, '_codevz_waitlist', $list );
		}
		wp_send_json_success( [ 'message'=> esc_html__( 'Thank you, Your email added to waitlist', 'codevz-plus' ) ] );
	}

	// Waitlist system Send emails when stock changes to "instock"
	public function waitlist_send_emails( $product_id, $stock_status, $product ) {
		if ( $stock_status !== 'instock' ) {
			return;
		}

		$list = (array) get_post_meta( $product_id, '_codevz_waitlist', true );
		if ( empty( $list ) ) {
			return;
		}

		// Now safely assemble using HTML outside the translated strings
		$message  = esc_html__( 'Good news!', 'codevz-plus' );
		$message .= '<br /><br />';
		$message .= sprintf( esc_html__( 'The product (%s) is back in stock', 'codevz-plus' ), esc_html( $product->get_name() ) );
		$message .= '<br /><br />';
		$message .= '👉 <a href="' . esc_url( get_permalink( $product_id ) ) . '" target="_blank" style="color:#0073aa;">' . esc_html__( 'Buy it here', 'codevz-plus' ) . '</a>';
		$message .= '<br /><br />';
		$message .= esc_html__( 'Thank you!', 'codevz-plus' );

		$subject = sprintf(
			esc_html__( 'Product Back in Stock: %s', 'codevz-plus' ), 
			$product->get_name()
		);

		foreach( $list as $email ) {
			Codevz_Plus::sendMail( $email, $subject, $message );
		}

		// Clear waitlist after emails sent
		delete_post_meta( $product_id, '_codevz_waitlist' );
	}

	// Fix single product slider duplicated images.
	public function woocommerce_product_get_gallery_image_ids( $ids, $p ) {

		$main = $p->get_image_id();
		$new  = [];

		foreach( (array) $ids as $id ) {
			if ( $id != $main ) {
				$new[] = $id;
			}
		}

		return array_values( array_unique( $new ) );

	}

    // Replace all tabs except the first.
    public function render_delay_product_tabs( $tabs ) {
        if (!is_product() || count($tabs) < 2) {
        	return $tabs;
        }

        $keys = array_keys($tabs);
        $first_tab = $keys[0]; // active tab, leave server-rendered

		foreach ($tabs as $key => $tab) {

		    if ($key === $first_tab || stripos($key, 'review') !== false) {
		    	continue;
		    }

	        // Save original callback
	        $original_callback = $tab['callback'];

	        // New callback template.
	        $tabs[$key]['callback'] = function() use ($original_callback, $key, $tab) {
	            ob_start();
	            call_user_func($original_callback, $key, $tab);
	            $content = ob_get_clean();
				Codevz_Plus::tpl( 'product_tabs', $content, "a[href='#tab-" . esc_attr( $key ) . "']" );
			};

		}

        return $tabs;
    }

}
Codevz_Plus_Woocommerce::instance();