<?php
/**
 * Add an element to fusion-builder.
 *
 * @package fusion-builder
 * @since 3.3
 */

if ( fusion_is_element_enabled( 'fusion_post_cards' ) ) {

	if ( ! class_exists( 'FusionSC_PostCards' ) ) {
		/**
		 * Shortcode class.
		 *
		 * @since 3.3
		 */
		class FusionSC_PostCards extends Fusion_Element {
			/**
			 * The one, true instance of this object.
			 *
			 * @static
			 * @access private
			 * @since 3.3
			 * @var object
			 */
			private static $instance;

			/**
			 * The counter.
			 *
			 * @access private
			 * @since 3.3
			 * @var int
			 */
			private $element_counter = 1;

			/**
			 * An array of the shortcode arguments.
			 *
			 * @access protected
			 * @since 3.3
			 * @var array
			 */
			protected $args;

			/**
			 * Shortcode name.
			 *
			 * @access public
			 * @since 3.3
			 * @var string
			 */
			public $shortcode_name;

			/**
			 * Post types.
			 *
			 * @access public
			 * @since 3.3
			 * @var mixed
			 */
			public $post_types = null;

			/**
			 * Supported taxonomies.
			 *
			 * @access public
			 * @since 3.3
			 * @var mixed
			 */
			public $taxonomies = null;

			/**
			 * Map taxonomies to post types
			 *
			 * @access public
			 * @since 3.3
			 * @var mixed
			 */
			public $taxonomy_map = [];

			/**
			 * Whether we are requesting from editor.
			 *
			 * @access protected
			 * @since 3.3
			 * @var array
			 */
			protected $live_request = false;

			/**
			 * Constructor.
			 *
			 * @access public
			 * @since 1.0
			 */
			public function __construct() {
				parent::__construct();
				$this->shortcode_name = 'fusion_post_cards';
				add_filter( 'fusion_attr_post-cards-shortcode', [ $this, 'attr' ] );
				add_filter( 'fusion_attr_post-cards-shortcode-pagination', [ $this, 'attr_pagination' ] );
				add_filter( 'fusion_attr_post-cards-shortcode-posts', [ $this, 'attr_posts' ] );

				add_shortcode( $this->shortcode_name, [ $this, 'render' ] );

				// Ajax mechanism for query related part.
				add_action( 'wp_ajax_get_fusion_post_cards', [ $this, 'ajax_query' ] );

				add_action( 'pre_get_posts', [ $this, 'alter_shop_loop' ], 20 );
			}

			/**
			 * Creates or returns an instance of this class.
			 *
			 * @static
			 * @access public
			 * @since 3.3
			 */
			public static function get_instance() {

				// If an instance hasn't been created and set to $instance create an instance and set it to $instance.
				if ( null === self::$instance ) {
					self::$instance = new FusionSC_PostCards();
				}
				return self::$instance;
			}

			/**
			 * Gets the default values.
			 *
			 * @static
			 * @access public
			 * @since 3.3
			 * @return array
			 */
			public static function get_element_defaults() {
				$fusion_settings = awb_get_fusion_settings();
				return [
					'animation_direction'   => 'left',
					'animation_offset'      => $fusion_settings->get( 'animation_offset' ),
					'animation_speed'       => '',
					'animation_type'        => '',
					'animation_delay'       => 0,
					'hide_on_mobile'        => fusion_builder_default_visibility( 'string' ),
					'class'                 => '',
					'id'                    => '',
					'columns'               => $fusion_settings->get( 'woocommerce_shop_page_columns' ),
					'columns_medium'        => '0',
					'columns_small'         => '0',
					'column_spacing'        => $fusion_settings->get( 'woocommerce_archive_grid_column_spacing' ),
					'row_spacing'           => $fusion_settings->get( 'woocommerce_archive_grid_column_spacing' ),
					'layout'                => 'grid',
					'margin_bottom'         => '',
					'margin_left'           => '',
					'margin_right'          => '',
					'margin_top'            => '',
					'number_posts'          => '0',
					'offset'                => '',
					'order'                 => 'DESC',
					'orderby'               => 'date',
					'orderby_term'          => 'name',
					'post_card'             => '0',
					'post_card_list_view'   => '0',
					'post_type'             => 'post',
					'posts_by'              => 'all',
					'scrolling'             => 'pagination',
					'source'                => 'posts',
					'terms_by'              => '',
					'flex_align_items'      => 'flex-start',
					'out_of_stock'          => 'include',
					'show_hidden'           => 'no',

					// Carousel.
					'scroll_items'          => '',
					'mouse_scroll'          => 'no',
					'autoplay'              => 'no',
					'show_nav'              => 'yes',

					// Slider.
					'slider_animation'      => 'fade',
					'nav_margin_top'        => '40px',

					// Separator styles.
					'separator_style_type'  => 'none',
					'separator_sep_color'   => '',
					'separator_width'       => '',
					'separator_alignment'   => '',
					'separator_border_size' => '',
				];
			}

			/**
			 * Used to set any other variables for use on front-end editor template.
			 *
			 * @static
			 * @access public
			 * @since 3.3
			 * @return array
			 */
			public static function get_element_extras() {
				$fusion_settings = awb_get_fusion_settings();
				$is_builder      = ( function_exists( 'fusion_is_preview_frame' ) && fusion_is_preview_frame() ) || ( function_exists( 'fusion_is_builder_frame' ) && fusion_is_builder_frame() );

				$extras = [
					'load_more_text'                    => apply_filters( 'avada_load_more_item_name', esc_attr__( 'Load More Items', 'fusion-builder' ) ),
					'pagination_global'                 => apply_filters( 'fusion_builder_blog_pagination', '' ),
					'pagination_range_global'           => apply_filters( 'fusion_pagination_size', $fusion_settings->get( 'pagination_range' ) ),
					'pagination_start_end_range_global' => apply_filters( 'fusion_pagination_start_end_size', $fusion_settings->get( 'pagination_start_end_range' ) ),
					'visibility_medium'                 => $fusion_settings->get( 'visibility_medium' ),
					'visibility_small'                  => $fusion_settings->get( 'visibility_small' ),
				];

				if ( $is_builder ) {
					$post_types = fusion_post_cards()->fetch_post_types();
					if ( is_array( $post_types ) ) {
						foreach ( $post_types as $post_type ) {
							/* translators: %s: "Post type label". */
							$extras[ 'load_more_text_' . $post_type->name ] = apply_filters( 'avada_load_more_' . $post_type->name . '_name', sprintf( esc_attr__( 'Load More %s', 'fusion-builder' ), $post_type->label ) );
						}
					}
				}
				return $extras;
			}

			/**
			 * Gets the query data.
			 *
			 * @static
			 * @access public
			 * @since 3.3
			 * @param array $defaults An array of defaults.
			 * @return void
			 */
			public function ajax_query( $defaults ) {
				check_ajax_referer( 'fusion_load_nonce', 'fusion_load_nonce' );
				$this->query( $defaults );
			}

			/**
			 * Gets the query data.
			 *
			 * @static
			 * @access public
			 * @since 3.3
			 * @param array $defaults The default args.
			 * @return array|Object
			 */
			public function query( $defaults ) {
				// Return if there's a query override.
				$query_override = apply_filters( 'fusion_post_cards_shortcode_query_override', null, $defaults );

				if ( $query_override ) {
					return $query_override;
				}

				// From Ajax Request.
				if ( isset( $_POST['model'] ) && ! apply_filters( 'fusion_builder_live_request', false ) ) { // phpcs:ignore WordPress.Security.NonceVerification
					$defaults           = wp_unslash( $_POST['model']['params'] ); // phpcs:ignore WordPress.Security
					$return_data        = [];
					$this->live_request = true;
					add_filter( 'fusion_builder_live_request', '__return_true' );
				}

				$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
				if ( is_front_page() || is_home() ) {
					$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : ( ( get_query_var( 'page' ) ) ? get_query_var( 'page' ) : 1 );
				}
				$defaults['paged'] = $paged;

				if ( 'terms' === $defaults['source'] ) {
					$posts = $this->term_query( $defaults, $this->live_request );
				} else {
					$posts = $this->post_query( $defaults, $this->live_request );
				}

				if ( ! $this->live_request ) {
					return $posts;
				}
				$this->args = $defaults;
				// Check for post card design choice.
				if ( 0 === (int) $defaults['post_card'] ) {
					$return_data['placeholder'] = $this->get_placeholder();
					echo wp_json_encode( $return_data );
					wp_die();
				}

				// Ensure ajax column CSS does not conflict.
				$cid = isset( $_POST['cid'] ) ? sanitize_text_field( wp_unslash( $_POST['cid'] ) ) : $defaults['post_card']; // phpcs:ignore WordPress.Security.NonceVerification.Missing
				FusionBuilder()->set_global_shortcode_parent( $cid );

				// Either we have terms or posts depending on what we want.
				$have_posts = ( 'terms' === $defaults['source'] && ! empty( $posts ) ) || ( in_array( $defaults['source'], [ 'posts', 'related', 'up_sells', 'cross_sells' ], true ) && $posts->have_posts() );

				if ( ! $have_posts ) {
					$return_data['placeholder'] = $this->get_placeholder( 'empty' );
					echo wp_json_encode( $return_data );
					wp_die();
				}

				$return_data['paged']         = $paged;
				$return_data['max_num_pages'] = '';

				if ( $have_posts ) {

					if ( in_array( $this->args['source'], [ 'posts', 'related', 'up_sells', 'cross_sells' ], true ) ) {
						ob_start();
						while ( $posts->have_posts() ) {
							$posts->the_post();

							$this->render_custom();
						}
						$return_data['loop_product']  = ob_get_clean();
						$return_data['max_num_pages'] = $posts->max_num_pages;
					} else {
						ob_start();
						foreach ( $posts as $term ) {
							$GLOBALS['wp_query']->is_tax         = true;
							$GLOBALS['wp_query']->is_archive     = true;
							$GLOBALS['wp_query']->queried_object = $term;

							$this->render_custom();
						}
						$return_data['loop_product'] = ob_get_clean();
					}
				}

				// Process children elements CSS.
				$return_data['nested_css'] = apply_filters( 'fusion_post_cards_elements_css', [] );

				echo wp_json_encode( $return_data );
				wp_die();
			}

			/**
			 * Get the terms.
			 *
			 * @static
			 * @access public
			 * @since 3.3
			 * @param array $defaults The default args.
			 * @param bool  $live_request Whether this is a live editor request.
			 * @return array|Object
			 */
			public function term_query( $defaults, $live_request = false ) {
				if ( '0' == $defaults['offset'] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
					$defaults['offset'] = '';
				}
				$taxonomy = $defaults['terms_by'];
				$args     = [
					'taxonomy'   => $taxonomy,
					'number'     => max( (int) $defaults['number_posts'], 0 ),
					'hide_empty' => false,
					'orderby'    => $defaults['orderby_term'],
					'order'      => $defaults['order'],
				];

				if ( '' !== $defaults['offset'] ) {
					$args['offset'] = $defaults['offset'];
				}

				// Filter by taxonomy.
				foreach ( [ 'include', 'exclude' ] as $filter ) {
					$option = $filter . '_term_' . $taxonomy;
					$terms  = isset( $defaults[ $option ] ) && ! empty( $defaults[ $option ] ) ? $defaults[ $option ] : false;

					if ( $terms ) {
						if ( false !== strpos( $terms, ',' ) ) {
							$terms = explode( ',', $terms );
						} elseif ( false !== strpos( $terms, '|' ) ) {
							$terms = explode( '|', $terms );
						}
						$args[ $filter ] = $terms;
					}
				}

				$args['post_cards_query'] = true;

				return get_terms( $args );
			}

			/**
			 * Get single posts.
			 *
			 * @access public
			 * @since 3.3
			 * @param array $defaults The default args.
			 * @param bool  $live_request Whether this is a live editor request.
			 * @return array|Object
			 */
			public function post_query( $defaults, $live_request = false ) {
				$fusion_settings = awb_get_fusion_settings();

				$is_builder  = ( function_exists( 'fusion_is_preview_frame' ) && fusion_is_preview_frame() ) || ( function_exists( 'fusion_is_builder_frame' ) && fusion_is_builder_frame() );
				$target_post = $is_builder && function_exists( 'Fusion_Template_Builder' ) ? Fusion_Template_Builder()->get_target_example() : false;
				$post_id     = $target_post ? $target_post->ID : get_the_ID();
				$post_id     = isset( $_POST['post_id'] ) ? $_POST['post_id'] : $post_id; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput, WordPress.Security.NonceVerification.Missing

				if ( '0' == $defaults['offset'] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
					$defaults['offset'] = '';
				}

				$args = [
					'post_type'      => isset( $defaults['source'] ) && 'posts' === $defaults['source'] ? $defaults['post_type'] : get_post_type( $post_id ),
					'posts_per_page' => (int) $defaults['number_posts'],
					'paged'          => $defaults['paged'],
				];

				$args['orderby'] = $defaults['orderby'];
				$args['order']   = $defaults['order'];
				if ( 'product' === $defaults['post_type'] ) {
					$args['orderby'] = ( isset( $_GET['product_orderby'] ) ) ? sanitize_text_field( wp_unslash( $_GET['product_orderby'] ) ) : $defaults['orderby']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
					$args['order']   = ( isset( $_GET['product_order'] ) ) ? sanitize_text_field( wp_unslash( $_GET['product_order'] ) ) : $defaults['order']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

					if ( function_exists( 'WC' ) ) {
						$ordering_args   = WC()->query->get_catalog_ordering_args( $args['orderby'], $args['order'] );
						$args['orderby'] = $ordering_args['orderby'];
						$args['order']   = $ordering_args['order'];

						if ( $ordering_args['meta_key'] ) {
							$args['meta_key'] = $ordering_args['meta_key']; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
						}
					}
					$args['posts_per_page'] = '0' === $defaults['number_posts'] ? $fusion_settings->get( 'woo_items' ) : (int) $defaults['number_posts'];
					$args['posts_per_page'] = ( isset( $_GET['product_count'] ) ) ? (int) $_GET['product_count'] : $args['posts_per_page']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				}

				if ( '' !== $defaults['offset'] ) {
					$args['offset'] = $defaults['offset'];
				}

				// Filter by taxonomy.
				if ( 'all' !== $defaults['posts_by'] ) {
					$post_type_taxonomies = get_object_taxonomies( $defaults['post_type'], 'objects' );
					$taxonomy             = $defaults['posts_by'];

					// If taxonomy is used by post type, then lets filter for it.
					if ( isset( $post_type_taxonomies[ $taxonomy ] ) ) {
						foreach ( [ 'include', 'exclude' ] as $filter ) {
							$option = $filter . '_' . $taxonomy;
							$terms  = isset( $defaults[ $option ] ) && ! empty( $defaults[ $option ] ) ? $defaults[ $option ] : false;

							if ( $terms ) {
								if ( false !== strpos( $terms, ',' ) ) {
									$terms = explode( ',', $terms );
								} elseif ( false !== strpos( $terms, '|' ) ) {
									$terms = explode( '|', $terms );
								}

								$terms = is_array( $terms ) ? $terms : [ $terms ];

								$tax_args = [
									'taxonomy' => $taxonomy,
									'field'    => 'id',
									'terms'    => apply_filters( 'avada_element_term_selection', $terms, $defaults['post_type'], $taxonomy ),
								];

								if ( 'exclude' === $filter ) {
									$tax_args['operator'] = 'NOT IN';
								}
								$args['tax_query'][] = $tax_args;
							}
						}
					}
				}

				// Product visibility option.
				if ( 'product' === $defaults['post_type'] && 'no' === $defaults['show_hidden'] ) {
					$args['tax_query']['relation'] = 'AND';
					$args['tax_query'][]           = [
						'taxonomy' => 'product_visibility',
						'field'    => 'slug',
						'terms'    => [ 'exclude-from-catalog', 'exclude-from-search' ],
						'operator' => 'NOT IN',
					];
				}

				// If out of stock are set not to show, hide them.
				if ( 'product' === $defaults['post_type'] && 'exclude' === $defaults['out_of_stock'] ) {
					$args['meta_query'][] = [
						'key'     => '_stock_status',
						'value'   => 'outofstock',
						'compare' => 'NOT IN',
					];
				}

				// Related items.
				if ( 'related' === $defaults['source'] ) {
					$terms = get_the_terms( $post_id, $defaults['terms_by'] );
					$terms = empty( $terms ) ? [] : $terms;
					$terms = wp_list_pluck( $terms, 'slug' );

					unset( $args['meta_query'] );

					$args['post__not_in'] = [ $post_id ];
					$args['tax_query']    = []; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
					$args['tax_query'][]  = [
						'taxonomy' => $defaults['terms_by'],
						'field'    => 'slug',
						'terms'    => $terms,
					];
				}

				// Cross sells && upsells.
				if ( in_array( $defaults['source'], [ 'cross_sells', 'up_sells' ], true ) && class_exists( 'WooCommerce' ) ) {
					$product    = wc_get_product( $post_id );
					$cart_sells = ( is_page() || is_cart() && is_object( WC()->cart ) ) ? WC()->cart->get_cross_sells() : [];
					$sells      = 'cross_sells' === $defaults['source'] && 0 < count( $cart_sells ) ? $cart_sells : [ -1 ];

					if ( 'product' === get_post_type( $post_id ) && $product ) {
						$sells = 'cross_sells' === $defaults['source'] ? $product->get_cross_sell_ids() : $product->get_upsell_ids();
						$sells = 0 < count( $sells ) ? $sells : [ -1 ];
					}

					unset( $args['meta_query'] );
					unset( $args['tax_query'] );

					$args['post_type']    = 'product';
					$args['post__not_in'] = [ $post_id ];
					$args['post__in']     = $sells;
				}

				// Ajax returns protected posts, but we just want published.
				if ( $live_request ) {
					$args['post_status'] = 'publish';
				}

				$args['post_cards_query'] = true;

				return fusion_cached_query( apply_filters( 'fusion_post_cards_shortcode_query_args', $args ) );
			}

			/**
			 * Render the shortcode.
			 *
			 * @access public
			 * @since  3.3
			 * @param  array  $args    Shortcode parameters.
			 * @param  string $content Content between shortcode.
			 * @return string          HTML output
			 */
			public function render( $args, $content = '' ) {
				$fusion_settings = awb_get_fusion_settings();
				$this->defaults  = self::get_element_defaults();

				// We need dynamic defaults for post type.
				if ( isset( $args['posts_by'] ) ) {
					$this->defaults[ 'include_' . $args['posts_by'] ] = '';
					$this->defaults[ 'exclude_' . $args['posts_by'] ] = '';
				}
				if ( isset( $args['terms_by'] ) ) {
					$this->defaults[ 'include_term_' . $args['terms_by'] ] = '';
					$this->defaults[ 'exclude_term_' . $args['terms_by'] ] = '';
				}
				$this->args = FusionBuilder::set_shortcode_defaults( $this->defaults, $args, 'fusion_post_cards' );

				$this->validate_args();

				$html = '';

				if ( 0 === (int) $this->args['post_card'] ) {
					return $this->get_placeholder();
				}

				$posts = $this->query( $this->args );

				$this->query = $posts;

				// Either we have terms or posts depending on what we want.
				$have_posts = ( 'terms' === $this->args['source'] && ! is_wp_error( $posts ) && ! empty( $posts ) ) || ( in_array( $this->args['source'], [ 'posts', 'related', 'up_sells', 'cross_sells' ], true ) && $posts->have_posts() );

				if ( ! $have_posts ) {
					return $this->get_placeholder( 'empty' );
				}

				$post_list = '';

				if ( $have_posts ) {

					// Backup global data.
					$original_post              = $GLOBALS['post'];
					$original_query             = $GLOBALS['wp_query'];
					$original_queried_object    = $GLOBALS['wp_query']->queried_object;
					$original_is_tax            = $GLOBALS['wp_query']->is_tax;
					$original_is_archive        = $GLOBALS['wp_query']->is_archive;
					$original_is_category       = $GLOBALS['wp_query']->is_category;
					$original_is_singular       = $GLOBALS['wp_query']->is_singular;
					$original_post_type_archive = $GLOBALS['wp_query']->is_post_type_archive;
					$original_is_search         = $GLOBALS['wp_query']->is_search;
					$original_is_404            = $GLOBALS['wp_query']->is_404;
					$original_is_date           = $GLOBALS['wp_query']->is_date;
					$original_is_day            = $GLOBALS['wp_query']->is_day;
					$original_is_month          = $GLOBALS['wp_query']->is_month;

					$post_list .= '<ul ' . FusionBuilder::attributes( 'post-cards-shortcode-posts' ) . '>';

					if ( 'terms' !== $this->args['source'] ) {
						$GLOBALS['wp_query']->is_tax               = false;
						$GLOBALS['wp_query']->is_archive           = false;
						$GLOBALS['wp_query']->is_category          = false;
						$GLOBALS['wp_query']->is_singular          = true;
						$GLOBALS['wp_query']->is_post_type_archive = false;
						$GLOBALS['wp_query']->is_search            = false;
						$GLOBALS['wp_query']->is_404               = false;
						$GLOBALS['wp_query']->is_date              = false;
						$GLOBALS['wp_query']->is_day               = false;
						$GLOBALS['wp_query']->is_month             = false;

						ob_start();

						while ( $posts->have_posts() ) {
							$posts->the_post();
							$GLOBALS['post'] = get_post( get_the_ID() ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
							setup_postdata( $GLOBALS['post'] );
							$GLOBALS['wp_query']->queried_object = $GLOBALS['post'];

							$this->render_custom();
						}

						$post_list .= ob_get_clean();
					} else {
						$GLOBALS['wp_query']->is_tax     = true;
						$GLOBALS['wp_query']->is_archive = true;

						ob_start();

						foreach ( $posts as $term ) {
							$GLOBALS['wp_query']->queried_object = $term;

							$this->render_custom();
						}

						$post_list .= ob_get_clean();
					}

					// Restore global data.
					$GLOBALS['post']                           = $original_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']                       = $original_query; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_tax               = $original_is_tax; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_archive           = $original_is_archive; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_category          = $original_is_category; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_singular          = $original_is_singular; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->queried_object       = $original_queried_object; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_post_type_archive = $original_post_type_archive; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_search            = $original_is_search; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_404               = $original_is_404; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_date              = $original_is_date; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_day               = $original_is_day; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
					$GLOBALS['wp_query']->is_month             = $original_is_month; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
				}

				$post_list .= '</ul>';

				if ( 'carousel' === $this->args['layout'] ) {
					if ( 'yes' === $this->args['show_nav'] ) {
						$post_list .= sprintf(
							'<div %s><span %s></span><span %s></span></div>',
							FusionBuilder::attributes( 'fusion-carousel-nav' ),
							FusionBuilder::attributes( 'fusion-nav-prev' ),
							FusionBuilder::attributes( 'fusion-nav-next' )
						);
					}

					$post_list = '<div ' . FusionBuilder::attributes( 'fusion-carousel-positioner' ) . '>' . $post_list . '</div>';
				}

				if ( 'no' !== $this->args['scrolling'] && 'terms' !== $this->args['source'] && 'grid' === $this->args['layout'] ) {
					$post_list .= $this->pagination( $this->query->max_num_pages, $fusion_settings->get( 'pagination_range' ), $this->query );
				}

				wp_reset_query(); // phpcs:ignore WordPress.WP.DiscouragedFunctions.wp_reset_query_wp_reset_query
				wp_reset_postdata();

				$html  = '<div ' . FusionBuilder::attributes( 'post-cards-shortcode' ) . '>';
				$html .= $post_list;

				// If infinite scroll with "load more" button is used.
				if ( 'load_more_button' === $this->args['scrolling'] && 1 < $posts->max_num_pages && 'terms' !== $this->args['source'] && 'grid' === $this->args['layout'] ) {
					$post_type_obj = get_post_type_object( $this->args['post_type'] );
					$html         .= '<button class="fusion-load-more-button fusion-product-button fusion-clearfix">';
					/* translators: The name. */
					$html .= apply_filters( 'avada_load_more_' . strtolower( $post_type_obj->labels->name ) . '_name', sprintf( esc_attr__( 'Load More %s', 'fusion-builder' ), $post_type_obj->labels->name ) );
					$html .= '</button>';
				}

				$html .= '</div>';
				$html .= $this->get_styles(); // Get custom styles.

				$this->element_counter++;

				$this->on_render();

				do_action( 'fusion_post_cards_rendered' );

				return apply_filters( 'fusion_element_post_cards_content', $html, $args );

			}

			/**
			 * Change args to valid values based on other options.
			 *
			 * @access public
			 * @since 3.3
			 * @return void
			 */
			public function validate_args() {
				// Force layout for list view.
				if ( 'product' === $this->args['post_type'] && 'posts' === $this->args['source'] && 'list' === $this->get_product_view() ) {
					$this->args['layout']  = 'grid';
					$this->args['columns'] = 1;
				}

				if ( 1 === (int) $this->args['columns'] && 'grid' === $this->args['layout'] ) {
					$this->args['column_spacing'] = '0px';
				}

				// No delay offering for carousels and sliders.
				if ( 'grid' !== $this->args['layout'] ) {
					$this->args['animation_delay'] = 0;
				}
			}

			/**
			 * Render custom layout style.
			 *
			 * @access public
			 * @since 3.3
			 * @return void
			 */
			public function render_custom() {
				$post_card = $this->get_post_card_design();

				if ( $post_card ) {
					$separator                      = $this->live_request || function_exists( 'fusion_separator' ) && 'none' !== $this->args['separator_style_type'] && 'grid' === $this->args['layout'] && 1 === (int) $this->args['columns'];
					FusionBuilder()->post_card_data = [
						'is_rendering'   => true,
						'columns'        => $this->args['columns'],
						'column_spacing' => $this->args['column_spacing'],
					];
					add_filter( 'fusion_dynamic_post_id', [ $this, 'nested_post_id' ], 10 );
					add_filter( 'fusion_attr_fusion-column', [ $this, 'column_attributes' ], 20 );
					add_filter( 'fusion_attr_fusion-column-wrapper', [ $this, 'column_wrapper_attributes' ], 20 );
					add_filter( 'fusion_column_tag', [ $this, 'column_tag' ], 20, 2 );

					if ( $separator ) {
						add_filter( 'fusion_column_before_close', [ $this, 'maybe_render_separator' ], 20, 2 );
					}

					Fusion_Template_Builder()->render_content( $post_card );

					remove_filter( 'fusion_dynamic_post_id', [ $this, 'nested_post_id' ], 10 );
					remove_filter( 'fusion_attr_fusion-column', [ $this, 'column_attributes' ], 20 );
					remove_filter( 'fusion_attr_fusion-column-wrapper', [ $this, 'column_wrapper_attributes' ], 20 );
					remove_filter( 'fusion_column_tag', [ $this, 'column_tag' ], 20 );

					if ( $separator ) {
						remove_filter( 'fusion_column_before_close', [ $this, 'maybe_render_separator' ], 20 );
					}
					FusionBuilder()->post_card_data = [
						'is_rendering'   => false,
						'columns'        => 1,
						'column_spacing' => 0,
					];

					do_action( 'fusion_post_card_rendered' );
				}
			}

			/**
			 * Render separator.
			 *
			 * @since 3.3
			 * @param string $output      The output.
			 * @param string $column_type The column type.
			 * @return string
			 */
			public function maybe_render_separator( $output, $column_type ) {
				if ( 'fusion_builder_column' !== $column_type ) {
					return $output;
				}

				$separator_args = [
					'style_type'  => $this->args['separator_style_type'],
					'sep_color'   => $this->args['separator_sep_color'],
					'width'       => $this->args['separator_width'],
					'alignment'   => $this->args['separator_alignment'],
					'border_size' => $this->args['separator_border_size'],
					'position'    => 'absolute',
				];
				$output        .= fusion_separator()->render( $separator_args );
				return $output;
			}

			/**
			 * Adds children elements CSS rules to <style> block
			 *
			 * @since 3.3
			 * @return void
			 */
			public function add_elements_css_properties() {

				$elements_css = apply_filters( 'fusion_post_cards_elements_css', [] );

				foreach ( $elements_css as $rules ) {
					foreach ( $rules as $rule ) {
						$this->add_css_property( $this->base_selector . ' ' . $rule['selector'], $rule['rule'], $rule['value'], $rule['important'] );
					}
				}
			}

			/**
			 * Return nested post ID for dynamic data.
			 *
			 * @access public
			 * @since 3.3
			 * @param int $post_id Post ID for dynamic data.
			 * @return int
			 */
			public function nested_post_id( $post_id ) {
				return get_the_ID();
			}

			/**
			 * Get product view layout.
			 *
			 * @access public
			 * @since 3.3
			 * @return string
			 */
			public function get_product_view() {
				$product_view = 'grid';
				if ( function_exists( 'Avada' ) && isset( $_SERVER['QUERY_STRING'] ) ) {
					parse_str( sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ), $params );
					$product_view = ( isset( $params['product_view'] ) ) ? $params['product_view'] : Avada()->settings->get( 'woocommerce_product_view' );
				}
				return $product_view;
			}

			/**
			 * Get post card design.
			 *
			 * @access public
			 * @since 3.3
			 * @return object
			 */
			public function get_post_card_design() {
				$post_card = get_post( (int) $this->args['post_card'] );

				if ( 'product' === $this->args['post_type'] && 'posts' === $this->args['source'] && 0 !== (int) $this->args['post_card_list_view'] ) {
					if ( 'list' === $this->get_product_view() ) {
						$post_card = get_post( (int) $this->args['post_card_list_view'] );
					}
				}
				return $post_card;
			}

			/**
			 * Column attributes within custom grid.
			 *
			 * @access public
			 * @since 3.3
			 * @param array $attr Column attributes.
			 * @return array
			 */
			public function column_attributes( $attr ) {

				// Column within content element within post card.
				if ( ! FusionBuilder()->post_card_data['is_rendering'] ) {
					return $attr;
				}

				// replace column class for live editor.
				if ( $this->live_request ) {
					$attr['class'] = preg_replace( '/fusion-builder-column-[0-9]+/', '', $attr['class'] );
				}

				// No need this class for carousel & slider.
				if ( 'grid' !== $this->args['layout'] ) {
					$attr['class'] = preg_replace( '/fusion-layout-column\s/', '', $attr['class'] );
				}

				$attr['class'] .= ' post-card';
				if ( 'grid' === $this->args['layout'] ) {
					$attr['class'] .= ' fusion-grid-column fusion-post-cards-grid-column';

					if ( 'product' === $this->args['post_type'] && 'posts' === $this->args['source'] ) {
						$product_view   = $this->get_product_view();
						$attr['class'] .= ' product-' . $product_view . '-view';
					}
				} elseif ( 'carousel' === $this->args['layout'] ) {
					$attr['class'] .= ' fusion-carousel-item';
				}

				if ( 'product' === $this->args['post_type'] && 'posts' === $this->args['source'] ) {
					$attr['class'] .= ' product';
				}

				// Delayed animated, inherit animation from parent.
				if ( 'grid' === $this->args['layout'] ) {
					if ( $this->args['animation_type'] && ! empty( $this->args['animation_delay'] ) ) {
						$attr['class'] .= ' fusion-animated';

						// Animation, no delay, set full to column.
					} elseif ( $this->args['animation_type'] ) {
						$attr = Fusion_Builder_Animation_Helper::add_animation_attributes( $this->args, $attr );
					}
				}

				return $attr;
			}

			/**
			 * Column attributes within custom grid.
			 *
			 * @access public
			 * @since 3.3
			 * @param array $attr Column attributes.
			 * @return array
			 */
			public function column_wrapper_attributes( $attr ) {
				if ( 'carousel' === $this->args['layout'] ) {
					$attr['class'] .= ' fusion-carousel-item-wrapper';
				}

				return $attr;
			}

			/**
			 * Column main tag.
			 *
			 * @access public
			 * @since 3.3
			 * @param string $tag Column HTML tag.
			 * @param array  $args Column attributes.
			 * @return string
			 */
			public function column_tag( $tag = 'div', $args = [] ) {

				// We remove straight after using.
				remove_filter( 'fusion_column_tag', [ $this, 'column_tag' ], 20 );
				return 'li';
			}

			/**
			 * Builds the attributes array.
			 *
			 * @access public
			 * @since 3.3
			 * @return array
			 */
			public function attr() {

				$attr = fusion_builder_visibility_atts(
					$this->args['hide_on_mobile'],
					[
						'class' => 'fusion-post-cards fusion-post-cards-' . $this->element_counter,
					]
				);

				if ( $this->args['animation_type'] ) {

					// Grid and has delay, set parent args here, otherwise it will be on children.
					if ( 'grid' === $this->args['layout'] ) {
						if ( ! empty( $this->args['animation_delay'] ) ) {
							$attr                         = Fusion_Builder_Animation_Helper::add_animation_attributes( $this->args, $attr, true );
							$attr['data-animation-delay'] = $this->args['animation_delay'];
							$attr['class']               .= ' fusion-delayed-animation';
						}
					} else {

						// Not grid always no delay, add to parent.
						$attr = Fusion_Builder_Animation_Helper::add_animation_attributes( $this->args, $attr );
					}
				}

				if ( 'slider' === $this->args['layout'] ) {
					$attr['class'] .= ' fusion-slider-sc fusion-flexslider-loading flexslider';

					$attr['data-slideshow_autoplay']    = 'no' === $this->args['autoplay'] ? 'false' : 'true';
					$attr['data-slideshow_animation']   = $this->args['slider_animation'];
					$attr['data-slideshow_control_nav'] = 'no' === $this->args['show_nav'] ? 'false' : 'true';
				} elseif ( 'carousel' === $this->args['layout'] ) {
					$attr['class'] .= ' fusion-carousel fusion-carousel-responsive';

					$attr['data-autoplay']      = $this->args['autoplay'];
					$attr['data-columns']       = $this->args['columns'];
					$attr['data-columnsmedium'] = $this->args['columns_medium'];
					$attr['data-columnssmall']  = $this->args['columns_small'];
					$attr['data-itemmargin']    = $this->args['column_spacing'];
					$attr['data-itemwidth']     = 180;
					$attr['data-touchscroll']   = $this->args['mouse_scroll'];
					$attr['data-imagesize']     = 'auto';
					$attr['data-scrollitems']   = $this->args['scroll_items'];
				} elseif ( 'grid' === $this->args['layout'] && 'terms' !== $this->args['source'] ) {
					$attr['class'] .= ' fusion-grid-archive';
				}

				if ( $this->args['class'] ) {
					$attr['class'] .= ' ' . $this->args['class'];
				}

				if ( $this->args['id'] ) {
					$attr['id'] = $this->args['id'];
				}

				return $attr;

			}

			/**
			 * Builds the attributes array.
			 *
			 * @access public
			 * @since 3.3
			 * @return array
			 */
			public function attr_pagination() {
				$attr = [
					'class' => 'fusion-woo-product-grid-pagination fusion-clearfix',
				];

				if ( $this->is_load_more() ) {
					$attr['class'] .= ' infinite-scroll infinite-scroll-hide';
				}

				return $attr;
			}

			/**
			 * Render the blog pagination.
			 *
			 * @access public
			 * @since 3.3
			 * @param int    $max_pages     Max number of pages.
			 * @param int    $range         How many page numbers to display to either side of the current page.
			 * @param object $current_query The query.
			 */
			public function pagination( $max_pages = '', $range = 1, $current_query = '' ) {
				global $wp_query;

				$range = apply_filters( 'fusion_pagination_size', $range );

				if ( '' === $max_pages ) {
					if ( '' === $current_query ) {
						$max_pages = $wp_query->max_num_pages;
						$max_pages = ( ! $max_pages ) ? 1 : $max_pages;
					} else {
						$max_pages = $current_query->max_num_pages;
					}
				}
				$max_pages = intval( $max_pages );

				$blog_global_pagination = apply_filters( 'fusion_builder_blog_pagination', '' );
				$infinite_pagination    = 'pagination' !== $this->args['scrolling'] && 'pagination' !== strtolower( $blog_global_pagination );
				$pagination_html        = fusion_pagination( $max_pages, $range, $current_query, $infinite_pagination, true );

				return apply_filters( 'fusion_post_cards_pagination_html', $pagination_html, $max_pages, $range, $current_query, $blog_global_pagination );
			}

			/**
			 * Builds the attributes array.
			 *
			 * @access public
			 * @since 3.3
			 * @return array
			 */
			public function attr_posts() {
				$attr = [
					'class' => '',
				];

				if ( 'grid' === $this->args['layout'] ) {
					$attr['class'] .= 'fusion-grid fusion-grid-' . $this->args['columns'] . ' fusion-flex-align-items-' . $this->args['flex_align_items'];
				} elseif ( 'slider' === $this->args['layout'] ) {
					$attr['class'] .= 'slides';
				} elseif ( 'carousel' === $this->args['layout'] ) {
					$attr['class'] .= 'fusion-carousel-holder';
				}

				if ( $this->is_load_more() ) {
					$attr['class'] .= ' fusion-grid-container-infinite';

					$attr['data-pages'] = $this->query->max_num_pages;
				}

				if ( 'load_more_button' === $this->args['scrolling'] ) {
					$attr['class'] .= ' fusion-grid-container-load-more';
				}

				return $attr;
			}

			/**
			 * Sets the necessary scripts.
			 *
			 * @access public
			 * @since 3.3
			 * @return void
			 */
			public function on_first_render() {
				// Skip if empty.
				if ( null === $this->args || empty( $this->args ) ) {
					return;
				}

				$post_type_obj = get_post_type_object( $this->args['post_type'] );

				Fusion_Dynamic_JS::enqueue_script( 'fusion-carousel' );

				Fusion_Dynamic_JS::enqueue_script(
					'fusion-js-' . $this->shortcode_name,
					FusionBuilder::$js_folder_url . '/general/fusion-post-cards.js',
					FusionBuilder::$js_folder_path . '/general/fusion-post-cards.js',
					[ 'jquery', 'jquery-infinite-scroll' ],
					'3.2',
					true
				);

				Fusion_Dynamic_JS::localize_script(
					'fusion-js-' . $this->shortcode_name,
					'fusionPostCardsVars',
					[
						/* translators: The name. */
						'infinite_text'         => '<em>' . sprintf( __( 'Loading the next set of %s...', 'fusion-builder' ), strtolower( $post_type_obj->labels->name ) ) . '</em>',
						'infinite_finished_msg' => '<em>' . __( 'All items displayed.', 'fusion-builder' ) . '</em>',
						'pagination_type'       => $this->args['scrolling'],
					]
				);
			}

			/**
			 * Load base CSS.
			 *
			 * @access public
			 * @since 3.3
			 * @return void
			 */
			public function add_css_files() {
				FusionBuilder()->add_element_css( FUSION_BUILDER_PLUGIN_DIR . 'assets/css/shortcodes/grid.min.css' );
				FusionBuilder()->add_element_css( FUSION_BUILDER_PLUGIN_DIR . 'assets/css/shortcodes/post-cards.min.css' );
			}

			/**
			 * Check if pagination is loadmore.
			 *
			 * @access public
			 * @since 3.3
			 * @return boolean
			 */
			public function is_load_more() {
				return in_array( $this->args['scrolling'], [ 'infinite', 'load_more_button' ], true );
			}

			/**
			 * Get placeholder.
			 *
			 * @since 3.3
			 * @param  string $type placeholder type.
			 * @return string
			 */
			protected function get_placeholder( $type = 'card' ) {
				if ( ! current_user_can( 'manage_options' ) ) {
					return '';
				}

				if ( 'card' === $type ) {
					return sprintf( '<a href="%s" target="_blank" class="fusion-builder-placeholder">%s</a>', admin_url( 'admin.php?page=avada-library' ), esc_html__( 'Please select post card design to display here.', 'fusion-builder' ) );
				} else {
					$msg = in_array( $this->args['source'], [ 'posts', 'related', 'up_sells', 'cross_sells' ], true ) ? esc_html__( 'No posts found.', 'fusion-builder' ) : esc_html__( 'No terms found.', 'fusion-builder' );
					return sprintf( '<div class="fusion-builder-placeholder">%s</div>', $msg );
				}
			}

			/**
			 * Get reverse number.
			 *
			 * @access public
			 * @since 3.3
			 * @param string $value number value.
			 * @return string
			 */
			public function get_reverse_num( $value ) {
				return strpos( $value, '-' ) > 0 ? str_replace( '-', '', $value ) : '-' . $value;
			}

			/**
			 * Get the styles.
			 *
			 * @access protected
			 * @since 3.3
			 * @return string
			 */
			protected function get_styles() {
				$fusion_settings = awb_get_fusion_settings();

				$this->base_selector = '.fusion-post-cards.fusion-post-cards-' . $this->element_counter;
				$this->dynamic_css   = [];

				$selectors = [
					$this->base_selector . ' .infinite-scroll-hide',
				];
				if ( $this->is_load_more() ) {
					$this->add_css_property( $selectors, 'display', 'none' );
				}
				if ( '1' !== $this->args['columns'] ) {
					$column_spacing = fusion_library()->sanitize->get_value_with_unit( $this->args['column_spacing'] );

					$selectors = [
						$this->base_selector . ' ul.fusion-grid',
					];
					$this->add_css_property( $selectors, 'margin-right', 'calc((' . $column_spacing . ')/ -2)' );
					$this->add_css_property( $selectors, 'margin-left', 'calc((' . $column_spacing . ')/ -2)' );

					$selectors = [
						$this->base_selector . ' ul.fusion-grid > .fusion-grid-column',
					];
					$this->add_css_property( $selectors, 'padding-left', 'calc((' . $column_spacing . ')/ 2)' );
					$this->add_css_property( $selectors, 'padding-right', 'calc((' . $column_spacing . ')/ 2)' );

					$selectors = [
						$this->base_selector . ' ul.fusion-grid > .fusion-grid-column > .fusion-column-inner-bg',
					];
					$this->add_css_property( $selectors, 'margin-left', 'calc((' . $column_spacing . ')/ 2)' );
					$this->add_css_property( $selectors, 'margin-right', 'calc((' . $column_spacing . ')/ 2)' );
				}

				if ( 'grid' === $this->args['layout'] ) {
					$row_spacing = fusion_library()->sanitize->get_value_with_unit( $this->args['row_spacing'] );

					$selectors = [
						$this->base_selector . ' ul.fusion-grid',
					];
					$this->add_css_property( $selectors, 'margin-top', 'calc((' . $row_spacing . ')/ -2)' );

					$selectors = [
						$this->base_selector . ' ul.fusion-grid > .fusion-grid-column',
					];
					$this->add_css_property( $selectors, 'padding-top', 'calc((' . $row_spacing . ')/ 2)' );
					$this->add_css_property( $selectors, 'padding-bottom', 'calc((' . $row_spacing . ')/ 2)' );

					$selectors = [
						$this->base_selector . ' ul.fusion-grid > .fusion-grid-column > .fusion-column-inner-bg',
					];
					$this->add_css_property( $selectors, 'margin-top', 'calc((' . $row_spacing . ')/ 2)' );
					$this->add_css_property( $selectors, 'margin-bottom', 'calc((' . $row_spacing . ')/ 2)' );
				}

				// Margin styles.
				if ( ! $this->is_default( 'margin_top' ) ) {
					$this->add_css_property( $this->base_selector, 'margin-top', fusion_library()->sanitize->get_value_with_unit( $this->args['margin_top'] ) );
				}
				if ( ! $this->is_default( 'margin_right' ) ) {
					$this->add_css_property( $this->base_selector, 'margin-right', fusion_library()->sanitize->get_value_with_unit( $this->args['margin_right'] ) );
				}
				if ( ! $this->is_default( 'margin_bottom' ) ) {
					$this->add_css_property( $this->base_selector, 'margin-bottom', fusion_library()->sanitize->get_value_with_unit( $this->args['margin_bottom'] ) );
				}
				if ( ! $this->is_default( 'margin_left' ) ) {
					$this->add_css_property( $this->base_selector, 'margin-left', fusion_library()->sanitize->get_value_with_unit( $this->args['margin_left'] ) );
				}

				$selectors = [
					$this->base_selector . ' .flex-control-nav',
				];
				if ( 'slider' === $this->args['layout'] ) {
					$this->add_css_property( $selectors, 'bottom', fusion_library()->sanitize->get_value_with_unit( $this->get_reverse_num( $this->args['nav_margin_top'] ) ) );
				}

				// Process children elements CSS.
				$this->add_elements_css_properties();

				$css = $this->parse_css();

				if ( 'grid' === $this->args['layout'] ) {
					$responsive_style = '';

					// Responsive options.
					foreach ( [ 'medium', 'small' ] as $responsive_size ) {
						$key = 'columns_' . $responsive_size;

						// Check for default value.
						if ( $this->is_default( $key ) ) {
							continue;
						}

						$this->dynamic_css = [];

						// Build responsive styles.
						$selectors = [
							$this->base_selector . ' .fusion-grid .fusion-grid-column',
						];
						$this->add_css_property( $selectors, 'width', $this->get_grid_width_val( $this->args[ $key ] ) . '!important' );

						$media             = sprintf( '@media only screen and (max-width:%spx)', $fusion_settings->get( 'visibility_' . $responsive_size ) );
						$responsive_style .= sprintf( '%s { %s }', $media, $this->parse_css() );
					}
					$css .= $responsive_style;
				}

				return $css ? '<style>' . $css . '</style>' : '';
			}

			/**
			 * Apply post per page on shop page.
			 *
			 * @access public
			 * @since 3.3
			 * @param  object $query The WP_Query object.
			 * @return  void
			 */
			public function alter_shop_loop( $query ) {
				if ( ! is_admin() && $query->is_main_query() && $query->is_post_type_archive( 'product' ) && 'no' === fusion_get_option( 'show_wc_shop_loop' ) ) {
					$search_override        = get_post( wc_get_page_id( 'shop' ) );
					$has_archives_component = $search_override && has_shortcode( $search_override->post_content, 'fusion_post_cards' );

					if ( $has_archives_component ) {
						$pattern = get_shortcode_regex( [ 'fusion_post_cards' ] );
						$content = $search_override->post_content;
						if ( preg_match_all( '/' . $pattern . '/s', $search_override->post_content, $matches )
							&& array_key_exists( 2, $matches )
							&& in_array( 'fusion_post_cards', $matches[2], true ) ) {
							$search_atts  = shortcode_parse_atts( $matches[3][0] );
							$number_posts = ( isset( $_GET['product_count'] ) ) ? (int) $_GET['product_count'] : $search_atts['number_posts']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
							$query->set( 'paged', ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1 );
							if ( '0' !== $number_posts ) {
								$query->set( 'posts_per_page', $number_posts );
							}
						}
					}
				}
			}

			/**
			 * Get grid width value.
			 *
			 * @access public
			 * @since 3.3
			 * @param string $columns Columns count.
			 * @return string
			 */
			public function get_grid_width_val( $columns ) {
				$cols = [
					'1' => '100%',
					'2' => '50%',
					'3' => '33.3333%',
					'4' => '25%',
					'5' => '20%',
					'6' => '16.6666%',
				];
				return $cols[ $columns ];
			}

			/**
			 * Fetch post types.
			 *
			 * @access protected
			 * @since 3.3
			 * @return mixed
			 */
			public function fetch_post_types() {
				if ( null !== $this->post_types ) {
					return $this->post_types;
				}

				$post_types = get_post_types( [ 'public' => true ], 'objects' );
				unset( $post_types['attachment'] );
				unset( $post_types['slide'] );
				unset( $post_types['page'] );
				$this->post_types = apply_filters( 'post_card_post_types', $post_types );
				return $this->post_types;
			}

			/**
			 * Fetch taxonomies.
			 *
			 * @access protected
			 * @since 3.3
			 * @return string
			 */
			public function fetch_taxonomies() {
				if ( null !== $this->taxonomies ) {
					return $this->taxonomies;
				}

				$post_types = $this->fetch_post_types();
				if ( is_array( $post_types ) ) {
					foreach ( $post_types as $post_type ) {
						$new_taxonomies = get_object_taxonomies( $post_type->name, 'objects' );
						foreach ( $new_taxonomies as $new_taxonomy ) {
							$post_taxonomies[ $new_taxonomy->name ] = $new_taxonomy;

							// Need each taxonomy for each post type.
							if ( ! isset( $this->taxonomy_map[ $post_type->name ] ) ) {
								$this->taxonomy_map[ $post_type->name ] = [ 'all' ];
							}
							$this->taxonomy_map[ $post_type->name ][] = $new_taxonomy->name;
						}
					}
				}

				unset( $post_taxonomies['post_format'] ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedUnsetVariable
				unset( $post_taxonomies['product_visibility'] ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedUnsetVariable
				$this->taxonomies = apply_filters( 'post_card_post_taxonomies', $post_taxonomies );
				return $this->taxonomies;
			}

			/**
			 * Fetch post type option select.
			 *
			 * @access protected
			 * @since 3.3
			 * @return string
			 */
			public function fetch_post_type_option() {
				$post_types        = $this->fetch_post_types();
				$post_type_options = [];
				if ( is_array( $post_types ) ) {
					foreach ( $post_types as $post_type ) {
						$post_type_options[ esc_attr( $post_type->name ) ] = esc_html( $post_type->label );
					}
				}

				return [
					'type'        => 'select',
					'heading'     => esc_attr__( 'Post Type', 'fusion-builder' ),
					'description' => esc_attr__( 'Select the post type to display.', 'fusion-builder' ),
					'param_name'  => 'post_type',
					'default'     => 'post',
					'value'       => $post_type_options,
					'dependency'  => [
						[
							'element'  => 'source',
							'value'    => 'posts',
							'operator' => '==',
						],
					],
					'callback'    => [
						'function' => 'fusion_ajax',
						'action'   => 'get_fusion_post_cards',
						'ajax'     => true,
					],
				];
			}

			/**
			 * Fetch post taxonomy option select.
			 *
			 * @access protected
			 * @since 3.3
			 * @return string
			 */
			public function fetch_post_terms_option() {
				$taxonomies       = $this->fetch_taxonomies();
				$taxonomy_options = [];

				if ( is_array( $taxonomies ) ) {
					foreach ( $taxonomies as $taxonomy ) {
						$taxonomy_options[ $taxonomy->name ] = ucwords( esc_html( $taxonomy->label ) );
					}
				}

				return [
					'type'        => 'select',
					'heading'     => esc_html__( 'Taxonomy', 'fusion-builder' ),
					'description' => esc_html__( 'Select which taxonomy to use.', 'fusion-builder' ),
					'param_name'  => 'terms_by',
					'default'     => '',
					'value'       => $taxonomy_options,
					'dependency'  => [
						[
							'element'  => 'source',
							'value'    => 'posts',
							'operator' => '!=',
						],
						[
							'element'  => 'source',
							'value'    => 'up_sells',
							'operator' => '!=',
						],
						[
							'element'  => 'source',
							'value'    => 'cross_sells',
							'operator' => '!=',
						],
					],
					'callback'    => [
						'function' => 'fusion_ajax',
						'action'   => 'get_fusion_post_cards',
						'ajax'     => true,
					],
				];
			}

			/**
			 * Fetch post type specific filter options.
			 *
			 * @access protected
			 * @since 3.3
			 * @return string
			 */
			public function fetch_post_filter_option() {
				$taxonomies       = $this->fetch_taxonomies();
				$taxonomy_options = [
					'all' => esc_html__( 'All', 'fusion-builder' ),
				];

				if ( empty( $taxonomies ) ) {
					return [];
				}

				// There ae some, add each to select field if they are accepted.
				foreach ( $taxonomies as $taxonomy ) {
					$taxonomy_options[ $taxonomy->name ] = ucwords( esc_html( $taxonomy->label ) );
				}

				return [
					'type'        => 'select',
					'heading'     => esc_html__( 'Posts By', 'fusion-builder' ),
					'description' => esc_html__( 'Select which taxonomy to pull posts from or select all to pull all.', 'fusion-builder' ),
					'param_name'  => 'posts_by',
					'default'     => 'all',
					'value'       => $taxonomy_options,
					'conditions'  => [
						'option' => 'post_type',
						'map'    => $this->taxonomy_map,
					],
					'dependency'  => [
						[
							'element'  => 'source',
							'value'    => 'posts',
							'operator' => '==',
						],
					],
					'callback'    => [
						'function' => 'fusion_ajax',
						'action'   => 'get_fusion_post_cards',
						'ajax'     => true,
					],
				];
			}

			/**
			 * Fetch taxonomy options.
			 *
			 * @access protected
			 * @since 3.3
			 * @return string
			 */
			public function fetch_post_taxonomy_options() {
				$taxonomies = $this->fetch_taxonomies();
				$options    = [];
				if ( is_array( $taxonomies ) ) {
					foreach ( $taxonomies as $taxonomy ) {
						$field_type  = 'ajax_select';
						$ajax        = 'fusion_search_query';
						$ajax_params = [
							'taxonomy' => $taxonomy->name,
						];
						$selection   = [];

						if ( 25 > wp_count_terms( $taxonomy->name ) ) {
							$ajax       = '';
							$field_type = 'multiple_select';
							$terms      = get_terms(
								[
									'taxonomy'   => $taxonomy->name,
									'hide_empty' => true,
								]
							);

							// All terms.
							foreach ( $terms as $term ) {
								$selection[ $term->term_id ] = $term->name;
							}
						}

						$taxonomy_title  = ucwords( $taxonomy->labels->name );
						$include_options = [
							'type'        => $field_type,
							/* translators: Taxonomy title. */
							'heading'     => sprintf( esc_attr__( 'Include %s', 'fusion-builder' ), $taxonomy_title ),
							'placeholder' => ucwords( $taxonomy->labels->name ),

							/* translators: Taxonomy name. */
							'description' => sprintf( esc_attr__( 'Select a %s or leave blank for all.', 'fusion-builder' ), strtolower( $taxonomy->labels->name ) ),
							'param_name'  => 'include_' . $taxonomy->name,
							'default'     => '',
							'value'       => $selection,
							'ajax'        => $ajax,
							'ajax_params' => $ajax_params,
							'callback'    => [
								'function' => 'fusion_ajax',
								'action'   => 'get_fusion_post_cards',
								'ajax'     => true,
							],
							'dependency'  => [
								[
									'element'  => 'posts_by',
									'value'    => $taxonomy->name,
									'operator' => '==',
								],
								[
									'element'  => 'source',
									'value'    => 'posts',
									'operator' => '==',
								],
							],
						];
						$options[]       = $include_options;

						// Duplicate the option for terms.
						$include_options['param_name'] = 'include_term_' . $taxonomy->name;
						$include_options['dependency'] = [
							[
								'element'  => 'terms_by',
								'value'    => $taxonomy->name,
								'operator' => '==',
							],
							[
								'element'  => 'source',
								'value'    => 'terms',
								'operator' => '==',
							],
						];
						$options[]                     = $include_options;

						$exclude_options = [
							'type'        => $field_type,
							/* translators: Taxonomy title. */
							'heading'     => sprintf( esc_attr__( 'Exclude %s', 'fusion-builder' ), $taxonomy_title ),
							'placeholder' => ucwords( $taxonomy->labels->name ),

							/* translators: Taxonomy name. */
							'description' => sprintf( esc_attr__( 'Select a %s or leave blank for all.', 'fusion-builder' ), strtolower( $taxonomy->labels->name ) ),
							'param_name'  => 'exclude_' . $taxonomy->name,
							'default'     => '',
							'value'       => $selection,
							'ajax'        => $ajax,
							'ajax_params' => $ajax_params,
							'callback'    => [
								'function' => 'fusion_ajax',
								'action'   => 'get_fusion_post_cards',
								'ajax'     => true,
							],
							'dependency'  => [
								[
									'element'  => 'posts_by',
									'value'    => $taxonomy->name,
									'operator' => '==',
								],
								[
									'element'  => 'source',
									'value'    => 'posts',
									'operator' => '==',
								],
							],
						];
						$options[]       = $exclude_options;

						// Duplicate option for terms.
						$exclude_options['param_name'] = 'exclude_term_' . $taxonomy->name;
						$exclude_options['dependency'] = $include_options['dependency'];
						$options[]                     = $exclude_options;
					}
				}

				return $options;
			}
		}
	}

	/**
	 * Instantiates the post cards class.
	 *
	 * @return object FusionSC_PostCards
	 */
	function fusion_post_cards() { // phpcs:ignore WordPress.NamingConventions
		return FusionSC_PostCards::get_instance();
	}

	// Instantiate post cards.
	fusion_post_cards();
}

/**
 * Map shortcode to Avada Builder.
 */
function fusion_element_post_cards() {
	$fusion_settings = awb_get_fusion_settings();
	$editing         = function_exists( 'is_fusion_editor' ) && is_fusion_editor();

	$post_type_option  = [];
	$post_type_options = [];
	$taxonomy_options  = [];
	$filter_option     = [];
	$post_terms_option = [];
	$layouts           = [
		'0' => esc_attr__( 'None', 'fusion-builder' ),
	];

	// If builder get custom layout options.
	if ( $editing && function_exists( 'Fusion_Builder_Library' ) ) {
		// In case taxonomy is not registered yet, register.
		Fusion_Builder_Library()->register_layouts();

		$post_cards = get_posts(
			[
				'post_type'      => 'fusion_element',
				'posts_per_page' => '-1',
				'tax_query'      => [ // phpcs:ignore WordPress.DB.SlowDBQuery
					[
						'taxonomy' => 'element_category',
						'field'    => 'slug',
						'terms'    => 'post_cards',
					],
				],
			]
		);

		if ( $post_cards ) {
			foreach ( $post_cards as $post_card ) {
				$layouts[ $post_card->ID ] = $post_card->post_title;
			}
		}

		// We only need options if element is active so check here is safe.
		if ( function_exists( 'fusion_post_cards' ) ) {
			$post_cards        = fusion_post_cards();
			$post_type_option  = $post_cards->fetch_post_type_option();
			$post_terms_option = $post_cards->fetch_post_terms_option();
			$filter_option     = $post_cards->fetch_post_filter_option();
			$taxonomy_options  = $post_cards->fetch_post_taxonomy_options();
		}
	}

	$source_values = [
		'posts'   => esc_attr__( 'Posts', 'fusion-builder' ),
		'terms'   => esc_attr__( 'Terms', 'fusion-builder' ),
		'related' => esc_attr__( 'Related', 'fusion-builder' ),
	];

	if ( class_exists( 'WooCommerce' ) ) {
		$woo_sources = [
			'up_sells'    => esc_attr__( 'Upsells', 'fusion-builder' ),
			'cross_sells' => esc_attr__( 'Cross-sells', 'fusion-builder' ),
		];

		$source_values = array_merge( $source_values, $woo_sources );
	}

	$library_link = '<a href="' . admin_url( 'admin.php?page=avada-library' ) . '">' . esc_attr__( 'Avada Library', 'fusion-builder' ) . '</a>';

	$params = [
		[
			'type'        => 'select',
			'heading'     => esc_attr__( 'Post Card', 'fusion-builder' ),
			'group'       => esc_attr__( 'General', 'fusion-builder' ),

			/* translators: The Avada Library link. */
			'description' => sprintf( __( 'Select a saved Post Card design to use. Create new or edit existing Post Cards in the %s.', 'fusion-builder' ), $library_link ),
			'param_name'  => 'post_card',
			'default'     => '0',
			'value'       => $layouts,
			'callback'    => [
				'function' => 'fusion_ajax',
				'action'   => 'get_fusion_post_cards',
				'ajax'     => true,
			],
		],
		[
			'type'        => 'select',
			'heading'     => esc_attr__( 'Post Card List View', 'fusion-builder' ),
			'group'       => esc_attr__( 'General', 'fusion-builder' ),

			/* translators: The Avada Library link. */
			'description' => sprintf( __( 'This post card will be used in the list view which can be triggered with the sorting element. Post cards can be created in the %s.', 'fusion-builder' ), $library_link ),
			'param_name'  => 'post_card_list_view',
			'default'     => '0',
			'value'       => $layouts,
			'dependency'  => [
				[
					'element'  => 'source',
					'value'    => 'posts',
					'operator' => '==',
				],
				[
					'element'  => 'post_type',
					'value'    => 'product',
					'operator' => '==',
				],
			],
			'callback'    => [
				'function' => 'fusion_ajax',
				'action'   => 'get_fusion_post_cards',
				'ajax'     => true,
			],
		],
		[
			'type'        => 'radio_button_set',
			'heading'     => esc_attr__( 'Content Source', 'fusion-builder' ),
			'description' => __( 'Select the type of content you would like to show. <strong>Note:</strong> The related option will fetch items related to the post that it is placed on based on taxonomy selection.', 'fusion-builder' ),
			'param_name'  => 'source',
			'default'     => 'posts',
			'value'       => $source_values,
			'callback'    => [
				'function' => 'fusion_ajax',
				'action'   => 'get_fusion_post_cards',
				'ajax'     => true,
			],
		],
		$post_type_option,
		$post_terms_option,
		$filter_option,
	];

	foreach ( $taxonomy_options as $taxonomy_option ) {
		$params[] = $taxonomy_option;
	}

	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Include Out Of Stock Products', 'fusion-builder' ),
		'description' => esc_attr__( 'Include or exclude out of stock products.', 'fusion-builder' ),
		'param_name'  => 'out_of_stock',
		'value'       => [
			'include' => esc_attr__( 'Include', 'fusion-builder' ),
			'exclude' => esc_attr__( 'Exclude', 'fusion-builder' ),
		],
		'default'     => 'include',
		'dependency'  => [
			[
				'element'  => 'source',
				'value'    => 'posts',
				'operator' => '==',
			],
			[
				'element'  => 'post_type',
				'value'    => 'product',
				'operator' => '==',
			],
		],
		'callback'    => [
			'function' => 'fusion_ajax',
			'action'   => 'get_fusion_post_cards',
			'ajax'     => true,
		],
	];

	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Show Hidden Products', 'fusion-builder' ),
		'description' => esc_attr__( 'Display hidden products that are excluded from search or catalogs.', 'fusion-builder' ),
		'param_name'  => 'show_hidden',
		'value'       => [
			'yes' => esc_attr__( 'Yes', 'fusion-builder' ),
			'no'  => esc_attr__( 'No', 'fusion-builder' ),
		],
		'default'     => 'no',
		'dependency'  => [
			[
				'element'  => 'source',
				'value'    => 'posts',
				'operator' => '==',
			],
			[
				'element'  => 'post_type',
				'value'    => 'product',
				'operator' => '==',
			],
		],
		'callback'    => [
			'function' => 'fusion_ajax',
			'action'   => 'get_fusion_post_cards',
			'ajax'     => true,
		],
	];

	$params[] = [
		'type'        => 'range',
		'heading'     => esc_attr__( 'Number of Posts', 'fusion-builder' ),
		'description' => sprintf(
			/* translators: %1$s: Portfolio Link. %2$s: Products Link. */
			esc_attr__( 'Select number of posts per page.  Set to -1 to display all. Set to 0 to use the post type default number of posts. For %1$s and %2$s this comes from the global options. For all others Settings > Reading.', 'fusion-builder' ),
			'<a href="' . admin_url( 'themes.php?page=avada_options#portfolio_archive_items' ) . '" target="_blank">' . esc_attr__( 'portfolio', 'fusion-builder' ) . '</a>',
			'<a href="' . admin_url( 'themes.php?page=avada_options#woo_items' ) . '" target="_blank">' . esc_attr__( 'products', 'fusion-builder' ) . '</a>'
		),
		'param_name'  => 'number_posts',
		'min'         => '-1',
		'max'         => '50',
		'step'        => '1',
		'value'       => '0',
		'callback'    => [
			'function' => 'fusion_ajax',
			'action'   => 'get_fusion_post_cards',
			'ajax'     => true,
		],
	];
	$params[] = [
		'type'        => 'range',
		'heading'     => esc_attr__( 'Posts Offset', 'fusion-builder' ),
		'description' => esc_attr__( 'The number of posts to skip. ex: 1.', 'fusion-builder' ),
		'param_name'  => 'offset',
		'value'       => '0',
		'min'         => '0',
		'max'         => '24',
		'step'        => '1',
		'callback'    => [
			'function' => 'fusion_ajax',
			'action'   => 'get_fusion_post_cards',
			'ajax'     => true,
		],
	];
	$params[] = [
		'type'        => 'select',
		'heading'     => esc_attr__( 'Order By', 'fusion-builder' ),
		'description' => __( 'Defines how posts should be ordered. <strong>Note:</strong> (Price|Popularity|Rating)options only work for product-related queries.', 'fusion-builder' ),
		'param_name'  => 'orderby',
		'default'     => 'date',
		'value'       => [
			'date'          => esc_attr__( 'Date', 'fusion-builder' ),
			'title'         => esc_attr__( 'Post Title', 'fusion-builder' ),
			'name'          => esc_attr__( 'Post Slug', 'fusion-builder' ),
			'author'        => esc_attr__( 'Author', 'fusion-builder' ),
			'id'            => esc_attr__( 'ID', 'fusion-builder' ),
			'comment_count' => esc_attr__( 'Number of Comments', 'fusion-builder' ),
			'modified'      => esc_attr__( 'Last Modified', 'fusion-builder' ),
			'rand'          => esc_attr__( 'Random', 'fusion-builder' ),
			'price'         => esc_attr__( 'Price', 'fusion-builder' ),
			'popularity'    => esc_attr__( 'Popularity (sales)', 'fusion-builder' ),
			'rating'        => esc_attr__( 'Average Rating', 'fusion-builder' ),
		],
		'dependency'  => [
			[
				'element'  => 'source',
				'value'    => 'terms',
				'operator' => '!=',
			],
		],
		'callback'    => [
			'function' => 'fusion_ajax',
			'action'   => 'get_fusion_post_cards',
			'ajax'     => true,
		],
	];
	$params[] = [
		'type'        => 'select',
		'heading'     => esc_attr__( 'Order By', 'fusion-builder' ),
		'description' => __( 'Defines how terms should be ordered.', 'fusion-builder' ),
		'param_name'  => 'orderby_term',
		'default'     => 'name',
		'value'       => [
			'name'        => esc_attr__( 'Name', 'fusion-builder' ),
			'slug'        => esc_attr__( 'Slug', 'fusion-builder' ),
			'term_group'  => esc_attr__( 'Term Group', 'fusion-builder' ),
			'term_id'     => esc_attr__( 'Term ID', 'fusion-builder' ),
			'description' => esc_attr__( 'Description', 'fusion-builder' ),
			'parent'      => esc_attr__( 'Parent', 'fusion-builder' ),
		],
		'dependency'  => [
			[
				'element'  => 'source',
				'value'    => 'terms',
				'operator' => '=',
			],
		],
		'callback'    => [
			'function' => 'fusion_ajax',
			'action'   => 'get_fusion_post_cards',
			'ajax'     => true,
		],
	];

	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Order', 'fusion-builder' ),
		'description' => esc_attr__( 'Defines the sorting order of posts.', 'fusion-builder' ),
		'param_name'  => 'order',
		'default'     => 'DESC',
		'value'       => [
			'DESC' => esc_attr__( 'Descending', 'fusion-builder' ),
			'ASC'  => esc_attr__( 'Ascending', 'fusion-builder' ),
		],
		'dependency'  => [
			[
				'element'  => 'orderby',
				'value'    => 'rand',
				'operator' => '!=',
			],
		],
		'callback'    => [
			'function' => 'fusion_ajax',
			'action'   => 'get_fusion_post_cards',
			'ajax'     => true,
		],
	];
	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Pagination Type', 'fusion-builder' ),
		'description' => esc_attr__( 'Select the type of pagination.', 'fusion-builder' ),
		'param_name'  => 'scrolling',
		'default'     => 'pagination',
		'value'       => [
			'no'               => esc_attr__( 'No Pagination', 'fusion-builder' ),
			'pagination'       => esc_attr__( 'Pagination', 'fusion-builder' ),
			'infinite'         => esc_attr__( 'Infinite Scrolling', 'fusion-builder' ),
			'load_more_button' => esc_attr__( 'Load More Button', 'fusion-builder' ),
		],
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '==',
			],
			[
				'element'  => 'source',
				'value'    => 'terms',
				'operator' => '!=',
			],
		],
	];
	$params[] = [
		'type'        => 'checkbox_button_set',
		'heading'     => esc_attr__( 'Element Visibility', 'fusion-builder' ),
		'param_name'  => 'hide_on_mobile',
		'value'       => fusion_builder_visibility_options( 'full' ),
		'default'     => fusion_builder_default_visibility( 'array' ),
		'description' => esc_attr__( 'Choose to show or hide the element on small, medium or large screens. You can choose more than one at a time.', 'fusion-builder' ),
	];
	$params[] = [
		'type'        => 'textfield',
		'heading'     => esc_attr__( 'CSS Class', 'fusion-builder' ),
		'description' => esc_attr__( 'Add a class to the wrapping HTML element.', 'fusion-builder' ),
		'param_name'  => 'class',
		'value'       => '',
	];
	$params[] = [
		'type'        => 'textfield',
		'heading'     => esc_attr__( 'CSS ID', 'fusion-builder' ),
		'description' => esc_attr__( 'Add an ID to the wrapping HTML element.', 'fusion-builder' ),
		'param_name'  => 'id',
		'value'       => '',
	];
	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Layout', 'fusion-builder' ),
		'description' => esc_attr__( 'Select how you want Post Cards to display.', 'fusion-builder' ),
		'param_name'  => 'layout',
		'value'       => [
			'grid'     => esc_attr__( 'Grid', 'fusion-builder' ),
			'carousel' => esc_attr__( 'Carousel', 'fusion-builder' ),
			'slider'   => esc_attr__( 'Slider', 'fusion-builder' ),
		],
		'default'     => 'grid',
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
	];
	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Post Card Alignment', 'fusion-builder' ),
		'description' => esc_attr__( 'Select the Post Cards alignment within rows.', 'fusion-builder' ),
		'param_name'  => 'flex_align_items',
		'back_icons'  => true,
		'grid_layout' => true,
		'value'       => [
			'flex-start' => esc_attr__( 'Flex Start', 'fusion-builder' ),
			'center'     => esc_attr__( 'Center', 'fusion-builder' ),
			'flex-end'   => esc_attr__( 'Flex End', 'fusion-builder' ),
			'stretch'    => esc_attr__( 'Stretch', 'fusion-builder' ),
		],
		'icons'       => [
			'flex-start' => '<span class="fusiona-align-top-columns"></span>',
			'center'     => '<span class="fusiona-align-center-columns"></span>',
			'flex-end'   => '<span class="fusiona-align-bottom-columns"></span>',
			'stretch'    => '<span class="fusiona-full-height"></span>',
		],
		'default'     => 'flex-start',
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '==',
			],
		],
	];
	$params[] = [
		'type'        => 'range',
		'heading'     => esc_attr__( 'Number of Columns', 'fusion-builder' ),
		'description' => esc_attr__( 'Set the number of columns per row.', 'fusion-builder' ),
		'param_name'  => 'columns',
		'value'       => $fusion_settings->get( 'woocommerce_shop_page_columns' ),
		'min'         => '0',
		'max'         => '6',
		'step'        => '1',
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'responsive'  => [
			'state'        => 'large',
			'values'       => [
				'small'  => '0',
				'medium' => '0',
			],
			'descriptions' => [
				'small'  => esc_attr__( 'Set the number of columns per row. Leave at 0 for automatic column breaking', 'fusion-builder' ),
				'medium' => esc_attr__( 'Set the number of columns per row. Leave at 0 for automatic column breaking', 'fusion-builder' ),
			],
		],
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'slider',
				'operator' => '!=',
			],
		],
	];
	$params[] = [
		'type'        => 'range',
		'heading'     => esc_attr__( 'Column Spacing', 'fusion-builder' ),
		'description' => esc_attr__( "Insert the amount of horizontal spacing between items without 'px'. ex: 40.", 'fusion-builder' ),
		'param_name'  => 'column_spacing',
		'value'       => $fusion_settings->get( 'woocommerce_archive_grid_column_spacing' ),
		'min'         => '1',
		'max'         => '300',
		'step'        => '1',
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'dependency'  => [
			[
				'element'  => 'columns',
				'value'    => '1',
				'operator' => '!=',
			],
			[
				'element'  => 'layout',
				'value'    => 'slider',
				'operator' => '!=',
			],
		],
	];
	$params[] = [
		'type'        => 'range',
		'heading'     => esc_attr__( 'Row Spacing', 'fusion-builder' ),
		'description' => esc_attr__( "Insert the amount of vertical spacing between items without 'px'. ex: 40.", 'fusion-builder' ),
		'param_name'  => 'row_spacing',
		'value'       => $fusion_settings->get( 'woocommerce_archive_grid_column_spacing' ),
		'min'         => '1',
		'max'         => '300',
		'step'        => '1',
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '==',
			],
		],
	];

	// Separator styles.
	$params[] = [
		'type'        => 'select',
		'heading'     => esc_attr__( 'Separator', 'fusion-builder' ),
		'description' => esc_attr__( 'Choose the horizontal separator line style. This will only be used on single column grids or list view.', 'fusion-builder' ),
		'param_name'  => 'separator_style_type',
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'callback'    => [
			'function' => 'fusion_post_card_separator',
			'ajax'     => false,
		],
		'value'       => [
			'none'          => esc_attr__( 'None', 'fusion-builder' ),
			'single solid'  => esc_attr__( 'Single Border Solid', 'fusion-builder' ),
			'double solid'  => esc_attr__( 'Double Border Solid', 'fusion-builder' ),
			'single|dashed' => esc_attr__( 'Single Border Dashed', 'fusion-builder' ),
			'double|dashed' => esc_attr__( 'Double Border Dashed', 'fusion-builder' ),
			'single|dotted' => esc_attr__( 'Single Border Dotted', 'fusion-builder' ),
			'double|dotted' => esc_attr__( 'Double Border Dotted', 'fusion-builder' ),
		],
		'default'     => 'none',
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '==',
			],
		],
	];
	$params[] = [
		'type'        => 'colorpickeralpha',
		'heading'     => esc_attr__( 'Separator Color', 'fusion-builder' ),
		'description' => esc_attr__( 'Controls the separator color.', 'fusion-builder' ),
		'param_name'  => 'separator_sep_color',
		'value'       => '',
		'default'     => $fusion_settings->get( 'sep_color' ),
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'callback'    => [
			'function' => 'fusion_post_card_separator',
			'ajax'     => false,
		],
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '==',
			],
			[
				'element'  => 'separator_style_type',
				'value'    => 'none',
				'operator' => '!=',
			],
		],
	];
	$params[] = [
		'type'             => 'dimension',
		'remove_from_atts' => true,
		'heading'          => esc_attr__( 'Separator Width', 'fusion-builder' ),
		'param_name'       => 'dimensions_width',
		'value'            => [
			'separator_width' => '',
		],
		'description'      => esc_attr__( 'In pixels (px or %), ex: 1px, ex: 50%. Leave blank for full width.', 'fusion-builder' ),
		'group'            => esc_attr__( 'Design', 'fusion-builder' ),
		'callback'         => [
			'function' => 'fusion_post_card_separator',
			'ajax'     => false,
		],
		'dependency'       => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '==',
			],
			[
				'element'  => 'separator_style_type',
				'value'    => 'none',
				'operator' => '!=',
			],
		],
	];
	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Separator Alignment', 'fusion-builder' ),
		'description' => esc_attr__( 'Select the separator alignment; only works when a width is specified.', 'fusion-builder' ),
		'param_name'  => 'separator_alignment',
		'value'       => [
			'center' => esc_attr__( 'Center', 'fusion-builder' ),
			'left'   => esc_attr__( 'Left', 'fusion-builder' ),
			'right'  => esc_attr__( 'Right', 'fusion-builder' ),
		],
		'default'     => 'center',
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'callback'    => [
			'function' => 'fusion_post_card_separator',
			'ajax'     => false,
		],
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '==',
			],
			[
				'element'  => 'separator_style_type',
				'value'    => 'none',
				'operator' => '!=',
			],
		],
	];
	$params[] = [
		'type'        => 'range',
		'heading'     => esc_attr__( 'Separator Border Size', 'fusion-builder' ),
		'param_name'  => 'separator_border_size',
		'value'       => '',
		'min'         => '0',
		'max'         => '50',
		'step'        => '1',
		'default'     => $fusion_settings->get( 'separator_border_size' ),
		'description' => esc_attr__( 'In pixels. ', 'fusion-builder' ),
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'callback'    => [
			'function' => 'fusion_post_card_separator',
			'ajax'     => false,
		],
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '==',
			],
			[
				'element'  => 'separator_style_type',
				'value'    => 'none',
				'operator' => '!=',
			],
		],
	];

	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Autoplay', 'fusion-builder' ),
		'description' => esc_attr__( 'Choose to autoplay the items.', 'fusion-builder' ),
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'param_name'  => 'autoplay',
		'value'       => [
			'yes' => esc_attr__( 'Yes', 'fusion-builder' ),
			'no'  => esc_attr__( 'No', 'fusion-builder' ),
		],
		'default'     => 'no',
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '!=',
			],
		],
	];
	$params[] = [
		'type'        => 'textfield',
		'heading'     => esc_attr__( 'Scroll Items', 'fusion-builder' ),
		'description' => esc_attr__( 'Insert the amount of items to scroll. Leave empty to scroll number of visible items.', 'fusion-builder' ),
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'param_name'  => 'scroll_items',
		'value'       => '',
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'carousel',
				'operator' => '==',
			],
		],
	];
	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Show Navigation', 'fusion-builder' ),
		'description' => esc_attr__( 'Choose to show navigation buttons on the carousel / slider.', 'fusion-builder' ),
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'param_name'  => 'show_nav',
		'value'       => [
			'yes' => esc_attr__( 'Yes', 'fusion-builder' ),
			'no'  => esc_attr__( 'No', 'fusion-builder' ),
		],
		'default'     => 'yes',
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '!=',
			],
		],
	];
	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Mouse Scroll', 'fusion-builder' ),
		'description' => esc_attr__( 'Choose to enable mouse drag control on the carousel. IMPORTANT: For easy draggability, when mouse scroll is activated, links will be disabled.', 'fusion-builder' ),
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'param_name'  => 'mouse_scroll',
		'value'       => [
			'yes' => esc_attr__( 'Yes', 'fusion-builder' ),
			'no'  => esc_attr__( 'No', 'fusion-builder' ),
		],
		'default'     => 'no',
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'carousel',
				'operator' => '==',
			],
		],
	];
	$params[] = [
		'type'             => 'dimension',
		'remove_from_atts' => true,
		'heading'          => esc_attr__( 'Navigation Margin', 'fusion-builder' ),
		'description'      => esc_attr__( 'Controls the space between content and navigation. Enter value including any valid CSS unit, default: 40px.', 'fusion-builder' ),
		'group'            => esc_attr__( 'Design', 'fusion-builder' ),
		'param_name'       => 'nav_margin',
		'value'            => [
			'nav_margin_top' => '',
		],
		'dependency'       => [
			[
				'element'  => 'layout',
				'value'    => 'slider',
				'operator' => '==',
			],
			[
				'element'  => 'show_nav',
				'value'    => 'yes',
				'operator' => '==',
			],
		],
	];
	$params[] = [
		'type'        => 'radio_button_set',
		'heading'     => esc_attr__( 'Animation', 'fusion-builder' ),
		'description' => esc_attr__( 'Choose for animation style.', 'fusion-builder' ),
		'group'       => esc_attr__( 'Design', 'fusion-builder' ),
		'param_name'  => 'slider_animation',
		'value'       => [
			'fade'  => esc_attr__( 'Fade', 'fusion-builder' ),
			'slide' => esc_attr__( 'Slide', 'fusion-builder' ),
		],
		'default'     => 'fade',
		'dependency'  => [
			[
				'element'  => 'layout',
				'value'    => 'slider',
				'operator' => '==',
			],
		],
	];
	$params[] = [
		'type'             => 'dimension',
		'remove_from_atts' => true,
		'heading'          => esc_attr__( 'Margin', 'fusion-builder' ),
		'description'      => esc_attr__( 'In pixels or percentage, ex: 10px or 10%.', 'fusion-builder' ),
		'param_name'       => 'margin',
		'value'            => [
			'margin_top'    => '',
			'margin_right'  => '',
			'margin_bottom' => '',
			'margin_left'   => '',
		],
		'group'            => esc_attr__( 'Design', 'fusion-builder' ),
	];

	$params['fusion_animation_placeholder'] = [
		'preview_selector' => '.fusion-post-cards',
	];
	$params[]                               = [
		'type'        => 'range',
		'heading'     => esc_attr__( 'Animation Delay', 'fusion-builder' ),
		'description' => esc_attr__( 'Controls the delay of animation between each element in a set. In seconds.', 'fusion-builder' ),
		'param_name'  => 'animation_delay',
		'group'       => esc_attr__( 'Extras', 'fusion-builder' ),
		'min'         => '0',
		'max'         => '1',
		'step'        => '0.1',
		'value'       => '0',
		'dependency'  => [
			[
				'element'  => 'animation_type',
				'value'    => '',
				'operator' => '!=',
			],
			[
				'element'  => 'layout',
				'value'    => 'grid',
				'operator' => '==',
			],
		],
		'preview'     => [
			'selector' => '.fusion-post-cards',
			'type'     => 'animation',
		],
	];

	fusion_builder_map(
		fusion_builder_frontend_data(
			'FusionSC_PostCards',
			[
				'name'         => esc_attr__( 'Post Cards', 'fusion-builder' ),
				'shortcode'    => 'fusion_post_cards',
				'icon'         => 'fusiona-post-cards-element',
				'help_url'     => 'https://theme-fusion.com/documentation/avada/elements/post-cards-cart-element/',
				'params'       => $params,
				'subparam_map' => [
					'separator_width' => 'dimensions_width',
				],
				'callback'     => [
					'function' => 'fusion_ajax',
					'action'   => 'get_fusion_post_cards',
					'ajax'     => true,
				],
			]
		)
	);
}
add_action( 'wp_loaded', 'fusion_element_post_cards' );
