<?php
/* Prevent direct access */
defined( 'ABSPATH' ) or die( "You can't access this file directly." );

if ( ! class_exists( 'ASL_Search_CPT' ) ) {
	/**
	 * Content (post,page,CPT) search class
	 *
	 * @class       ASL_Search_CPT
	 * @version     2.0
	 * @package     AjaxSearchLite/Classes/Search
	 * @category    Class
	 * @author      Ernest Marcinko
	 */
	class ASL_Search_CPT extends ASL_Search {

		/**
		 * @var array of query parts
		 */
		protected $parts = array();
		/**
		 * @var array of custom field query parts
		 */
		protected $cf_parts = array();

		protected $ordering = array(
			"primary"   => "relevance DESC",
			"secondary" => "post_date DESC",
			"primary_field" => "relevance"
		);

		/**
		 * Content search function
		 *
		 * @return array|string
		 */
		protected function do_search() {
			global $wpdb;
			global $q_config;

			$args = &$this->args;

			$asldb = wd_asl()->tables;
			if ( isset($args["_sd"]) )
				$sd = &$args["_sd"];
			else
				$sd = array();

			// Prefixes and suffixes
			$pre_field = $this->pre_field;
			$suf_field = $this->suf_field;
			$pre_like  = $this->pre_like;
			$suf_like  = $this->suf_like;

			$wcl = '%'; // Wildcard Left
			$wcr = '%'; // Wildcard right
			if ( $args["_exact_matches"] == 1 ) {
				if ( $args['_exact_match_location'] == 'start' ) {
					$wcl = '';
				} else if ( $args['_exact_match_location'] == 'end' ) {
					$wcr = '';
				} else if ( $args['_exact_match_location'] == 'full' ) {
					$wcr = '';
					$wcl = '';
				}
			}

			$kw_logic             = $args['keyword_logic'];
			$q_config['language'] = $args['_qtranslate_lang'];

			$s  = $this->s; // full keyword
			$_s = $this->_s; // array of keywords

			if ( $args['_limit'] > 0 ) {
				$this->remaining_limit = $args['_limit'];
			} else {
				if ( $args['_ajax_search'] )
					$this->remaining_limit = $args['posts_limit'];
				else
					$this->remaining_limit = $args['posts_limit_override'];
			}
			$query_limit = $this->remaining_limit * $this->remaining_limit_mod;

			if ($this->remaining_limit <= 0)
				return array();

			/*------------------------- Statuses ----------------------------*/
			$post_statuses = "";
			$allowed_statuses = "'publish'"; // used later!
			if ( count($args['post_status']) > 0) {
				$allowed_statuses = "'".implode( "','", $args['post_status'] )."'";
				$post_statuses = "AND (" . $pre_field . $wpdb->posts . ".post_status" . $suf_field . " IN ($allowed_statuses) )";
			}
			/*---------------------------------------------------------------*/

			/*------------------------- Paswword ----------------------------*/
			$post_password_query = '';
			if ( !$args['has_password'] ) {
				$post_password_query = " AND ( $wpdb->posts.post_password = '' )";
			}
			/*---------------------------------------------------------------*/

			/*----------------------- Gather Types --------------------------*/
			$page_q = "";
			if ( $args['_exclude_page_parent_child'] != '' )
				$page_q = " AND (
                    $wpdb->posts.post_parent NOT IN (" . str_replace( '|', ',', $args['_exclude_page_parent_child'] ) . ") AND
                    $wpdb->posts.ID NOT IN (" . str_replace( '|', ',', $args['_exclude_page_parent_child'] ) . ")
                )";

			// If no post types selected, well then return
			if ( count( $args['post_type'] ) < 1 && $page_q == "" ) {
				return '';
			} else {
				$words = implode( "','", $args['post_type'] );
				if ( in_array('product_variation', $args['post_type']) ) {
					$_post_types = $args['post_type'];
					$_post_types = array_diff($_post_types, array('product_variation'));
					if (count($_post_types) > 0)
						$or_ptypes = "OR $wpdb->posts.post_type IN ('".implode("', '", $_post_types)."')";
					else
						$or_ptypes = '';
					$post_types = "
                    ((
                        (
                            $wpdb->posts.post_type = 'product_variation' AND 
                            EXISTS(SELECT 1 FROM $wpdb->posts par WHERE par.ID = $wpdb->posts.post_parent AND par.post_status IN($allowed_statuses) ) 
                        )  $or_ptypes
                    ) $page_q)";
				} else {
					$post_types = "($wpdb->posts.post_type IN ('$words') $page_q)";
				}
			}
			/*---------------------------------------------------------------*/

			// ------------------------ Categories/tags/taxonomies ----------------------
			$term_query = $this->build_term_query( $wpdb->posts.".ID", $wpdb->posts.'.post_type' );
			// ---------------------------------------------------------------------

			/*------------- Custom Fields with Custom selectors -------------*/
			$cf_select = $this->build_cff_query( $wpdb->posts.".ID" );
			/*---------------------------------------------------------------*/

			/*----------------------- Exclude USER id -----------------------*/
			$user_query = "";
			if ( isset($args['post_user_filter']['include']) ) {
				if ( !in_array(-1, $args['post_user_filter']['include']) ) {
					$user_query = "AND $wpdb->posts.post_author IN (" . implode(", ", $args['post_user_filter']['include']) . ")
                    ";
				}
			}
			if ( isset($args['post_user_filter']['exclude']) ) {
				if ( !in_array(-1, $args['post_user_filter']['exclude']) )
					$user_query = "AND $wpdb->posts.post_author NOT IN (".implode(", ", $args['post_user_filter']['exclude']).") ";
				else
					return array();
			}
			/*---------------------------------------------------------------*/

			/*------------------------ Exclude ids --------------------------*/
			if ( !empty($args['post_not_in']) )
				$exclude_posts = "AND ($wpdb->posts.ID NOT IN (".(is_array($args['post_not_in']) ? implode(",", $args['post_not_in']) : $args['post_not_in'])."))";
			else
				$exclude_posts = "";
			if ( !empty($args['post_not_in2']) )
				$exclude_posts .= "AND ($wpdb->posts.ID NOT IN (".implode(",", $args['post_not_in2'])."))";
			/*---------------------------------------------------------------*/

			/*------------------------ Include ids --------------------------*/
			if ( !empty($args['post_in']) )
				$include_posts = "AND ($wpdb->posts.ID IN (".(is_array($args['post_in']) ? implode(",", $args['post_in']) : $args['post_in'])."))";
			else
				$include_posts = "";
			/*---------------------------------------------------------------*/

			/*------------------------ Term JOIN -------------------------*/
			// If the search in terms is not active, we don't need this unnecessary big join
			$term_join = "";
			if ( in_array('terms', $args['post_fields']) ) {
				$term_join = "
                LEFT JOIN $wpdb->term_relationships ON $wpdb->posts.ID = $wpdb->term_relationships.object_id
                LEFT JOIN $wpdb->term_taxonomy ON $wpdb->term_taxonomy.term_taxonomy_id = $wpdb->term_relationships.term_taxonomy_id
                LEFT JOIN $wpdb->terms ON $wpdb->term_taxonomy.term_id = $wpdb->terms.term_id";
			}
			/*---------------------------------------------------------------*/


			/*------------------------- WPML filter -------------------------*/
			$wpml_query = "(1)";
			if ( $args['_wpml_lang'] != "" ) {
				global $sitepress;
				$site_lang_selected = false;
				$wpml_post_types_arr = array();

				foreach ($args['post_type'] as $tt) {
					$wpml_post_types_arr[] = "post_" . $tt;
				}
				$wpml_post_types = implode( "','", $wpml_post_types_arr );

				// Let us get the default site language if possible
				if ( is_object($sitepress) && method_exists($sitepress, 'get_default_language') ) {
					$site_lang_selected = $sitepress->get_default_language() == $args['_wpml_lang'] ? true : false;
				}

				$_wpml_query_id_field = "$wpdb->posts.ID";
				// Product variations are not translated, so we need to use the parent ID (product) field to compare
				if ( in_array('product_variation', $args['post_type']) ) {
					$_wpml_query_id_field = "(IF($wpdb->posts.post_type='product_variation', $wpdb->posts.post_parent, $wpdb->posts.ID))";
				}

				$wpml_query = "
				EXISTS (
					SELECT DISTINCT(wpml.element_id)
					FROM " . $wpdb->prefix . "icl_translations as wpml
					WHERE
	                    $_wpml_query_id_field = wpml.element_id AND
	                    wpml.language_code = '" . ASL_Helpers::escape( $args['_wpml_lang'] ) . "' AND
	                    wpml.element_type IN ('$wpml_post_types')
                )";

				/**
				 * For missing translations..
				 * If the site language is used, the translation can be non-existent
				 */
				if ($site_lang_selected) {
					$wpml_query = "
                    NOT EXISTS (
                        SELECT DISTINCT(wpml.element_id)
                        FROM " . $wpdb->prefix . "icl_translations as wpml
                        WHERE
                            $_wpml_query_id_field = wpml.element_id AND
                            wpml.element_type IN ('$wpml_post_types')
                    ) OR
                    " . $wpml_query;
				}
			}
			/*---------------------------------------------------------------*/

			/*----------------------- POLYLANG filter -----------------------*/
			$polylang_query = "";
			if ( $args['_polylang_lang'] != "" ) {
				$languages = get_terms('language', array(
						'hide_empty' => false,
						'fields' => 'ids',
						'orderby' => 'term_group',
						'slug' => $args['_polylang_lang'])
				);
				if ( !empty($languages) && !is_wp_error($languages) && isset($languages[0]) ) {
					if ( in_array('product_variation', $args['post_type']) && class_exists('WooCommerce') ) {
						$poly_field = "IF($wpdb->posts.post_type = 'product_variation', $wpdb->posts.post_parent, $wpdb->posts.ID)";
					} else {
						$poly_field = "$wpdb->posts.ID";
					}
					$polylang_empty_query = '';
					if ( $args['_polylang_lang'] == pll_default_language() ) {
						$polylang_empty_query = "
							NOT EXISTS (
								SELECT *
								FROM $wpdb->term_relationships as xt
								INNER JOIN $wpdb->term_taxonomy as tt ON ( xt.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'language')
								WHERE
									xt.object_id = $wpdb->posts.ID
							) OR ";
					}
					$polylang_query = " AND (
						$polylang_empty_query
						$poly_field IN ( SELECT DISTINCT(tr.object_id)
							FROM $wpdb->term_relationships AS tr
							LEFT JOIN $wpdb->term_taxonomy as tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'language')
							WHERE tt.term_id = $languages[0]
						 ) 
					 )";
				}
			}
			/*---------------------------------------------------------------*/

			/*--------------------- Post parent IDs -------------------------*/
			$post_parents = '';
			if ( count($args['post_parent']) > 0 ) {
				$post_parents = "AND $wpdb->posts.post_parent IN (".implode(',', $args['post_parent']).") ";
			}
			/*---------------------------------------------------------------*/

			/*---------------- Post parent IDs exclude ----------------------*/
			$post_parents_exclude = '';
			if ( count($args['post_parent_exclude']) > 0 ) {
				$post_parents_exclude = "AND $wpdb->posts.post_parent NOT IN (".implode(',', $args['post_parent_exclude']).") ";
			}
			/*---------------------------------------------------------------*/

			/*--------------------- Other Query stuff -----------------------*/
			// Do not select the content field, if it is not used at all
			$select_content = $args['_post_get_content'] ? $wpdb->posts. ".post_content" : "''";

			// Do not select excerpt if its not used at all
			$select_excerpt = $args['_post_get_excerpt'] ? $wpdb->posts. ".post_excerpt" : "''";
			/*---------------------------------------------------------------*/

			/*----------------------- Date filtering ------------------------*/
			$date_query = "";
			$date_query_parts = $this->get_date_query_parts();
			if ( count($date_query_parts) > 0 )
				$date_query = " AND (" . implode(" AND ", $date_query_parts) . ") ";
			/*---------------------------------------------------------------*/

			/*-------------- Additional Query parts by Filters --------------*/
			/**
			 * Use these filters to add additional parts to the select, join or where
			 * parts of the search query.
			 * Params:
			 *  array() $args - search arguments
			 *  string $s - the full search keyword
			 *  array() $_s - the array of unique search keywords separated
			 */
			$add_select = apply_filters('asl_cpt_query_add_select', '', $args, $s, $_s);
			$add_join = apply_filters('asl_cpt_query_add_join', '', $args, $s, $_s);
			$add_where = apply_filters('asl_cpt_query_add_where', '', $args, $s, $_s);
			/*---------------------------------------------------------------*/

			/*---------------- Primary custom field ordering ----------------*/
			$custom_field_selectp = "1 ";
			if (
				strpos($args['post_primary_order'], 'customfp') !== false &&
				$args['_post_primary_order_metakey'] !== false
			) {
				$custom_field_selectp = "(SELECT IF(meta_value IS NULL, 0, meta_value)
                FROM $wpdb->postmeta
                WHERE
                    $wpdb->postmeta.meta_key='".esc_sql($args['_post_primary_order_metakey'])."' AND
                    $wpdb->postmeta.post_id=$wpdb->posts.ID
                LIMIT 1
                ) ";
			}
			/*---------------------------------------------------------------*/

			/*--------------- Secondary custom field ordering ---------------*/
			$custom_field_selects = "1 ";
			if (
				strpos($args['post_secondary_order'], 'customfs') !== false &&
				$args['_post_secondary_order_metakey'] !== false
			) {
				$custom_field_selects = "(SELECT IF(meta_value IS NULL, 0, meta_value)
                FROM $wpdb->postmeta
                WHERE
                    $wpdb->postmeta.meta_key='".esc_sql($args['_post_secondary_order_metakey'])."' AND
                    $wpdb->postmeta.post_id=$wpdb->posts.ID
                LIMIT 1
                ) ";
			}
			/*---------------------------------------------------------------*/


			/**
			 * This is the main query.
			 *
			 * The ttid field is a bit tricky as the term_taxonomy_id doesn't always equal term_id,
			 * so we need the LEFT JOINS :(
			 */
			$this->ordering['primary'] = $args['post_primary_order'];
			$this->ordering['secondary'] = $args['post_secondary_order'];

			$_primary_field = explode(" ", $this->ordering['primary']);
			$this->ordering['primary_field'] = $_primary_field[0];

			$orderby_primary    = str_replace( "post_", $wpdb->posts . ".post_",  $args['post_primary_order'] );
			$orderby_secondary  = str_replace( "post_", $wpdb->posts . ".post_",  $args['post_secondary_order'] );

			if (
				$args['post_primary_order_metatype'] !== false &&
				$args['post_primary_order_metatype'] == 'numeric'
			)
				$orderby_primary = str_replace('customfp', 'CAST(customfp as SIGNED)', $orderby_primary);

			if (
				$args['post_secondary_order_metatype'] !== false &&
				$args['post_secondary_order_metatype'] == 'numeric'
			)
				$orderby_secondary = str_replace('customfs', 'CAST(customfs as SIGNED)', $orderby_secondary);

			$this->query = "
    		SELECT
    		    $add_select
    		    {args_fields}
                $wpdb->posts.post_title as title,
                $wpdb->posts.post_title as post_title,
                $wpdb->posts.ID as id,
                $this->c_blogid as blogid,
                $wpdb->posts.post_date as date,
                $wpdb->posts.post_date as post_date,
                $select_content as content,
                $select_excerpt as excerpt,
                $wpdb->posts.post_type as post_type,
                'pagepost' as content_type,
                'post_page_cpt' as g_content_type,
                (SELECT
                    $wpdb->users." . w_isset_def( $sd['author_field'], 'display_name' ) . " as author
                    FROM $wpdb->users
                    WHERE $wpdb->users.ID = $wpdb->posts.post_author
                ) as author,
                $wpdb->posts.post_author as post_author,
                $wpdb->posts.post_type as post_type,
                {relevance_query} as relevance,
                $custom_field_selectp as customfp,
                $custom_field_selects as customfs
            FROM $wpdb->posts
                $term_join
                $add_join
                {args_join}
            WHERE
                $post_types
                $term_query
                $user_query
                AND $cf_select
                $post_statuses
                $post_password_query
                AND {like_query}
                $exclude_posts
                $include_posts
                $post_parents
                $post_parents_exclude
                AND ( $wpml_query )
                $polylang_query
                $date_query
                $add_where
                {args_where}
            GROUP BY
                {args_groupby}
            ORDER BY {args_orderby} $orderby_primary, $orderby_secondary, id DESC
            LIMIT $query_limit";

			// Place the argument query fields
			if ( isset($args['cpt_query']) && is_array($args['cpt_query']) ) {
				$this->query = str_replace(
					array('{args_fields}', '{args_join}', '{args_where}', '{args_orderby}'),
					array($args['cpt_query']['fields'], $args['cpt_query']['join'], $args['cpt_query']['where'], $args['cpt_query']['orderby']),
					$this->query
				);
			} else {
				$this->query = str_replace(
					array('{args_fields}', '{args_join}', '{args_where}', '{args_orderby}'),
					'',
					$this->query
				);
			}
			if ( isset($args['cpt_query'], $args['cpt_query']['groupby']) && $args['cpt_query']['groupby'] != '' ) {
				$this->query = str_replace('{args_groupby}', $args['cpt_query']['groupby'], $this->query);
			} else {
				$this->query = str_replace('{args_groupby}', "$wpdb->posts.ID", $this->query);
			}

			$words = $args["_exact_matches"] == 1 && $s != '' ? array($s) : $_s;
			/**
			 * Ex.: When the minimum word count is 2, and the user enters 'a' then $_s is empty.
			 *      But $s is not actually empty, thus the wrong query will be executed.
			 */
			if ( count($words) == 0 && $s != '' ) {
				$words = array($s);
				// Allow only beginnings
				if ( $args["_exact_matches"] == 0 )
					$wcl = '';
			}
			if ( $s != '' )
				$words = !in_array($s, $words) ? array_merge(array($s), $words) : $words;

			$relevance_added = false;
			foreach ( $words as $k => $word ) {
				$parts           = array();
				$relevance_parts = array();
				$is_exact = $args["_exact_matches"] == 1 || ( count($words) > 1 && $k == 0 && ($kw_logic == 'or' || $kw_logic == 'and') );

				/*----------------------- Title query ---------------------------*/
				if ( in_array('title', $args['post_fields']) ) {
					if ( $kw_logic == 'or' || $kw_logic == 'and' || $is_exact ) {
						$parts[] = "( " . $pre_field . $wpdb->posts . ".post_title" . $suf_field . " LIKE $pre_like'$wcl" . $word . "$wcr'$suf_like )";
					} else {
						$parts[] = "
                               ( " . $pre_field . $wpdb->posts . ".post_title" . $suf_field . " LIKE $pre_like'% " . $word . " %'$suf_like
                            OR  " . $pre_field . $wpdb->posts . ".post_title" . $suf_field . " LIKE $pre_like'" . $word . " %'$suf_like
                            OR  " . $pre_field . $wpdb->posts . ".post_title" . $suf_field . " LIKE $pre_like'% " . $word . "'$suf_like
                            OR  " . $pre_field . $wpdb->posts . ".post_title" . $suf_field . " = '" . $word . "')";
					}
					if ( !$relevance_added ) {
						$relevance_parts[] = "(case when
                    (" . $pre_field . $wpdb->posts . ".post_title" . $suf_field . " LIKE '$s')
                     then " . (w_isset_def($sd['etitleweight'], 10) * 4) . " else 0 end)";

						$relevance_parts[] = "(case when
                    (" . $pre_field . $wpdb->posts . ".post_title" . $suf_field . " LIKE '$s%')
                     then " . (w_isset_def($sd['etitleweight'], 10) * 2) . " else 0 end)";

						$relevance_parts[] = "(case when
                    (" . $pre_field . $wpdb->posts . ".post_title" . $suf_field . " LIKE '%$s%')
                     then " . w_isset_def($sd['titleweight'], 10) . " else 0 end)";

						// The first word relevance is higher
						if ( isset($_s[0]) ) {
							$relevance_parts[] = "(case when
                      (" . $pre_field . $wpdb->posts . ".post_title" . $suf_field . " LIKE '%" . $_s[0] . "%')
                       then " . w_isset_def($sd['etitleweight'], 10) . " else 0 end)";
						}
					}
				}
				/*---------------------------------------------------------------*/

				/*---------------------- Content query --------------------------*/
				if ( in_array('content', $args['post_fields']) ) {
					if ( $kw_logic == 'or' || $kw_logic == 'and' || $is_exact ) {
						$parts[] = "( " . $pre_field . $wpdb->posts . ".post_content" . $suf_field . " LIKE $pre_like'$wcl" . $word . "$wcr'$suf_like )";
						/**
						 * Exact matching multi line + word boundary with REGEXP
						 *
						 * $parts[] = "( " . $pre_field . $wpdb->posts . ".post_content" . $suf_field . " REGEXP '([[:blank:][:punct:]]|^|\r\n)" . $word . "([[:blank:][:punct:]]|$|\r\n)' )";
						 */
					} else {
						$parts[] = "
                           (" . $pre_field . $wpdb->posts . ".post_content" . $suf_field . " LIKE $pre_like'% " . $word . " %'$suf_like
                        OR  " . $pre_field . $wpdb->posts . ".post_content" . $suf_field . " LIKE $pre_like'" . $word . " %'$suf_like
                        OR  " . $pre_field . $wpdb->posts . ".post_content" . $suf_field . " LIKE $pre_like'% " . $word . "'$suf_like
                        OR  " . $pre_field . $wpdb->posts . ".post_content" . $suf_field . " = '" . $word . "')";
					}
					if ( !$relevance_added ) {
						if ( isset($_s[0]) ) {
							$relevance_parts[] = "(case when
                            (" . $pre_field . $wpdb->posts . ".post_content" . $suf_field . " LIKE '%" . $_s[0] . "%')
                             then ".w_isset_def($sd['contentweight'], 10)." else 0 end)";
						}

						$relevance_parts[] = "(case when
                    (" . $pre_field . $wpdb->posts . ".post_content" . $suf_field . " LIKE '%$s%')
                     then " . w_isset_def($sd['econtentweight'], 10) . " else 0 end)";
					}
				}
				/*---------------------------------------------------------------*/

				/*----------------- Permalink/Post name query -------------------*/
				if ( in_array('permalink', $args['post_fields']) ) {
					$parts[] = "( " . $pre_field . $wpdb->posts . ".post_name" . $suf_field . " LIKE $pre_like'$wcl" . $word . "$wcr'$suf_like )";
				}
				/*---------------------------------------------------------------*/

				/*---------------------- Excerpt query --------------------------*/
				if ( in_array('excerpt', $args['post_fields']) ) {
					if ( $kw_logic == 'or' || $kw_logic == 'and' || $is_exact ) {
						$parts[] = "( " . $pre_field . $wpdb->posts . ".post_excerpt" . $suf_field . " LIKE $pre_like'$wcl" . $word . "$wcr'$suf_like )";
					} else {
						$parts[] = "
                           (" . $pre_field . $wpdb->posts . ".post_excerpt" . $suf_field . " LIKE $pre_like'% " . $word . " %'$suf_like
                        OR  " . $pre_field . $wpdb->posts . ".post_excerpt" . $suf_field . " LIKE $pre_like'" . $word . " %'$suf_like
                        OR  " . $pre_field . $wpdb->posts . ".post_excerpt" . $suf_field . " LIKE $pre_like'% " . $word . "'$suf_like
                        OR  " . $pre_field . $wpdb->posts . ".post_excerpt" . $suf_field . " = '" . $word . "')";
					}
					if ( !$relevance_added ) {
						if ( isset($_s[0]) ) {
							$relevance_parts[] = "(case when
                            (" . $pre_field . $wpdb->posts . ".post_excerpt" . $suf_field . " LIKE '%" . $_s[0] . "%')
                             then ".w_isset_def($sd['excerptweight'], 10)." else 0 end)";
						}

						$relevance_parts[] = "(case when
                    (" . $pre_field . $wpdb->posts . ".post_excerpt" . $suf_field . " LIKE '%$s%')
                     then " . w_isset_def($sd['eexcerptweight'], 10) . " else 0 end)";
					}
				}
				/*---------------------------------------------------------------*/

				/*------------------------ Term query ---------------------------*/
				if ( in_array('terms', $args['post_fields']) ) {
					if ( $kw_logic == 'or' || $kw_logic == 'and' || $is_exact ) {
						$parts[] = "( " . $pre_field . $wpdb->terms . ".name" . $suf_field . " LIKE $pre_like'$wcl" . $word . "$wcr'$suf_like )";
					} else {
						$parts[] = "
                           (" . $pre_field . $wpdb->terms . ".name" . $suf_field . " LIKE $pre_like'% " . $word . " %'$suf_like
                        OR  " . $pre_field . $wpdb->terms . ".name" . $suf_field . " LIKE $pre_like'" . $word . " %'$suf_like
                        OR  " . $pre_field . $wpdb->terms . ".name" . $suf_field . " LIKE $pre_like'% " . $word . "'$suf_like
                        OR  " . $pre_field . $wpdb->terms . ".name" . $suf_field . " = '" . $word . "')";
					}

					if ( !$relevance_added ) {
						$relevance_parts[] = "(case when
                    (" . $pre_field . $wpdb->terms . ".name" . $suf_field . " = '$s')
                     then " . w_isset_def($sd['etermsweight'], 10) . " else 0 end)";
					}
				}
				/*---------------------------------------------------------------*/

				/*---------------------- Custom Fields --------------------------*/
				if ( $args['post_custom_fields_all'] == 1 )
					$args['post_custom_fields'] = array("all");

				if ( count($args['post_custom_fields']) > 0 ) {
					$cf_parts = array();
					foreach ( $args['post_custom_fields'] as $cfield ) {
						$key_part = $args['post_custom_fields_all'] == 1 ? "" : "$wpdb->postmeta.meta_key='$cfield' AND ";

						if ( $kw_logic == 'or' || $kw_logic == 'and' || $is_exact ) {
							$cf_parts[] = "( $key_part " . $pre_field . $wpdb->postmeta . ".meta_value" . $suf_field . " LIKE $pre_like'$wcl" . $word . "$wcr'$suf_like )";
						} else {
							$cf_parts[] = "( $key_part 
							(" . $pre_field . $wpdb->postmeta . ".meta_value" . $suf_field . " LIKE $pre_like'% " . $word . " %'$suf_like
							OR  " . $pre_field . $wpdb->postmeta . ".meta_value" . $suf_field . " LIKE $pre_like'" . $word . " %'$suf_like
							OR  " . $pre_field . $wpdb->postmeta . ".meta_value" . $suf_field . " LIKE $pre_like'% " . $word . "'$suf_like
							OR  " . $pre_field . $wpdb->postmeta . ".meta_value" . $suf_field . " = '" . $word . "') )";
						}
						
					}
					$parts[] = "( EXISTS (SELECT 1 FROM $wpdb->postmeta WHERE (".implode(' OR ', $cf_parts).") AND $wpdb->posts.ID = $wpdb->postmeta.post_id) )";
				}
				/*---------------------------------------------------------------*/

				/*---------------------- Post CPT IDs ---------------------------*/
				if ( in_array("ids", $args['post_fields']) )
					$parts[] = "($wpdb->posts.ID LIKE '$word')";
				/*---------------------------------------------------------------*/

				$this->parts[] = array( $parts, $relevance_parts );
				$relevance_added = true;
			}

			$querystr = $this->build_query( $this->parts );
			$querystr = apply_filters('asl_query_cpt', $querystr, $args, $args['_id'], $args['_ajax_search']);
			$all_pageposts = $wpdb->get_results( $querystr, OBJECT );

			// Get the real count, up to 500
			$this->results_count = count($all_pageposts);
			// For non-ajax search, results count needs to be limited to the maximum limit,
			// ..as nothing is parsed beyond that
			if ($args['_ajax_search'] == false && $this->results_count > $this->remaining_limit) {
				$this->results_count = $this->remaining_limit;
			}
			/**
			 * Order them again:
			 *  - The custom field ordering always uses alphanumerical comparision, which is not ok
			 */
			if (
				count($all_pageposts) > 0 &&
				(
					strpos($args['post_primary_order'], 'customfp') !== false ||
					strpos($args['post_secondary_order'], 'customfs') !== false
				)
			) {
				usort( $all_pageposts, array( $this, 'compare_by_primary' ) );
				/**
				 * Let us save some time. There is going to be a user selecting the same sorting
				 * for both primary and secondary. Only do secondary if it is different from the primary.
				 */
				if ( $this->ordering['primary'] != $this->ordering['secondary'] ) {
					$i = 0;
					foreach ($all_pageposts as $pk => $pp) {
						$all_pageposts[$pk]->primary_order = $i;
						$i++;
					}

					usort( $all_pageposts, array( $this, 'compare_by_secondary' ) );
				}
			}
			// Slice the needed ones only
			$all_pageposts = array_slice($all_pageposts, $args['_call_num'] * $this->remaining_limit, $this->remaining_limit);

			$this->results = $all_pageposts;
			$this->return_count = count($this->results);

			return $all_pageposts;
		}

		protected function build_pgp_query($post_id_field) {
			global $wpdb;
			$query = '1';
			$s  = $this->s;
			$args = $this->args;

			$group_query_arr = array();
			foreach(wd_asl()->priority_groups->getSorted() as $group) {
				// Rule affects this instance?
				if ($group['instance'] != 0 && $group['instance'] != $args['_sid'])
					continue;
				// Rule affects this phrase?
				if ( ASL_mb::strlen($group['phrase']) > 0 ) {
					switch ($group['phrase_logic']) {
						case 'any':
							if (ASL_mb::strpos($s, $group['phrase']) === false)
								continue 2;
							break;
						case 'exact':
							if ($s !== $group['phrase'])
								continue 2;
							break;
						case 'start':
							if (ASL_mb::strpos($s, $group['phrase']) !== 0)
								continue 2;
							break;
						case 'end':
							if (!(ASL_mb::substr($s, -ASL_mb::strlen($group['phrase'])) === $group['phrase']))
								continue 2;
							break;
					}
				}
				$rule_query_arr = array();
				foreach($group['rules'] as $rule) {
					switch($rule['field']) {
						case 'tax':
							$tax_term_query_arr = array();
							foreach ( $rule['values'] as $taxonomy => $terms ) {
								if (count($terms) < 1)
									continue;
								$term_ids = implode(',', $terms);
								if ( $rule['operator'] == 'in' ) {
									$operator = 'EXISTS';
								} else {
									$operator = 'NOT EXISTS';
								}
								$tax_term_query_arr[] = "(
                                $operator ( SELECT 1
                                        FROM $wpdb->term_relationships AS gptr
                                        LEFT JOIN $wpdb->term_taxonomy as gptt ON (gptr.term_taxonomy_id = gptt.term_taxonomy_id AND gptt.taxonomy = '$taxonomy')
                                        WHERE gptt.term_id IN ($term_ids) AND gptr.object_id = $post_id_field
                                 ) )";
							}
							if ( count($tax_term_query_arr) ) {
								$tax_term_query = '( '.implode(' AND ', $tax_term_query_arr) . ' )';
								$rule_query_arr[] = $tax_term_query;
							}
							break;
						case 'cf':
							// Get the field and the values
							foreach ( $rule['values'] as $field => $values )
								break;
							$field = ASL_Helpers::escape($field);

							$numeric_operators = array('=', '<>', '>', '>=', '<', '<=');
							if ( false !== $nkey = array_search($rule['operator'], $numeric_operators) ) {
								$operator = $numeric_operators[$nkey];
								$values = ASL_Helpers::force_numeric($values);
								$qry = " gp_pm.meta_value".$operator.$values[0];
							} else if(
								$rule['operator'] == 'between' &&
								isset($values[0], $values[1])
							) {
								$qry = " gp_pm.meta_value BETWEEN ".$values[0]. " AND ".$values[1];
							} else if ( $rule['operator'] == 'like' ) {
								$qry = " gp_pm.meta_value LIKE '%".$values[0]."%'";
							} else if ( $rule['operator'] == 'not like' ) {
								$qry = " gp_pm.meta_value NOT LIKE '%".$values[0]."%'";
							} else if ( $rule['operator'] == 'elike' ) {
								$qry = " gp_pm.meta_value LIKE '".$values[0]."'";
							} else {
								// None of the operators above?
								continue 2;
							}

							$cf_query = "
                            (EXISTS(
                                SELECT 1
                                FROM $wpdb->postmeta as gp_pm
                                WHERE gp_pm.post_id = $post_id_field AND gp_pm.meta_key='$field' AND $qry
                                GROUP BY gp_pm.meta_id
                                ORDER BY gp_pm.meta_id
                                LIMIT 1
                            ))";
							$rule_query_arr[] = $cf_query;
							break;
					}
				}

				// Construct the WHEN-THEN for this case
				if ( count($rule_query_arr) > 0 ) {
					$priority = $group['priority'] + 0;
					if ( $group['logic'] == 'and' )
						$rule_logic = ' AND ';
					else
						$rule_logic = ' OR ';
					$condition = implode($rule_logic, $rule_query_arr);
					$group_query_arr[] = "WHEN $condition THEN $priority";
				}

			}

			if ( count($group_query_arr) > 0 ) {
				$query = implode(' ', $group_query_arr);
				$query = "(CASE
                    $query
                    ELSE 1
                END)";
			}

			return $query;
		}

		protected function build_term_query($post_id_field, $post_type_field) {
			global $wpdb;
			$args = $this->args;

			if ( isset($_GET['ignore_op']) ) return "";

			$term_query = "";
			$term_query_parts = array();

			foreach ($args['post_tax_filter'] as $k => $item) {
				$tax_term_query = '';
				$taxonomy = $item['taxonomy'];

				// Is there an argument set to allow empty items for this taxonomy filter?
				if ( isset($item['allow_empty']) ) {
					$allow_empty_tax_term = $item['allow_empty'];
				} else {
					$allow_empty_tax_term = $taxonomy == 'post_tag' ? $args["_post_tags_empty"] : $args['_post_allow_empty_tax_term'];
				}

				if ( $allow_empty_tax_term == 1 ) {
					$empty_terms_query = "
                    NOT EXISTS (
                        SELECT *
                        FROM $wpdb->term_relationships as xt
                        INNER JOIN $wpdb->term_taxonomy as tt ON ( xt.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = '$taxonomy')
                        WHERE
                            xt.object_id = $post_id_field
                    ) OR ";
				} else {
					$empty_terms_query = "";
				}

				// Quick explanation for the AND
				// .. MAIN SELECT: selects all object_ids that are not in the array
				// .. SUBSELECT:   excludes all the object_ids that are part of the array
				// This is used because of multiple object_ids (posts in more than 1 tag)
				if ( !empty($item['exclude']) ) {
					$words = implode( ',', $item['exclude'] );
					$tax_term_query = " (
                        $empty_terms_query

						$post_id_field IN (
							SELECT DISTINCT(tr.object_id)
								FROM $wpdb->term_relationships AS tr
				                LEFT JOIN $wpdb->term_taxonomy as tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = '$taxonomy')
												WHERE
													tt.term_id NOT IN ($words)
													AND tr.object_id NOT IN (
														SELECT DISTINCT(trs.object_id)
														FROM $wpdb->term_relationships AS trs
				                    LEFT JOIN $wpdb->term_taxonomy as tts ON (trs.term_taxonomy_id = tts.term_taxonomy_id AND tts.taxonomy = '$taxonomy')
														WHERE tts.term_id IN ($words)
													)
										)
									)";
				}
				if ( !empty($item['include']) ) {
					$words = implode( ',', $item['include'] );
					if ( !empty($tax_term_query) )
						$tax_term_query .= " AND ";
					if ( isset($item['logic']) && $item['logic'] == 'andex' ) {
						$tax_term_query .= "(
                            $empty_terms_query
    
                            ".count($item['include'])." = ( SELECT COUNT(tr.object_id)
                                FROM $wpdb->term_relationships AS tr
                                LEFT JOIN $wpdb->term_taxonomy as tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = '$taxonomy')
                                WHERE tt.term_id IN ($words) AND tr.object_id = $post_id_field
                          ) )";
					} else {
						$tax_term_query .= "(
                            $empty_terms_query
    
                            $post_id_field IN ( SELECT DISTINCT(tr.object_id)
                                FROM $wpdb->term_relationships AS tr
                                LEFT JOIN $wpdb->term_taxonomy as tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = '$taxonomy')
                                WHERE tt.term_id IN ($words)
                          ) )";
					}
				}


				/**
				 * POST TAG SPECIFIC ONLY
				 *
				 * At this point we need to check if the user wants to hide the empty tags but the $tag_query
				 * turned out to be empty. (if not all tags are used and all of them are selected).
				 * If so, then return true on every post type other than 'post' OR check if any tags
				 * are associated with the post.
				 */
				if (
					$taxonomy == 'post_tag' &&
					$args['_post_tags_active'] == 1 &&
					$tax_term_query == "" &&
					$args["_post_tags_empty"] == 0
				) {
					$tax_term_query = "
                    (
                        ($post_type_field != 'post') OR

                        EXISTS (
                            SELECT *
                            FROM $wpdb->term_relationships as xt
                            INNER JOIN $wpdb->term_taxonomy as tt ON ( xt.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'post_tag')
                            WHERE
                                xt.object_id = $post_id_field
                        )
                    )";
				}
				// ----------------------------------------------------

				if ( !empty($tax_term_query) )
					$term_query_parts[] = "(" . $tax_term_query . ")";
			}

			if ( !empty($term_query_parts) )
				$term_query = "AND (" . implode(" ".strtoupper($args['_taxonomy_group_logic'])." ", $term_query_parts) . ") ";

			return $term_query;
		}

		protected function build_cff_query( $post_id_field ) {
			global $wpdb;
			$args = $this->args;
			$parts = array();

			$allow_cf_null = $args['_post_meta_allow_null'];

			foreach ( $args['post_meta_filter'] as $data ) {

				$operator = $data['operator'];
				$posted = $data['value'];
				$field = $data['key'];
				$logic_and_separate_custom_fields = isset($data['logic_and_separate_custom_fields']) && true;
				$values_count = 1;

				// Is this a special case of date operator?
				if (strpos($operator, "datetime") === 0) {
					$date_add = $allow_cf_null ? '' : "$wpdb->postmeta.meta_value<>'' AND ";
					switch ($operator) {
						case 'datetime =':
							$current_part = "($date_add $wpdb->postmeta.meta_value BETWEEN '$posted 00:00:00' AND '$posted 23:59:59')";
							break;
						case 'datetime <>':
							$current_part = "($date_add $wpdb->postmeta.meta_value NOT BETWEEN '$posted 00:00:00' AND '$posted 23:59:59')";
							break;
						case 'datetime <':
							$current_part = "($date_add $wpdb->postmeta.meta_value < '$posted 00:00:00')";
							break;
						case 'datetime <=':
							$current_part = "($date_add $wpdb->postmeta.meta_value <= '$posted 23:59:59')";
							break;
						case 'datetime >':
							$current_part = "($date_add $wpdb->postmeta.meta_value > '$posted 23:59:59')";
							break;
						case 'datetime >=':
							$current_part = "($date_add $wpdb->postmeta.meta_value >= '$posted 00:00:00')";
							break;
						default:
							$current_part = "($date_add $wpdb->postmeta.meta_value < '$posted 00:00:00')";
							break;
					}
					// Is this a special case of timestamp?
				} else if (strpos($operator, "timestamp") === 0) {
					$date_add = $allow_cf_null ? '' : "$wpdb->postmeta.meta_value<>'' AND ";
					switch ($operator) {
						case 'timestamp =':
							$current_part = "($date_add $wpdb->postmeta.meta_value BETWEEN $posted AND ".($posted + 86399).")";
							break;
						case 'timestamp <>':
							$current_part = "($date_add $wpdb->postmeta.meta_value NOT BETWEEN $posted AND ".($posted + 86399).")";
							break;
						case 'timestamp <':
							$current_part = "($date_add $wpdb->postmeta.meta_value < $posted)";
							break;
						case 'timestamp <=':
							$current_part = "($date_add $wpdb->postmeta.meta_value <= ".($posted + 86399).")";
							break;
						case 'timestamp >':
							$current_part = "($date_add $wpdb->postmeta.meta_value > ".($posted + 86399).")";
							break;
						case 'timestamp >=':
							$current_part = "($date_add $wpdb->postmeta.meta_value >= $posted)";
							break;
						default:
							$current_part = "($date_add $wpdb->postmeta.meta_value < $posted)";
							break;
					}
					// Check BETWEEN first -> range slider
				} else if ( $operator === "BETWEEN" ) {
					$current_part = "($wpdb->postmeta.meta_value BETWEEN " . $posted[0] . " AND " . $posted[1] . " )";
					// If not BETWEEN but value is array, then drop-down or checkboxes
				} else if ( is_array($posted) ) {
					// Is there a logic sent?
					$logic  = isset($data['logic']) ? $data['logic'] : "OR";
					$values_count = count($posted);
					if ( $logic_and_separate_custom_fields && $logic == 'AND' )
						$logic = 'OR';
					$values = '';
					if ($operator === "IN" ) {
						$val = implode("','", $posted);
						if ( !empty($val) ) {
							if ($values != '') {
								$values .= " $logic $wpdb->postmeta.meta_value $operator ('" . $val . "')";
							} else {
								$values .= "$wpdb->postmeta.meta_value $operator ('" . $val . "')";
							}
						}
					} else {
						foreach ($posted as $v) {
							if ($operator === "ELIKE" || $operator === "NOT ELIKE") {
								$_op = $operator === 'ELIKE' ? 'LIKE' : 'NOT LIKE';
								if ($values != '') {
									$values .= " $logic $wpdb->postmeta.meta_value $_op '" . $v . "'";
								} else {
									$values .= "$wpdb->postmeta.meta_value $_op '" . $v . "'";
								}
							} else if ($operator === "NOT LIKE" || $operator === "LIKE") {
								if ($values != '') {
									$values .= " $logic $wpdb->postmeta.meta_value $operator '%" . $v . "%'";
								} else {
									$values .= "$wpdb->postmeta.meta_value $operator '%" . $v . "%'";
								}
							} else {
								if ($values != '') {
									$values .= " $logic $wpdb->postmeta.meta_value $operator " . $v;
								} else {
									$values .= "$wpdb->postmeta.meta_value $operator " . $v;
								}
							}
						}
					}

					$values  = $values == '' ? '0' : $values;
					$current_part = "($values)";
					// String operations
				} else if ($operator === "NOT LIKE" || $operator === "LIKE") {
					$current_part = "($wpdb->postmeta.meta_value $operator '%" . $posted . "%')";
				} else if ($operator === "ELIKE" || $operator === "NOT ELIKE") {
					$_op = $operator === 'ELIKE' ? 'LIKE' : 'NOT LIKE';
					$current_part = "($wpdb->postmeta.meta_value $_op '$posted')";
				} else {
					// Numeric operations or problematic stuff left
					$current_part = "($wpdb->postmeta.meta_value $operator $posted  )";
				}

				// Finally add the current part to the parts array
				if ( $current_part != "") {
					if ( isset($data['allow_missing']) )
						$allowance = $data['allow_missing'];
					else
						$allowance = $allow_cf_null;

					$parts[] = array($field, $current_part, $allowance, $logic_and_separate_custom_fields, $values_count);
				}
			}

			// The correct count is the unique fields count
			//$meta_count = count( $unique_fields );

			$cf_select = "(1)";
			$cf_select_arr = array();

			/**
			 * NOTE 1:
			 * With the previous NOT EXISTS(...) subquery solution the search would hang in some cases
			 * when checking if empty values are allowed. No idea why though...
			 * Eventually using separate sub-queries for each field is the best.
			 *
			 * NOTE 2:
			 * COUNT(post_id) is a MUST in the nested IF() statement !! Otherwise the query will return empty rows, no idea why either..
			 */

			foreach ( $parts as $k => $part ) {
				$field = $part[0];          // Field name
				$def = $part[2] ? "(
                    SELECT IF((meta_key IS NULL OR meta_value = ''), -1, COUNT(meta_id))
                    FROM $wpdb->postmeta
                    WHERE $wpdb->postmeta.post_id = $post_id_field AND $wpdb->postmeta.meta_key='$field'
                    LIMIT 1
                  ) = -1
                 OR" : '';                  // Allowance
				$qry = $part[1];            // Query condition
				if ( $part[3] ) {           // AND logic in for separated fields?
					$compare_to = intval($part[4]);
				} else {
					$compare_to = 1;
				}
				$cf_select_arr[] = "
                (
                  $def
                  (
                    SELECT COUNT(meta_id) as mtc
                    FROM $wpdb->postmeta
                    WHERE $wpdb->postmeta.post_id = $post_id_field AND $wpdb->postmeta.meta_key='$field' AND $qry
                    ORDER BY mtc
                    LIMIT 1
                  )  >= $compare_to
                )";
			}
			if ( count($cf_select_arr) ) {
				// Connect them based on the meta logic
				$cf_select = "( ". implode( $args['_post_meta_logic'], $cf_select_arr ) . " )";
			}

			return $cf_select;
		}

		protected function get_date_query_parts( $table_alias = "", $date_field = "post_date" ) {
			global $wpdb;
			$args = $this->args;

			if ( empty($table_alias) )
				$table_alias = $wpdb->posts;

			$date_query_parts = array();

			foreach( $args['post_date_filter'] as $date_filter ) {
				if (isset($date_filter['date']))    // Given by date argument
					$date = $date_filter['date'];
				else                                // Given by year, month, day arguments
					$date = $date_filter['year'] . "-" . sprintf("%02d", $date_filter['month']) . "-" . sprintf("%02d", $date_filter['day']);

				if ($date_filter['interval'] == "before") {
					$op = $date_filter['operator'] == "exclude" ? ">" : "<=";
					$date_query_parts[] = "$table_alias.$date_field $op '" . $date . " 23:59:59'";
				} else {
					$op = $date_filter['operator'] == "exclude" ? "<" : ">=";
					$date_query_parts[] = "$table_alias.$date_field $op '" . $date . " 00:00:00'";
				}
			}
			return $date_query_parts;
		}


		/**
		 * usort() custom function, sort by primary
		 *
		 * @param $a
		 * @param $b
		 *
		 * @return int
		 */
		protected function compare_by_primary( $a, $b ) {

			switch ($this->ordering['primary']) {
				case "average_rating DESC":
					// ceil() is very important here!! as this expects 1, 0, -1 but no values inbetween
					return ceil((float)$b->average_rating - (float)$a->average_rating);
				case "relevance DESC":
					return $b->relevance - $a->relevance;
				case "post_date DESC":
					$date_diff = strtotime($b->date) - strtotime($a->date);
					if ($date_diff == 0)
						return $b->id - $a->id;

					return $date_diff;
				case "post_date ASC":
					$date_diff = strtotime($a->date) - strtotime($b->date);
					if ($date_diff == 0)
						return $a->id - $b->id;

					return $date_diff;
				case "post_title DESC":
					return strcasecmp($b->title, $a->title);
				case "post_title ASC":
					return strcasecmp($a->title, $b->title);
				case "menu_order DESC":
					return $b->menu_order - $a->menu_order;
				case "menu_order ASC":
					return $a->menu_order - $b->menu_order;
				case "customfp DESC":
					if ($this->args['post_primary_order_metatype'] == 'numeric')
						return floatval($b->customfp) - floatval($a->customfp);
					else
						return strcasecmp($b->customfp, $a->customfp);
				case "customfp ASC":
					if ($this->args['post_primary_order_metatype'] == 'numeric')
						return floatval($a->customfp) - floatval($b->customfp);
					else
						return strcasecmp($a->customfp, $b->customfp);
				case "RAND()":
					return rand(-1,1);
				default:
					return $b->relevance - $a->relevance;
			}
		}

		/**
		 * usort() custom function, sort by secondary
		 *
		 * @param $a
		 * @param $b
		 *
		 * @return int
		 */
		protected function compare_by_secondary( $a, $b ) {

			$field = $this->ordering['primary_field'];

			if ($a->$field == $b->$field) {

				switch ($this->ordering['secondary']) {
					case "relevance DESC":
						return $b->relevance - $a->relevance;
					case "post_date DESC":
						$date_diff = strtotime($b->date) - strtotime($a->date);
						if ($date_diff == 0)
							return $b->id - $a->id;
						return $date_diff;
					case "post_date ASC":
						$date_diff = strtotime($a->date) - strtotime($b->date);
						if ($date_diff == 0)
							return $a->id - $b->id;
						return $date_diff;
					case "post_title DESC":
						return strcasecmp($b->title, $a->title);
					case "post_title ASC":
						return strcasecmp($a->title, $b->title);
					case "menu_order DESC":
						return $b->menu_order - $a->menu_order;
					case "menu_order ASC":
						return $a->menu_order - $b->menu_order;
					case "customfs DESC":
						if ($this->args['post_secondary_order_metatype'] == 'numeric')
							return $b->customfs - $a->customfs;
						else
							return strcasecmp($b->customfs, $a->customfs);
					case "customfs ASC":
						if ($this->args['post_secondary_order_metatype'] == 'numeric')
							return $a->customfs - $b->customfs;
						else
							return strcasecmp($a->customfs, $b->customfs);
					case "RAND()":
						return rand(-1,1);
					default:
						return $b->relevance - $a->relevance;
				}

			}

			/**
			 * If the primary fields are not equal, then leave it as it is.
			 * The primary sorting already sorted the items out so return 0 - as equals
			 */
			return $a->primary_order - $b->primary_order;
		}

		/**
		 * usort() custom function, sort by ID
		 *
		 * @param $obj_a
		 * @param $obj_b
		 *
		 * @return mixed
		 */
		protected function compare_posts( $obj_a, $obj_b ) {
			return $obj_a->id - $obj_b->id;
		}

		/**
		 * usort() custom function, sort by priority > relevance > date > title
		 *
		 * @param $a
		 * @param $b
		 *
		 * @return int
		 */
		protected function compare_by_rp( $a, $b ) {
			if ( $a->priority === $b->priority ) {
				if ( $a->relevance === $b->relevance ) {
					if ( $a->date != null && $a->date != "" ) {
						return strtotime( $b->date ) - strtotime( $a->date );
					} else {
						return strcasecmp( $a->title, $b->title );
					}
				} else {
					return $b->relevance - $a->relevance;
				}
			}

			return $b->priority - $a->priority;
		}

		/**
		 * usort() custom function, sort by priority > date ascending
		 *
		 * @param $a
		 * @param $b
		 *
		 * @return int
		 */
		protected function compare_by_rd_asc( $a, $b ) {
			if ( $a->priority === $b->priority ) {
				return strtotime( $a->date ) - strtotime( $b->date );
			}

			return $b->priority - $a->priority;
		}

		/**
		 * usort() custom function, sort by priority > date descending
		 *
		 * @param $a
		 * @param $b
		 *
		 * @return int
		 */
		protected function compare_by_rd_desc( $a, $b ) {
			if ( $a->priority === $b->priority ) {
				return strtotime( $b->date ) - strtotime( $a->date );
			}

			return $b->priority - $a->priority;
		}

		/**
		 * usort() custom function, sort by title descending
		 *
		 * @param $a
		 * @param $b
		 *
		 * @return int
		 */
		protected function compare_by_title_desc( $a, $b ) {
			return strcasecmp( $b->title, $a->title );
		}

		/**
		 * usort() custom function, sort by title ascending
		 *
		 * @param $a
		 * @param $b
		 *
		 * @return int
		 */
		protected function compare_by_title_asc( $a, $b ) {
			return strcasecmp( $a->title, $b->title );
		}


		/**
		 * Builds the query from the parts
		 *
		 * @param $parts
		 *
		 * @return string query
		 */
		protected function build_query( $parts ) {
			$args = &$this->args;
			$kw_logic = str_replace('EX', '', strtoupper( $args['keyword_logic'] ) );
			$kw_logic = $kw_logic != 'AND' && $kw_logic != 'OR' ? 'AND' : $kw_logic;

			$r_parts = array(); // relevance parts

			/*------------------------- Build like --------------------------*/
			$exact_query = '';
			$like_query_arr = array();
			foreach ( $parts as $k=>$part ) {
				if ( isset($part[0]) && count($part[0]) > 0 ) {
					if ( $k == 0 )
						$exact_query = '(' . implode(' OR ', $part[0]) . ')';
					else
						$like_query_arr[] = '(' . implode(' OR ', $part[0]) . ')';
				}
			}
			$like_query = implode(' ' . $kw_logic . ' ', $like_query_arr);

			// When $exact query is empty, then surely $like_query must be empty too, see above
			if ( $exact_query == '' ) {
				$like_query = "(1)";
			} else {
				// Both $like_query and $exact_query set
				if ( $like_query != '' ) {
					$like_query = "( $exact_query OR $like_query )";
				} else {
					$like_query = "( $exact_query )";
				}
			}
			/*---------------------------------------------------------------*/

			/*---------------------- Build relevance ------------------------*/
			foreach ( $parts as $part ) {
				if ( isset($part[1]) && count($part[1]) > 0 )
					$r_parts = array_merge( $r_parts, $part[1] );
			}
			$relevance = implode( ' + ', $r_parts );
			if ( $args['_post_use_relevance'] != 1 || $relevance == "" ) {
				$relevance = "(1)";
			} else {
				$relevance = "($relevance)";
			}
			/*---------------------------------------------------------------*/

			if ( isset($this->remaining_limit) ) {
				if ($this->limit_start != 0)
					$limit = $this->limit_start . ", " . $this->remaining_limit;
				else
					$limit = $this->remaining_limit;
			} else {
				$limit = 10;
			}

			return str_replace(
				array( "{relevance_query}", "{like_query}", "{remaining_limit}" ),
				array( $relevance, $like_query, $limit ),
				$this->query
			);

		}

		/**
		 * Post-processes the results
		 *
		 * @return array of results
		 */
		protected function post_process() {

			$pageposts  = is_array( $this->results ) ? $this->results : array();
			$s          = $this->s;
			$_s         = $this->_s;
			$args = $this->args;

			do_action("asl_start_post_processing");

			// No post processing if the search data param is missing or explicitly set
			if ( !isset($args['_sd']) || $args['_no_post_process'] ) {
				$this->results = $pageposts;
				return $pageposts;
			}

			$sd = $args['_sd'];
			$searchId = $args['_sid'];
			$com_options = wd_asl()->o['asl_compatibility'];

			/*--------------------- For Image Parser -----------------------*/
			// Do not select the content field, if it is not used at all
			$get_content = $sd['showdescription'] != 1;

			// Do not select excerpt if its not used at all
			$get_excerpt = !(
				$sd['primary_titlefield'] == 1 ||
				$sd['secondary_titlefield'] == 1 ||
				$sd['primary_descriptionfield'] == 1 ||
				$sd['secondary_descriptionfield'] == 1
			);

			$image_settings = $sd['image_options'];
			foreach ( $pageposts as $k => &$r ) {
				// Attachment post-process uses this section, but it has a separate image parser, so skip that
				if ( $r->post_type == 'attachment' )
					continue;

				if ( isset( $args['_switch_on_preprocess'] ) && is_multisite() ) {
					switch_to_blog( $r->blogid );
				}

				if ( $image_settings['show_images'] != 0 &&
					'' !== ($im = $this->getBFIimage( $r, $get_content, $get_excerpt ))
				) {
					if ( $image_settings['image_cropping'] == 0 ) {
						$r->image = $im;
					} else {
						if ( strpos( $im, "mshots/v1" ) === false && strpos( $im, ".gif" ) === false ) {
							$bfi_params = array( 'width'  => $image_settings['image_width'],
								'height' => $image_settings['image_height'],
								'crop'   => true
							);
							$r->image = bfi_thumb( $im, $bfi_params );
						} else {
							$r->image = $im;
						}
					}
				}
			}
			if ( isset( $args['_switch_on_preprocess'] ) && is_multisite() ) {
				restore_current_blog();
			}
			/*---------------------------------------------------------------*/

			// For non-ajax searches, this is the cut off, no other post-processing required
			if ( !$args['_ajax_search'] ) {
				$this->results = $pageposts;
				return $pageposts;
			}

			// VC 4.6+ fix: Shortcodes are not loaded in ajax responses
			// class_exists() is mandatory, some PHP versions fail
			if ( class_exists("WPBMap") && method_exists("WPBMap", "addAllMappedShortcodes") )
				WPBMap::addAllMappedShortcodes();


			$this->deregisterShortcodes();

			global $sitepress;

			/* Images, title, desc */
			foreach ( $pageposts as $k => &$r ) {

				if ( isset( $args['_switch_on_preprocess'] ) && is_multisite() ) {
					switch_to_blog( $r->blogid );
				}

				// ---- URL FIX for WooCommerce product variations
				$wc_prod_var_o = null; // Reset for each loop
				if ( $r->post_type == 'product_variation' && class_exists( 'WC_Product_Variation' ) ) {
					$wc_prod_var_o = wc_get_product( $r->id );
					$r->link       = $wc_prod_var_o->get_permalink();
				} else {
					$r->link = get_permalink( $r->id );
				}
				// Filter it though WPML
				if ( $args['_wpml_lang'] != '') {
					if ( ICL_LANGUAGE_CODE != $args['_wpml_lang'] ) {
						$r->link = apply_filters('wpml_permalink', $r->link, $args['_wpml_lang'], true);
					}
				} else if ( is_object($sitepress) && method_exists($sitepress, 'get_default_language') ) {
					$l = apply_filters( 'wpml_post_language_details', null,  $r->id );
					if ( is_array($l) && isset($l['language_code']) ) {
						$_lang = ICL_LANGUAGE_CODE;
						do_action( 'wpml_switch_language', $l['language_code'] );;
						$r->link = get_permalink($r->id);
						$r->link = apply_filters('wpml_permalink', $r->link, $l['language_code'], true);
						do_action( 'wpml_switch_language', $_lang );
					}
				}

				$_t_keys = array('primary_titlefield', 'secondary_titlefield');
				foreach ($_t_keys as $tk) {
					$sd[$tk] = $sd[$tk] . ''; // Convert for the switch statement
					if ( $sd[$tk] == '-1' ) continue;

					switch($sd[$tk]) {
						case '0':
							$r->title = get_the_title($r->id);
							break;
						case '1':
							if ( ASL_mb::strlen( $r->excerpt ) >= 200 ) {
								$r->title = wd_substr_at_word( $r->excerpt, 200 );
							} else {
								$r->title = $r->excerpt;
							}
							break;
						case 'c__f':
							if ( $sd[$tk.'_cf'] == '' ) {
								$r->title = get_the_title( $r->id );
							} else {
								$field_val = ASL_Helpers::getCFValue($sd[$tk.'_cf'], $r, $com_options['use_acf_getfield'], $args);
								if ( $field_val != '' ) {
									$r->title = wd_strip_tags_ws( $field_val, $sd['striptagsexclude'] );
								} else {
									$r->title = get_the_title($r->id);
								}
							}
							break;
						default:
							$r->title = get_the_title($r->id);
					}

					if ($r->title != "") break;
				}

				if ( !empty($sd['advtitlefield']) )
					$r->title = $this->adv_field(
						array(
							'main_field_slug' => 'titlefield',
							'main_field_value'=> $r->title,
							'r' => $r,
							'field_pattern' => stripslashes( $sd['advtitlefield'] )
						),
						$com_options['use_acf_getfield']
					);

				if ( ! isset( $sd['striptagsexclude'] ) ) {
					$sd['striptagsexclude'] = "<a><span>";
				}

				$_t_keys = array('primary_descriptionfield', 'secondary_descriptionfield');
				$_content = '';
				foreach ($_t_keys as $tk) {
					$sd[$tk] = $sd[$tk] . ''; // Convert for the switch statement
					if ( $sd[$tk] == '-1' ) continue;

					switch ($sd[$tk]) {
						case '1':
							if ( function_exists( 'qtranxf_use' ) && $args['_qtranslate_lang'] != "" ) {
								$r->excerpt = qtranxf_use($args['_qtranslate_lang'], $r->excerpt, false);
							}
							$_content = $r->excerpt;
							break;
						case '2':
							$_content = strip_tags( get_the_title( $r->id ), $sd['striptagsexclude'] );
							break;
						case 'c__f':
							if ( $sd[$tk.'_cf'] == '' ) {
								$_content = '';
							} else {
								$field_val = ASL_Helpers::getCFValue($sd[$tk.'_cf'], $r, $com_options['use_acf_getfield'], $args);
								if ( $field_val != '' ) {
									$_content = $field_val;
								} else {
									$_content = '';
								}
							}
							break;
						default: //including option '0', alias content
							if ( function_exists( 'qtranxf_use' ) && $args['_qtranslate_lang'] != "" ) {
								$r->content = qtranxf_use($args['_qtranslate_lang'], $r->content, false);
							}
							// For product variations, do something special
							if ( isset($wc_prod_var_o) ) {
								$r->content = $wc_prod_var_o->get_description();
								if ( $r->content == '') {
									$_pprod = wc_get_product($wc_prod_var_o->get_parent_id());
									$r->content = $_pprod->get_description();
								}
							}
							$_content = $r->content;
							break;
					}

					// Remove unneccessary gutemberg blocks
					$_content = ASL_Helpers::removeGutenbergBlocks($_content, array('core-embed/*'));

					// Deal with the shortcodes here, for more accuracy
					if ( $sd['shortcode_op'] == "remove" ) {
						if ( $_content != "" ) {
							// Remove shortcodes, keep the content, really fast and effective method
							$_content = preg_replace("~(?:\[/?)[^\]]+/?\]~su", '', $_content);
						}
					} else {
						if ( $_content != "" ) {
							$_content = apply_filters( 'the_content', $_content, $searchId );
						}
					}

					if ( $_content != '') break;
				}

				// Remove inline styles and scripts
				$_content = preg_replace( array(
					'#<script(.*?)>(.*?)</script>#is',
					'#<style(.*?)>(.*?)</style>#is'
				), '', $_content );

				$_content = wd_strip_tags_ws( $_content, $sd['striptagsexclude'] );

				// Get the words from around the search phrase, or just the description
				if ( $sd['description_context'] == 1 && count( $_s ) > 0 && $s != '') {
					// Try for an exact match
					$_ex_content = $this->context_find(
						$_content, $s,
						floor($sd['descriptionlength'] / 6),
						$sd['descriptionlength'],
						$sd['description_context_depth'],
						true
					);
					if ( $_ex_content === false ) {
						// No exact match, go with the first keyword
						$_content = $this->context_find(
							$_content, $_s[0],
							floor($sd['descriptionlength'] / 6),
							$sd['descriptionlength'],
							$sd['description_context_depth']
						);
					} else {
						$_content = $_ex_content;
					}
				} else if ( $_content != '' && (  ASL_mb::strlen( $_content ) > $sd['descriptionlength'] ) ) {
					$_content = wd_substr_at_word($_content, $sd['descriptionlength']) . "...";
				}

				$_content   = wd_closetags( $_content );

				if ( !empty($sd['advdescriptionfield']) )
					$r->content = $this->adv_field(
						array(
							'main_field_slug' => 'descriptionfield',
							'main_field_value'=> $_content,
							'r' => $r,
							'field_pattern' => stripslashes( $sd['advdescriptionfield'] )
						),
						$com_options['use_acf_getfield']
					);

				// --------------------------------- DATE -----------------------------------
				if ($sd["showdate"] == 1) {
					$post_time = strtotime($r->date);

					if ( $sd['custom_date'] == 1) {
						$date_format = w_isset_def($sd['custom_date_format'], "Y-m-d H:i:s");
					} else {
						$date_format = get_option('date_format', "Y-m-d") . " " . get_option('time_format', "H:i:s");
					}

					$r->date = @date_i18n($date_format, $post_time);
				}
				// --------------------------------------------------------------------------
			}

			if ( isset( $args['_switch_on_preprocess'] ) && is_multisite() ) {
				restore_current_blog();
			}

			$this->results = $pageposts;

			return $pageposts;

		}

		/**
		 * Fetches an image for BFI class
		 *
		 * @param $post StdClass post object
		 * @param $get_content bool to request the post content
		 * @param $get_excerpt bool to request the post excerpt
		 * @return string image URL
		 */
		protected function getBFIimage( $post, $get_content = false, $get_excerpt = false ) {
			if ( ! isset( $post->image ) || $post->image == null ) {
				$args = $this->args;
				if ( !isset($args['_sd']['image_options']) )
					return "";
				$sd = $args['_sd'];
				$image_settings = $args['_sd']['image_options'];
				$size = $image_settings['image_source_featured'] == "original" ? 'full' : $image_settings['image_source_featured'];

				if ( ! isset( $post->id ) ) {
					return "";
				}
				$im = "";
				for ( $i = 1; $i < 6; $i ++ ) {
					switch ( $image_settings[ 'image_source' . $i ] ) {
						case "featured":
							if ( $post->post_type == 'attachment' ) {
								$imx = wp_get_attachment_image_src($post->id, $size, false);
							} else {
								$imx = wp_get_attachment_image_src(
									get_post_thumbnail_id($post->id), $size, false
								);
							}
							if ( !is_wp_error($imx) && $imx !== false && isset($imx[0]) )
								$im = $imx[0];
							break;
						case "content":
							$content = $get_content ? get_post_field('post_content', $post->id) : $post->content;

							if ( $image_settings['apply_content_filter'] == 1 )
								$content = apply_filters('the_content', $content);

							$im = asl_get_image_from_content( $content, $sd['image_parser_image_number'], $sd['image_parser_exclude_filenames'] );
							break;
						case "excerpt":
							$excerpt = $get_excerpt ? get_post_field('post_excerpt', $post->id) : $post->excerpt;

							$im = asl_get_image_from_content( $excerpt, $sd['image_parser_image_number'], $sd['image_parser_exclude_filenames'] );
							break;
						case "screenshot":
							$im = 'https://s.wordpress.com/mshots/v1/' . urlencode( get_permalink( $post->id ) ) .
								'?w=' . $image_settings['image_width'] . '&h=' . $image_settings['image_height'];
							break;
						case "post_format":
							$format = get_post_format( $post->id );

							switch ($format) {
								case "audio":
									$im = ASL_URL_NP . "img/post_format/audio.png";
									break;
								case "video":
									$im = ASL_URL_NP . "img/post_format/video.png";
									break;
								case "quote":
									$im = ASL_URL_NP . "img/post_format/quote.png";
									break;
								case "image":
									$im = ASL_URL_NP . "img/post_format/image.png";
									break;
								case "gallery":
									$im = ASL_URL_NP . "img/post_format/gallery.png";
									break;
								case "link":
									$im = ASL_URL_NP . "img/post_format/link.png";
									break;
								default:
									$im = ASL_URL_NP . "img/post_format/default.png";
									break;
							}
							break;
						case "custom":
							if ( $image_settings['image_custom_field'] != "" ) {
								$val = get_post_meta( $post->id, $image_settings['image_custom_field'], true );
								if ( is_array($val) && !empty($val) ) {
									$val = reset($val);
								}
								if ( $val != null && $val != "" ) {
									if ( is_numeric($val) ) {
										$im = wp_get_attachment_image_url( $val, $size );
									} else {
										$im = $val;
									}
								}
							}
							break;
						case "default":
							if ( $image_settings['image_default'] != "" ) {
								$im = $image_settings['image_default'];
							}
							break;
						default:
							$im = "";
							break;
					}
					if ( $im != null && $im != '' ) {
						break;
					}
				}
				if ( !is_wp_error($im) )
					return ASL_Helpers::fixSSLURLs($im);
				return '';
			} else {
				return ASL_Helpers::fixSSLURLs($post->image);
			}
		}


		/**
		 * Generates the final field, based on the advanced field pattern
		 *
		 * @uses ASL_Helpers::getCFValue()
		 * @uses ASL_Helpers::fixSSLURLs()
		 * @uses shortcode_parse_atts()
		 *
		 * @param array     $f_args             Field related arguments
		 * @param boolean   $use_acf            If true, uses ACF get_field() function to get the meta
		 * @param boolean   $empty_on_missing   If true, returns an empty string if any of the fields is empty.
		 *
		 * @return string Final post title
		 */
		protected function adv_field( $f_args, $use_acf = false, $empty_on_missing = false ) {
			$args = &$this->args;

			$specials = array('__id', '__title', '__content', '__link', '__url', '__image', '__date', '__author');

			$f_args = wp_parse_args($f_args, array(
				'main_field_slug' => 'titlefield',  // The 'slug', aka the original field name
				'main_field_value'=> '',            // The default field value
				'r' => null,                        // Result object
				'field_pattern' => '{titlefield}'   // The field pattern
			));
			$_f_args = $f_args;

			if ( $f_args['field_pattern'] == '' ) {
				return $f_args['field_value'];
			}
			$field_pattern = $f_args['field_pattern']; // Lets not make changes to arguments, shall we.

			// Find conditional patterns, like [prefix {field} suffix}
			preg_match_all( "/(\[.*?\])/", $field_pattern, $matches );
			if ( isset( $matches[0] ) && isset( $matches[1] ) && is_array( $matches[1] ) ) {
				foreach ( $matches[1] as $fieldset ) {
					// Pass on each section to this function again, the code will never get here
					$_f_args['field_pattern'] = str_replace(array('[', ']'), '', $fieldset);
					$processed_fieldset = $this->adv_field(
						$_f_args,
						$use_acf,
						true
					);
					// Replace the original with the processed version, first occurrence, in case of duplicates
					$field_pattern = ASL_Helpers::replaceFirst($fieldset, $processed_fieldset, $field_pattern);
				}
			}

			preg_match_all( "/{(.*?)}/", $field_pattern, $matches );
			if ( isset( $matches[0] ) && isset( $matches[1] ) && is_array( $matches[1] ) ) {
				foreach ( $matches[1] as $complete_field ) {
					$field_args = shortcode_parse_atts($complete_field);
					if ( is_array($field_args) && isset($field_args[0]) ) {
						$field = array_shift($field_args);
					} else {
						continue;
					}
					if ( $field == $f_args['main_field_slug'] ) {
						$val = $f_args['main_field_value'];
						if ( isset($field_args['maxlength']) ) {
							$val = wd_substr_at_word($val, $field_args['maxlength']);
						}
						// value, field name, post object, field arguments
						$val = apply_filters('asl_cpt_advanced_field_value', $val, $field, $f_args['r'], $f_args);
						$field_pattern = str_replace( '{'.$complete_field.'}', $val, $field_pattern );
					} else {
						if ( in_array($field, $specials) ) {
							$r = $f_args['r'];
							$val = '';
							switch ($field) {
								case '__id':
									$val = isset($r->id) ? $r->id : '';
									break;
								case '__title':
									$val = isset($r->title) ? $r->title : '';
									break;
								case '__content':
									$val = isset($r->content) ? $r->content : '';
									break;
								case '__link':
								case '__url':
									$val = isset($r->link) ? $r->link : '';
									break;
								case '__image':
									$val = isset($r->image) ? $r->image : '';
									break;
								case '__date':
									if ( isset($r->date) ) {
										if ( isset($field_args['date_format']) ) {
											$val = date_i18n($field_args['date_format'], strtotime($r->date));
										} else {
											$val = $r->date;
										}
									}
									break;
								case '__author':
									$val = isset($r->author) && $r->author != '' ? $r->author : get_the_author_meta( 'display_name' , get_post_field ('post_author', $r->id));
									break;
							}
						} else if(strpos($field, '_taxonomy_') === 0 || strpos($field, '__tax_') === 0) {
							$taxonomy = str_replace(array('_taxonomy_', '__tax_'), '', $field);
							if ($taxonomy == '')
								continue;
							$count = isset($field_args['count']) ? $field_args['count'] + 0 : 5;
							$count = $count == 0 ? 5 : $count;
							$separator = isset($field_args['separator']) ? $field_args['separator']: ', ';
							$orderby = isset($field_args['orderby']) ? $field_args['orderby']: 'name';
							$order = isset($field_args['order']) ? $field_args['order']: 'ASC';
							$exclude = isset($field_args['exclude']) ? '1, '.$field_args['exclude']: '1';
							$hide_empty = isset($field_args['hide_empty']) ? $field_args['hide_empty'] == 1 : true;
							$hierarchical = isset($field_args['hierarchical']) ? $field_args['hierarchical'] == 1 : true;
							$childless = isset($field_args['childless']) ? $field_args['childless'] == 1 : false;
							$val = ASL_Helpers::getTermsList($taxonomy, $count, $separator, array(
								'orderby'   => $orderby,
								'order'     => $order,
								'object_ids'=> $f_args['r']->id,
								'exclude'   => $exclude,
								'hide_empty'=> $hide_empty,
								'hierarchical' => $hierarchical,
								'childless' => $childless
							));
						} else if ( strpos($field, '_pods_') === 0 || strpos($field, '_pod_') === 0 ) {
							// PODs field
							$val = ASL_Helpers::getPODsValue($field, $f_args['r']);
						} else if ( strpos($field, '__um_') === 0  ) {
							// User Meta Field
							$um_field = str_replace('__um_', '', $field);
							$author = get_post_field( 'post_author', $f_args['r']->id );
							$val = ASL_Helpers::getUserCFValue($um_field, $author, $use_acf);
						}else {
							// Probably a custom field?
							$val = ASL_Helpers::getCFValue($field, $f_args['r'], $use_acf, $args, $field_args);
						}
						// For the recursive call to break, if any of the fields is empty
						if ( $empty_on_missing && trim($val) == '')
							return '';
						$val = ASL_Helpers::fixSSLURLs($val);

						if ( isset($field_args['maxlength']) ) {
							$val = wd_substr_at_word($val, $field_args['maxlength']);
						}

						// value, field name, post object, field arguments
						$val = apply_filters('asl_cpt_advanced_field_value', $val, $field, $f_args['r'], $f_args);
						$field_pattern = str_replace( '{' . $complete_field . '}', $val, $field_pattern );
					}
				}
			}

			return $field_pattern;
		}

		public function deregisterShortcodes() {
			// Remove shortcodes with contents
			$ignore_shortcodes = array(
				"wpdreams_ajaxsearchpro", "wd_asp", "wd_asl", "wpdreams_ajaxsearchlite",
				"embed"
			);
			foreach ( $ignore_shortcodes as $shortcode ) {
				remove_shortcode( $shortcode );
				add_shortcode( $shortcode, array( $this, 'return_empty_string' ) );
			}
		}

		/**
		 * An empty function to override individual shortcodes, this must be public
		 *
		 * @return string
		 */
		public function return_empty_string() {
			return "";
		}
	}
}