PageSourceSearch

https://republicpropertytax.com/wp-content/themes/wpresidence/js/ajaxcalls.js?ver=1791147150

js republicpropertytax.com collected 2026-10-04 21:11:05 UTC 109,790 bytes, 2,932 lines download raw bytes

1/*global $, jQuery, ajaxcalls_vars, document, control_vars, window, control_vars,Modernizr,mapfunctions_vars,wpestate_open_menu,grecaptcha,wpestate_show_no_results,wpestate_load_on_demand_pins,get_custom_value,wpestate_enable_half_map_pin_action,wpestate_lazy_load_carousel_property_unit,Chart,widgetId1,widgetId2,widgetId3,widgetId4,wpestate_get_custom_value_tab_search*/
2///////////////////////////////////////////////////////////////////////////////////////////
3
4/**
5 * Safe redirect helper — mitigates XSS-5 (Open Redirect via AJAX Response).
6 *
7 * PayPal-related AJAX handlers return a URL string that the client uses to
8 * redirect the user to the payment gateway. A manipulated / compromised
9 * response could supply an attacker-controlled URL (phishing, malware).
10 *
11 * This helper validates the returned URL before assigning it to
12 * `window.location.href`. Only http(s) URLs that are either same-origin
13 * or on the paypal.com domain family (including sandbox) are allowed.
14 * Anything else (javascript:, data:, other hosts) is silently dropped.
15 *
16 * @param {string} data URL string returned from the AJAX endpoint.
17 */
18function wpestate_safe_redirect(data) {
19    "use strict";
20
21    // Reject non-string / empty payloads outright.
22    if (typeof data !== 'string' || data === '') {
23        return;
24    }
25
26    try {
27        // Parse relative to current origin so bare paths ("/checkout") still resolve.
28        var url = new URL(data, window.location.origin);
29
30        // Block dangerous schemes such as javascript:, data:, file:, vbscript:, etc.
31        if (url.protocol !== 'http:' && url.protocol !== 'https:') {
32            return;
33        }
34
35        var host = url.hostname.toLowerCase();
36
37        // Same-origin is always safe (theme-hosted redirect targets).
38        var isSameOrigin = (url.origin === window.location.origin);
39
40        // Whitelist PayPal production + sandbox hostnames.
41        // Regex covers paypal.com and any subdomain (www, sandbox, checkout, etc.).
42        var isPaypal = (host === 'paypal.com' || host === 'www.paypal.com' ||
43                        host === 'sandbox.paypal.com' || host === 'www.sandbox.paypal.com' ||
44                        /(^|\.)paypal\.com$/.test(host));
45
46        if (isSameOrigin || isPaypal) {
47            // URL is trusted — perform the redirect using the normalized href.
48            window.location.href = url.href;
49        }
50        // Fallthrough: untrusted host — silently ignore (fail closed).
51    } catch (e) {
52        // Malformed URL — ignore; do not redirect.
53    }
54}
55
56function wpestate_load_stats_tabs(listing_id) {
57    "use strict";
58    var ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
59    var nonce = jQuery('#wpestate_tab_stats').val();
60    jQuery.ajax({
61        type: 'POST',
62        url: ajaxurl,
63        dataType: 'json',
64        data: {
65            'action': 'wpestate_load_stats_property',
66            'postid': listing_id,
67            'security': nonce,
68        },
69        success: function (data) {
70            wpestate_show_prop_stat_graph_tab(data.array_values, data.labels, listing_id);
71        },
72        error: function (errorThrown) {}
73    });//end ajax
74}
75
76
77
78function wpestate_show_prop_stat_graph_tab(values, labels, listing_id) {
79    if (!document.getElementById('myChart')) {
80        return;
81    }
82
83    var ctx = jQuery("#myChart").get(0).getContext("2d");
84    var myNewChart = new Chart(ctx);
85    // var labels      =   '';
86    var traffic_data = '  ';
87
88    // labels          =   jQuery.parseJSON ( wpestate_property_vars.singular_label);
89    traffic_data = values;
90
91    var data = {
92        labels: labels,
93        datasets: [
94            {
95                label: ajaxcalls_vars.property_views,
96                fillColor: "rgba(220,220,220,0.5)",
97                strokeColor: "rgba(220,220,220,0.8)",
98                highlightFill: "rgba(220,220,220,0.75)",
99                highlightStroke: "rgba(220,220,220,1)",
100                data: traffic_data
101            },
102        ]
103    };
104
105    var options = {
106        title: 'page views',
107        //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
108        scaleBeginAtZero: true,
109
110        //Boolean - Whether grid lines are shown across the chart
111        scaleShowGridLines: true,
112
113        //String - Colour of the grid lines
114        scaleGridLineColor: "rgba(0,0,0,.05)",
115
116        //Number - Width of the grid lines
117        scaleGridLineWidth: 1,
118
119        //Boolean - Whether to show horizontal lines (except X axis)
120        scaleShowHorizontalLines: true,
121
122        //Boolean - Whether to show vertical lines (except Y axis)
123        scaleShowVerticalLines: true,
124
125        //Boolean - If there is a stroke on each bar
126        barShowStroke: true,
127
128        //Number - Pixel width of the bar stroke
129        barStrokeWidth: 2,
130
131        //Number - Spacing between each of the X value sets
132        barValueSpacing: 5,
133
134        //Number - Spacing between data sets within X values
135        barDatasetSpacing: 1,
136
137        //String - A legend template
138        legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
139
140    };
141
142    // var myBarChart = new Chart(ctx).Bar(data, options);
143    var myBarChart = new Chart(ctx, {
144        type: 'bar',
145        data: data,
146        options: options
147    });
148}
149
150/**
151 * Remaps half-map listing card column sizes for list-mode rendering.
152 *
153 * @param {string} markup - HTML string containing listing_wrapper elements.
154 * @returns {string} Updated markup with transformed column classes and data-org values.
155 */
156function wpestate_half_map_remap_list_columns(markup) {
157
158
159
160     if (typeof ajaxcalls_vars !== 'undefined' && ajaxcalls_vars.wp_estate_prop_unit === 'list') {
161
162        return markup;
163    }
164
165    if (typeof markup !== 'string' || markup === '') {
166        return markup;
167    }
168
169    var tempWrapper = jQuery('<div/>').html(markup);
170
171    tempWrapper.find('.listing_wrapper').each(function () {
172        var wrapper = jQuery(this);
173        var originalClasses = wrapper.attr('class') || '';
174     
175        var mdMatch = originalClasses.match(/col-md-(\d+)/);
176        var originalOrg = wrapper.attr('data-org') || (mdMatch ? mdMatch[1] : '') || '6';
177
178        // Persist the original column sizing so grid view can be restored later.
179        var lgMatch = originalClasses.match(/col-lg-(\d+)/);
180        var smMatch = originalClasses.match(/col-sm-(\d+)/);
181
182        wrapper.attr('data-half-map-original-md-class', mdMatch ? mdMatch[0] : '');
183        wrapper.attr('data-half-map-original-lg-class', lgMatch ? lgMatch[0] : '');
184        wrapper.attr('data-half-map-original-sm-class', smMatch ? smMatch[0] : '');
185        wrapper.attr('data-half-map-original-org', originalOrg);
186        var targetOrg = originalOrg;
187
188        switch (originalOrg) {
189        case '6':
190            targetOrg = '3';
191            break;
192        case '4':
193        case '3':
194            targetOrg = '2';
195            break;
196        case '12':
197            targetOrg = '12';
198            break;
199        default:
200            targetOrg = originalOrg;
201        }
202
203        ['md', 'lg', 'sm'].forEach(function (breakpoint) {
204            var regex = new RegExp('col-' + breakpoint + '-\\d+', 'g');
205            var matches = (wrapper.attr('class') || '').match(regex);
206
207            if (matches) {
208                wrapper.removeClass(matches.join(' '));
209            }
210
211            wrapper.addClass('col-' + breakpoint + '-' + targetOrg);
212        });
213
214        wrapper.attr('data-org', targetOrg);
215    });
216
217    return tempWrapper.html();
218}
219
220
221
222function wpestate_load_stats(listing_id) {
223    "use strict";
224    var ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
225    var nonce = jQuery('#wpestate_tab_stats').val();
226
227    jQuery.ajax({
228        type: 'POST',
229        url: ajaxurl,
230        dataType: 'json',
231        data: {
232            'action': 'wpestate_load_stats_property',
233            'postid': listing_id,
234            'security': nonce,
235        },
236        success: function (data) {
237            wpestate_show_prop_stat_graph(data.array_values, data.labels, listing_id);
238        },
239        error: function (errorThrown) {}
240    });//end ajax
241}
242
243function wpestate_show_prop_stat_graph(values, labels, listing_id) {
244    "use strict";
245
246    var ctx = jQuery("#myChart_" + listing_id).get(0).getContext('2d');
247    var myNewChart = new Chart(ctx);
248    var data = {
249        labels: labels,
250        datasets: [
251            {
252                label: ajaxcalls_vars.property_views,
253                fillColor: "rgba(220,220,220,0.5)",
254                strokeColor: "rgba(220,220,220,0.8)",
255                highlightFill: "rgba(220,220,220,0.75)",
256                highlightStroke: "rgba(220,220,220,1)",
257                data: values
258            },
259        ]
260    };
261
262    var options = {
263        scaleBeginAtZero: true,
264        scaleShowGridLines: true,
265        scaleGridLineColor: "rgba(0,0,0,.05)",
266        scaleGridLineWidth: 1,
267        scaleShowHorizontalLines: true,
268        scaleShowVerticalLines: true,
269        barShowStroke: true,
270        barStrokeWidth: 2,
271        barValueSpacing: 5,
272        barDatasetSpacing: 1,
273        legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
274    };
275
276    // var myBarChart = new Chart(ctx).Bar(data, options);
277    var myBarChart = new Chart(ctx, {
278        type: 'bar',
279        data: data,
280        options: options
281    });
282}
283
284
285/**
286 * Run standard AJAX filtering for the classic search wrapper.
287 *
288 * Builds the request payload from the current search UI state and sends it to
289 * `wpestate_ajax_filter_listings_search`. Taxonomy filters are sent by their
290 * taxonomy keys and term-id values plus meta_query clauses prepared by
291 * `wpestate_prepare_custom_search_ajax_data`.
292 *
293 * @param {number} newpage Target results page for pagination.
294 * @returns {void}
295 */
296function wpestate_start_filtering_ajax(newpage) {
297    "use strict";
298
299
300
301    // Step 1: Read scalar filters and request defaults.
302    var action = '';
303    var category = '';
304    var city = '';
305    var area = '';
306    var county_state = '';
307    var status = '';
308    var min_price = parseFloat(jQuery('#price_low').val(), 10);
309    var price_max = parseFloat(jQuery('#price_max').val(), 10);
310    var ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
311    var postid = 1;
312    var halfmap = 0;
313    var all_checkers = [];
314    var meta_query = [];
315    var order = jQuery('#a_filter_order').attr('data-value');
316    var nonce = jQuery('#wpestate_ajax_filtering').val();
317    var search_wrapper = jQuery('.search_wrapper');
318
319    // Step 2: Resolve search wrapper post id and half-map mode.
320    if (document.getElementById('search_wrapper')) {
321        postid = parseInt(jQuery('#search_wrapper').attr('data-postid'), 10);
322    }
323    if (jQuery('#google_map_prop_list_sidebar').length) {
324        halfmap = 1;
325    }
326
327    // Step 3: Collect shared features, geo and beds/baths values.
328    all_checkers = wpestate_collect_property_features_values(search_wrapper);
329
330    var geoValues = wpestate_collect_geo_values();
331    var geo_lat = geoValues.geo_lat;
332    var geo_long = geoValues.geo_long;
333    var geo_rad = geoValues.geo_rad;
334
335    var bedsBathsValues = wpestate_collect_beds_baths_component_values();
336    var componentsbeds = bedsBathsValues.componentsbeds;
337    var componentsbaths = bedsBathsValues.componentsbaths;
338
339    // Step 4: Build taxonomy/meta payload using the shared custom-search resolver.
340    var helperAjaxData = {};
341    var helperMetaQuery = wpestate_prepare_custom_search_ajax_data(
342        search_wrapper,
343        helperAjaxData,
344        componentsbeds,
345        componentsbaths
346    );
347
348    action = helperAjaxData.property_action_category || '';
349    category = helperAjaxData.property_category || '';
350    city = helperAjaxData.property_city || '';
351    area = helperAjaxData.property_area || '';
352    county_state = helperAjaxData.property_county_state || '';
353    status = helperAjaxData.property_status || '';
354    meta_query = jQuery.isArray(helperMetaQuery) ? helperMetaQuery : [];
355
356    // Step 5: Reset container and show loader before requesting new results.
357    jQuery('#listing_ajax_container').empty();
358    jQuery('#listing_loader').show();
359
360    // Step 6: Send AJAX request with taxonomy and meta query filters.
361    jQuery.ajax({
362        type: 'POST',
363        url: ajaxurl,
364        dataType: 'json',
365        data: {
366            'action': 'wpestate_ajax_filter_listings_search',
367            'property_action_category': action,
368            'property_category': category,
369            'property_city': city,
370            'property_area': area,
371            'property_county_state': county_state,
372            'property_status': status,
373            'price_min': min_price,
374            'price_max': price_max,         
375            'meta_query': meta_query,
376            'newpage': newpage,
377            'postid': postid,
378            'halfmap': halfmap,
379            'property_features': all_checkers,
380            'geo_lat': geo_lat,
381            'geo_long': geo_long,
382            'geo_rad': geo_rad,
383            'order': order,
384            'security': nonce
385
386             
387        },
388        // Step 7: Update UI with returned cards and rebind JS behaviors.
389        success: function (data) {
390
391            jQuery('#listing_loader').hide();
392            jQuery('.listing_loader_title').show();
393            jQuery('.pagination_nojax').hide();
394            //jQuery('#listing_ajax_container').empty().append(data);
395            jQuery('.entry-title.title_prop').addClass('half_results').text(data.no_founs);
396
397            jQuery('#listing_ajax_container').empty().append(data.cards);
398
399
400            wpestate_restart_js_after_ajax();
401
402        },
403        // Step 8: Keep default silent error handler behavior.
404        error: function (errorThrown) {}
405    });//end ajax
406}
407
408//////////////////////////////////////////////////////////////////////////////////////////////
409/// ajax filtering on header search ; jslint checked
410////////////////////////////////////////////////////////////////////////////////////////////
411
412
413/**
414 * Collect taxonomy values from multi-select controls in active scope.
415 *
416 * Reads taxonomy name from select/option data attributes, normalizes known
417 * aliases, then stores selected term ids (or values) as arrays on ajaxData.
418 *
419 * @param {jQuery} activeTabScope Scope used for tab-aware search extraction.
420 * @param {Object} ajaxData AJAX payload object to enrich.
421 * @returns {void}
422 */
423function wpestate_collect_multiselect_taxonomies(activeTabScope, ajaxData) {
424    "use strict";
425
426    // Step 1: Alias map for non-canonical taxonomy names from markup.
427    var taxonomyAliasMap = {
428        'cities': 'property_city',
429        'city': 'property_city',
430        'areas': 'property_area',
431        'area': 'property_area',
432        'countystate': 'property_county_state',
433        'county_state': 'property_county_state',
434        'county / state': 'property_county_state',
435        'types': 'property_action_category',
436        'actions': 'property_action_category',
437        'categories': 'property_category',
438        'status': 'property_status',
439        'features': 'property_features'
440    };
441
442    // Step 2: Process each multi-select field in the active scope.
443    activeTabScope.find('select[multiple]').each(function () {
444        var selectElement = jQuery(this);
445        var taxonomyName = '';
446        var optionValues = [];
447
448        // Step 2.1: Resolve taxonomy from select/selected-option metadata.
449        taxonomyName = (selectElement.attr('data-search-taxonomy') || '').trim();
450        if (taxonomyName === '') {
451            var firstSelectedOption = selectElement.find('option:selected').first();
452            if (firstSelectedOption.length > 0) {
453                taxonomyName = (
454                    firstSelectedOption.attr('data-search-taxonomy') ||
455                    firstSelectedOption.attr('data-taxonomy') ||
456                    ''
457                ).trim();
458            }
459        }
460
461        // Step 2.2: Normalize key format and map aliases to canonical keys.
462        taxonomyName = taxonomyName.toLowerCase();
463        taxonomyName = taxonomyName.replace(/\[\]$/g, '').trim();
464        if (taxonomyAliasMap[taxonomyName]) {
465            taxonomyName = taxonomyAliasMap[taxonomyName];
466        }
467        if (taxonomyName === '') {
468            return;
469        }
470
471        // Step 2.3: Collect selected option values (prefer term ids).
472        selectElement.find('option:selected').each(function () {
473            var selectedOption = jQuery(this);
474            var optionTermId = wpestate_get_term_id_from_element
474(selectedOption);
475            var optionValue = selectedOption.val() || '';
476            var resolvedValue = (optionTermId !== '') ? optionTermId : optionValue;
477
478            if (resolvedValue !== '' && String(resolvedValue).toLowerCase() !== 'all') {
479                optionValues.push(resolvedValue);
480            }
481        });
482
483        // Step 2.4: Store unique values under the resolved taxonomy key.
484        if (optionValues.length > 0) {
485            optionValues = optionValues.filter(function (value, index, array) {
486                return array.indexOf(value) === index;
487            });
488            ajaxData[taxonomyName] = optionValues;
489        }
490    });
491}
492
493/**
494 * Start advanced custom AJAX filtering for listings.
495 *
496 * This function collects active search values from `.search_wrapper`,
497 * normalizes taxonomy/meta inputs (including term-id aware fields),
498 * and sends the payload to `wpestate_custom_adv_ajax_filter_listings_search`.
499 *
500 * @param {number|string} newpage Requested pagination page.
501 * @returns {void}
502 */
503function wpestate_custom_search_start_filtering_ajax(newpage) {
504    "use strict";
505
506    // Step 1: search_wrapper is the form container.
507    var search_wrapper = jQuery('.search_wrapper');
508    var active_tab_scope = search_wrapper.find('.tab-pane.active').first();
509
510    // Step 1.1: When tabs exist, collect field values only from the active tab.
511    // This prevents hidden fields from inactive tabs from leaking into the request.
512    if (active_tab_scope.length === 0) {
513        active_tab_scope = search_wrapper;
514    }
515
516    // Step 2: active tab term id.
517    var term_id = jQuery('.tab-pane.active .term_id_class').val();
518
519    // Step 3: Define payload variables that will be populated from the UI.
520    var ajaxurl = ajaxcalls_vars.wpestate_ajax;
521  
522    var postid = '';
523    var slider_min = '';
524    var slider_max = '';
525    var halfmap = 0;
526    var property_features_values = [];
527    var filter_search_action10 = '';
528    var adv_location10 = '';
529    var filter_search_action11 = '';
530    var filter_search_categ11 = '';
531    var geo_lat = '';
532    var geo_long = '';
533    var geo_rad = '';
534    var componentsbeds = '';
535    var componentsbaths = '';
536    var location = '';
537    var term_counter = '';
538
539    // Step 3.1: If body has data-postid, use it as ajax postid.
540    var bodyPostId = jQuery('body').attr('data-postid');
541    if (typeof bodyPostId === 'undefined' || bodyPostId === null || bodyPostId === '') {
542        bodyPostId = jQuery('body').data('postid');
543    }
544    if (typeof bodyPostId !== 'undefined' && bodyPostId !== null && bodyPostId !== '') {
545        postid = bodyPostId;
546    }
547
548    // Step 4: Read slider values via dedicated collector.
549    var sliderValues = wpestate_collect_slider_values();
550    slider_min = sliderValues.slider_min;
551    slider_max = sliderValues.slider_max;
552
553    // Step 5: Keep tab term id synced for tabbed search layouts.
554    if ((mapfunctions_vars.adv_search_type === '6' || mapfunctions_vars.adv_search_type === '7')) { // &&  !jQuery('.halfsearch')[0]
555        term_id = jQuery('.tab-pane.active .term_id_class').val();
556    }
557
558    // Step 6: Read selected feature term ids via dedicated collector.
559    property_features_values = wpestate_collect_property_features_values(search_wrapper);
560
561    // Step 7: Read geolocation values via dedicated collector.
562    var geoValues = wpestate_collect_geo_values();
563    geo_lat = geoValues.geo_lat;
564    geo_long = geoValues.geo_long;
565    geo_rad = geoValues.geo_rad;
566
567    // Step 8: Read beds/baths component values via dedicated collector.
568    var bedsBathsValues = wpestate_collect_beds_baths_component_values();
569    componentsbeds = bedsBathsValues.componentsbeds;
570    componentsbaths = bedsBathsValues.componentsbaths;
571
572    // Step 8.1: Read the canonical location field from the active search scope.
573    location = active_tab_scope.find('input[name="location"]').first().val() || '';
574
575    // Step 9: Keep term counter linked to the active tab term id.
576    term_counter = term_id;
577
578    var nonce = jQuery('#wpestate_ajax_filtering').val();
579    var order = jQuery('#a_filter_order').attr('data-value');
580
581
582    const listingContainer = jQuery('#listing_ajax_container');
583    wpestate_createSkeletons(listingContainer);
584
585
586    //jQuery('#listing_ajax_container').empty();
587//    jQuery('#listing_loader').show(); 
588    var ajaxData = {
589            'action': 'wpestate_custom_adv_ajax_filter_listings_search',
590            'newpage': newpage,
591            'postid': postid,      
592            'halfmap': halfmap,
593            'property_features': property_features_values,
594            'filter_search_action10': filter_search_action10,
595            'location': location,
596            'adv_location10': adv_location10,
597            'filter_search_action11': filter_search_action11,
598            'filter_search_categ11': filter_search_categ11,
599            'order': order,
600            'term_counter': term_counter,
601            'componentsbeds'            :   componentsbeds,
602            'componentsbaths'           :   componentsbaths,
603            'security': nonce,
604            ///////////////////////////////////////////////////////////
605            'post_status': '',
606            'paged': '',
607            'posts_per_page': '',
608            'geo_lat': geo_lat,
609            'geo_long': geo_long,
610            'geo_rad': geo_rad,
611            'price_min': slider_min,
612            'price_max': slider_max,
613        };
614
615    // Step 10: Prepare taxonomy + post-meta ajax data from search controls.
616    var metaQueryPayload = wpestate_prepare_custom_search_ajax_data(
617        active_tab_scope,
618        ajaxData,
619        componentsbeds,
620        componentsbaths
621    );
622
623    // Step 11: Attach collected meta_query clauses to the AJAX payload.
624    ajaxData.meta_query = metaQueryPayload;
625
626    // Step 13: Fallback mapping for legacy templates missing `data-search-*`.
627    // Generic active-tab taxonomy fallback (no hardcoded field names).
628    var activeTabButton = search_wrapper.find('.adv_search_tab_item.active').first();
629    if (activeTabButton.length > 0) {
630        var activeTabTaxonomy = (activeTabButton.attr('data-tax') || '').trim();
631        var activeTabTermId = wpestate_get_term_id_from_element(activeTabButton);
632        var activeTabTermValue = (activeTabButton.attr('data-term') || '').trim();
633
634        if (activeTabTaxonomy !== '') {
635            if (activeTabTermId !== '') {
636                ajaxData[activeTabTaxonomy] = activeTabTermId;
637            } else if (activeTabTermValue !== '' && activeTabTermValue.toLowerCase() !== 'all') {
638                ajaxData[activeTabTaxonomy] = activeTabTermValue;
639            }
640
641            if (activeTabTaxonomy === 'property_action_category' && typeof ajaxData[activeTabTaxonomy] !== 'undefined') {
642                ajaxData.query_action = ajaxData[activeTabTaxonomy];
643            }
644        }
645    }
646
647    jQuery.ajax({
648        type: 'POST',
649        url: ajaxurl,
650        dataType: 'json',
651        data: ajaxData,
652        success: function (data) {
653
654    
655
656            jQuery('#listing_loader').hide();
657            jQuery('.listing_loader_title').show();
658            jQuery('.entry-title.title_prop').addClass('half_results').text(data.no_founs);
659
660            var cardsMarkup = data.cards;
661
662            // When half-map list view is active, remap the returned column sizes
663            // before injecting the new cards so they respect the list-mode sizing rules.
664            if (window.wpestateHalfMapState &&
665                typeof window.wpestateHalfMapState.getMode === 'function' &&
666                window.wpestateHalfMapState.getMode() === 'list') {
667                cardsMarkup = wpestate_half_map_remap_list_columns(cardsMarkup);
668            }
669
670            //  jQuery('#listing_ajax_container').empty().append(cardsMarkup);
671            wpestate_replaceSkeletons(listingContainer, cardsMarkup);
672
673            if (window.wpestateHalfMapState && typeof window.wpestateHalfMapState.applyCurrentLayout === 'function') {
674                window.wpestateHalfMapState.applyCurrentLayout();
675            }
676
677
678            wpestate_restart_js_after_ajax();
679            jQuery('.col-md-12.listing_wrapper .property_unit_custom_element.image').each(function () {
680                jQuery(this).parent().addClass('wrap_custom_image');
681            });
682            
683            if (data.saved_search !== null) {
684                jQuery(".search_param").html(data.saved_search);
685                jQuery("#search_args").val( JSON.stringify(data.args) );
686                jQuery("#meta_args").val(  JSON.stringify(data.search_meta) );
687
688            }
689        },
690        error: function (errorThrown) {
691
692        }
693    });//end ajax
694}
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735function wpestate_js_pagination_ajax(){
736    "use strict";
737
738    jQuery('.pagination_ajax a').on('click', function (event) {
739        event.preventDefault();
740        var newpage = parseInt(jQuery(this).attr('data-future'), 10);
741        wpestate_start_filtering(newpage);
742    });
743
744}
745
746
747////////////////////////////////////////////////////////////////////////////////////////////
748/// redo js after ajax calls - jslint checked
749////////////////////////////////////////////////////////////////////////////////////////////
750function wpestate_restart_js_after_ajax() {
751    "use strict";
752    wpestate_lazy_load_carousel_property_unit();
753    wpestate_card_unit_contact_actions();
754
755
756    // wpestate_enable_half_map_pin_action();
757    if (typeof wpestate_enable_half_map_pin_action == 'function') {
758        wpestate_enable_half_map_pin_action();
759    }
760    var newpage, post_id, post_image, to_add, icon;
761
762    jQuery('.pagination_ajax_search a').on('click', function (event) {
763        event.preventDefault();
764
765        newpage = parseInt(jQuery(this).attr('data-future'), 10);
766        document.getElementById('scrollhere').scrollIntoView();
767        if (mapfunctions_vars.custom_search === 'yes') {
768            wpestate_custom_search_start_filtering_ajax(newpage);  // should be custom
769        } else {
770            wpestate_start_filtering_ajax(newpage);//
771        }
772    });
773
774    jQuery('.pagination_ajax a').on('click', function (event) {
775        event.preventDefault();
776        newpage = parseInt(jQuery(this).attr('data-future'), 10);
777        document.getElementById('scrollhere').scrollIntoView();
778        wpestate_start_filtering(newpage);
779    });
780
781
782
783    jQuery('.property_listing').on('click', function (event) {
784        if (
785            event.target.classList.contains('demo-icon') || 
786            event.target.classList.contains('carousel-control-next-icon') || 
787            event.target.classList.contains('carousel-control-prev-icon')   
788        ){
789            return;
790        }
791
792
793        if (control_vars.property_modal === '1' && !Modernizr.mq('only all and (max-width: 1024px)')) {
794            event.preventDefault();
795            event.stopPropagation();
796            scroll_modal_save = scroll_modal;
797
798            var listing_id = jQuery(this).parent().attr('data-listid');
799            var main_img_url = jQuery(this).parent().attr('data-main-modal');
800            var main_title = jQuery(this).parent().attr('data-modal-title');
801            var link = jQuery(this).parent().attr('data-modal-link');
802            wpestate_enable_property_modal(listing_id, main_img_url, main_title, link);
803        } else {
804            if (control_vars.new_page_link === '_blank') {
805                return;
806            }
807            var link;
808            link = jQuery(this).attr('data-link');
809
810            window.open(link, '_self');
811        }
812    });
813
814
815
816
817
818    jQuery('.share_unit').on('click', function (event) {
819        event.stopPropagation();
820    });
821
822    var already_in = [];
823
824    jQuery('.compare-action').unbind('click');
825    jQuery('.compare-action').on('click', function (event) {
826        event.stopPropagation();
827        jQuery('.prop-compare').animate({
828            right: "0px"
829        });
830        post_id = jQuery(this).attr('data-pid');
831        for (var i = 0; i < already_in.length; i++) {
832            if (already_in[i] === post_id) {
833                return;
834            }
835        }
836
837        already_in.push(post_id);
838        post_image = jQuery(this).attr('data-pimage');
839
840        to_add = '<div class="items_compare ajax_compare" style="display:none;"><img src="' + post_image + '" class="img-responsive"><input type="hidden" value="' + post_id + '" name="selected_id[]" /></div>';
841        jQuery('div.items_compare:first-child').css('background', 'red');
842        if (parseInt(jQuery('.items_compare').length, 10) > 3) {
843            jQuery('.items_compare:first').remove();
844        }
845        jQuery('#submit_compare').before(to_add);
846        jQuery('.items_compare').fadeIn(800);
847    });
848
849    jQuery('#submit_compare').unbind('click');
850    jQuery('#submit_compare').on('click', function (event) {
851        jQuery('#form_compare').trigger('submit');
852    });
853
854    jQuery('.icon-fav').on('click', function (event) {
855        event.stopPropagation();
856        icon = jQuery(this);
857        wpestate_add_remove_favorite(icon);
858    });
859
860    jQuery(".share_list, .icon-fav, .compare-action,.property_listing_details_v7_item").on('mouseenter', function () {
861        jQuery(this).tooltip('show');
862    })
863            .on('mouseleave', function () {
864                jQuery(this).tooltip('hide');
865            });
866
867
868    jQuery('.share_list').on('click', function (event) {
869        event.stopPropagation();
870        var sharediv = jQuery(this).parent().find('.share_unit');
871        sharediv.toggle();
872        jQuery(this).toggleClass('share_on');
873    });
874
875    wpestate_grid_list_controls();
876    wpestate_enable_share_unit();
877    wpestate_enable_not_logged_favorites();
878}
879
880
881
882
883
884/*
885*
886* Remove/Add to favorites(property page)  when login is not required
887*
888*/
889
890function wpestate_add_remove_favorite_local_storage_single(icon,post_id){
891    var current_favorites = window.localStorage.getItem('wpestate_favorites');
892    post_id=parseInt(post_id);
893
894
895    if(icon.hasClass('isnotfavorite')){
896
897        if ( current_favorites ) {
898            current_favorites=current_favorites + ',' + post_id;
899            window.localStorage.setItem('wpestate_favorites',current_favorites );
900        } else {
901            current_favorites=post_id;
902            window.localStorage.setItem('wpestate_favorites', current_favorites);
903        }
904
905        setCookie('wpestate_favorites',current_favorites,10);
906
907        jQuery('#add_favorites').removeClass('isnotfavorite').addClass('isfavorite').attr('title', ajaxcalls_vars.remove_favorite);
908        jQuery('#add_favorites').html('<i class="fas fa-heart"></i>' + ajaxcalls_vars.favorite);
909  
910    }else{
911
912        var prop_ids = jQuery.map(current_favorites.split(','), function(value){
913						return parseInt(value);
914					});
915
916               
917        const index = prop_ids.indexOf( post_id );      
918      
919        if (index > -1) {
920            prop_ids.splice(index, 1);      
921            window.localStorage.setItem('wpestate_favorites', prop_ids);
922            setCookie('wpestate_favorites',prop_ids,10);
923        }
924
925
926        jQuery('#add_favorites').removeClass('isfavorite').addClass('isnotfavorite').attr('title', ajaxcalls_vars.add_favorite);
927        jQuery('#add_favorites').html('<i class="far fa-heart"></i>' + ajaxcalls_vars.favorite);
928
929    }
930
931    current_favorites = window.localStorage.getItem('wpestate_favorites');
932}
933
934
935
936/*
937*
938* Remove/Add to favorites when login is not required
939*
940*/
941
942
943function wpestate_add_remove_favorite_local_storage(icon,post_id){
944    var current_favorites = window.localStorage.getItem('wpestate_favorites');
945    post_id=parseInt(post_id);
946
947    if(icon.hasClass('icon-fav-off')){
948
949        if ( current_favorites ) {
950            current_favorites=current_favorites+','+post_id;
951            window.localStorage.setItem('wpestate_favorites', current_favorites);
952        } else {
953            current_favorites=post_id;
954            window.localStorage.setItem('wpestate_favorites', current_favorites);
955        }
956
957        setCookie('wpestate_favorites',current_favorites,10);
958
959        icon.removeClass('icon-fav-off').addClass('icon-fav-on');
960        icon.attr('title', ajaxcalls_vars.remove_fav);
961
962    }else{
963
964        var prop_ids = jQuery.map(current_favorites.split(','), function(value){
965						return parseInt(value);
966					});
967           
968        const index = prop_ids.indexOf( post_id );      
969  
970        if (index > -1) {
971            prop_ids.splice(index, 1);
972            window.localStorage.setItem('wpestate_favorites', prop_ids);
973            setCookie('wpestate_favorites',prop_ids,10);
974        }
975
976        if (icon.hasClass('remove_fav_dash')) {
977            icon.parents('.property_wrapper_dash').remove();
978            icon.parents('.listing_wrapper').remove();
979        }
980
981        icon.removeClass('icon-fav-on').addClass('icon-fav-off');
982        icon.attr('title', ajaxcalls_vars.add_favorite);
983    }
984
985    current_favorites = window.localStorage.getItem('wpestate_favorites');
986}
987
988
989
990/*
991*
992* Remove/Add to favorites
993*
994*/
995
996function wpestate_add_remove_favorite(icon) {
997    "use strict";
998    var post_id, securitypass, ajaxurl;
999    post_id = icon.attr('data-postid');
1000    securitypass = jQuery('#security-pass').val();
1001    ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
1002    var nonce = jQuery('#wpestate_ajax_filtering').val();
1003    var use_favorite_login = ajaxcalls_vars.favorites_login;
1004    
1005    if(use_favorite_login == "no"){
1006        wpestate_add_remove_favorite_local_storage(icon, post_id);
1007        return;
1008    }
1009    
1010    if (parseInt(ajaxcalls_vars.userid, 10) === 0) {
1011        wpestate_trigger_login_modal_open(1);
1012    } else {
1013        icon.toggleClass('icon-fav-off');
1014        icon.toggleClass('icon-fav-on');
1015
1016        
1017        jQuery.ajax({
1018            type: 'POST',
1019            url: ajaxurl,
1020            dataType: 'json',
1021            data: {
1022                'action': 'wpestate_ajax_add_fav',
1023                'post_id': post_id,
1024                'security': nonce
1025            },
1026            success: function (data) {
1027         
1028                if (data.added) {
1029                    icon.removeClass('icon-fav-off').addClass('icon-fav-on');
1030                    // Update Bootstrap tooltip attributes
1031                    icon.attr('aria-label', ajaxcalls_vars.remove_fav);
1032                    icon.attr('data-bs-original-title', ajaxcalls_vars.remove_fav);
1033                    // Also update title for fallback
1034                    icon.attr('title', ajaxcalls_vars.remove_fav);
1035                    
1036                    // Refresh the Bootstrap tooltip if it exists
1037                    if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
1038                        var tooltipInstance = bootstrap.Tooltip.getInstance(icon[0]);
1039                        if (tooltipInstance) {
1040                            tooltipInstance.setContent({
1041                                '.tooltip-inner': ajaxcalls_vars.remove_fav
1042                            });
1043                        }
1044                    }
1045                } else {
1046                    if (icon.hasClass('remove_fav_dash')) {
1047                        icon.parents('.property_wrapper_dash').remove();
1048                    }
1049                    icon.removeClass('icon-fav-on').addClass('icon-fav-off');
1050                    // Update Bootstrap tooltip attributes
1051                    icon.attr('aria-label', ajaxcalls_vars.add_favorite);
1052                    icon.attr('data-bs-original-title', ajaxcalls_vars.add_favorite);
1053                    // Also update title for fallback
1054                    icon.attr('title', ajaxcalls_vars.add_favorite);
1055                    
1056                    // Refresh the Bootstrap tooltip if it exists
1057                    if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
1058                        var tooltipInstance = bootstrap.Tooltip.getInstance(icon[0]);
1059                        if (tooltipInstance) {
1060                            tooltipInstance.setContent({
1061                                '.tooltip-inner': ajaxcalls_vars.add_favorite
1062                            });
1063                        }
1064                    }
1065                }
1066            },
1067            error: function (errorThrown) {
1068                // Handle error case
1069            }
1070        }); // end ajax
1071    } // end login use
1072}
1073
1074////////////////////////////////////////////////////////////////////////////////////////////
1075/// resend listing for approval-jslint checked
1076////////////////////////////////////////////////////////////////////////////////////////////
1077function wpestate_resend_for_approval(prop_id, selected_div) {
1078    "use strict";
1079    var ajaxurl, normal_list_no;
1080    ajaxurl = control_vars.admin_url + 'admin-ajax.php';
1081    var nonce = jQuery('#wpestate_property_actions').val();
1082
1083    jQuery.ajax({
1084        type: 'POST',
1085        url: ajaxurl,
1086        data: {
1087            'action': 'wpestate_ajax_resend_for_approval',
1088            'propid': prop_id,
1089            'security': nonce
1090        },
1091        success: function (data) {
1092
1093            if (data === 'pending') {
1094                selected_div.parent().empty().append('<span class="featured_prop">Sent for approval</span>');
1095                normal_list_no = parseInt(jQuery('#normal_list_no').text(), 10);
1096                jQuery('#normal_list_no').text(normal_list_no - 1);
1097            } else {
1098                selected_div.removeClass('resend_pending').html(data);
1099            }
1100        },
1101        error: function (errorThrown) {
1102
1103        }
1104    });//end ajax
1105}
1106
1107////////////////////////////////////////////////////////////////////////////////////////////
1108/// make property featured-jslint checked
1109////////////////////////////////////////////////////////////////////////////////////////////
1110function wpestate_make_prop_featured(prop_id, selectedspan) {
1111    "use strict";
1112    var ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
1113    var nonce = jQuery('#wpestate_property_actions').val();
1114
1115    jQuery.ajax({
1116        type: 'POST',
1117        url: ajaxurl,
1118        data: {
1119            'action': 'wpestate_ajax_make_prop_featured',
1120            'propid': prop_id,
1121            'security': nonce
1122        },
1123        success: function (data) {
1124            if (data.trim() === 'done') {
1125                selectedspan.empty().html('<span class="label label-success">' + ajaxcalls_vars.prop_featured + '</span>');
1126                var featured_list_no = parseInt(jQuery('#featured_list_no').text(), 10);
1127                jQuery('#featured_list_no').text(featured_list_no - 1);
1128            } else {
1129                selectedspan.empty().removeClass('make_featured').addClass('featured_exp').text(ajaxcalls_vars.no_prop_featured);
1130            }
1131
1132        },
1133        error: function (errorThrown) {
1134        }
1135
1136    });//end ajax
1137}
1138
1139////////////////////////////////////////////////////////////////////////////////////////////
1140/// pay package via paypal recuring-jslint checked
1141////////////////////////////////////////////////////////////////////////////////////////////
1142function wpestate_recuring_pay_pack_via_paypal() {
1143    "use strict";
1144    var ajaxurl, packName, packId;
1145    ajaxurl = control_vars.admin_url + 'admin-ajax.php';
1146
1147    packName = jQuery('.package_selected .pack-listing-title').text();
1148    packId = jQuery('.package_selected .pack-listing-title').attr('data-packid');
1149
1150    var nonce = jQuery('#wpestate_payments_nonce').val();
1151    jQuery.ajax({
1152        type: 'POST',
1153        url: ajaxurl,
1154        data: {
1155            'action': 'wpestate_ajax_paypal_pack_recuring_generation_rest_api',
1156            'packName': packName,
1157            'packId': packId,
1158            'security': nonce
1159        },
1160        success: function (data) {
1161            // Server returns a PayPal recurring-billing URL; validate before redirecting (XSS-5).
1162            wpestate_safe_redirect(data);
1163        },
1164        error: function (errorThrown) {
1165
1166        }
1167    });//end ajax
1168}
1169
1170jQuery(".buypackage").click(function () {
1171    "use strict";
1172    var stripe_pack_id, stripetitle2, stripetitle, stripepay;
1173
1174
1175    jQuery(".pack-listing").removeClass("package_selected");
1176    jQuery(".pack-listing input").prop('checked', false);
1177
1178    jQuery(this).find('.input_pak_check').prop('checked', true);
1179    jQuery(this).parent().addClass("package_selected");
1180
1181    stripetitle = jQuery(this).parent().find('.pack-listing-title').attr('data-stripetitle');
1182    stripetitle2 = jQuery(this).parent().find('.pack-listing-title').attr('data-stripetitle2');
1183    stripepay = jQuery(this).parent().find('.pack-listing-title').attr('data-stripepay');
1184    stripe_pack_id = jQuery(this).parent().find('.pack-listing-title').attr('data-packid');
1185
1186    jQuery('.stripe_buttons').attr("id", stripetitle2);
1187    jQuery('#stripe_script').attr("data-amount", stripepay);
1188    jQuery('#stripe_script').attr("data-description", stripetitle);
1189
1190    jQuery('#pack_id').val(stripe_pack_id);
1191    jQuery('#pack_title').val(stripetitle2);
1192    jQuery('#pay_ammout').val(stripepay);
1193    jQuery('#stripe_form').attr('data-amount', stripepay);
1194
1195
1196    jQuery('.wpestate_stripe_pay_desc').html(control_vars.stripe_pay_for + " " + stripetitle2);
1197    jQuery('#wpestate_stripe_form_button_sumit').html(control_vars.stripe_pay + " " + stripepay / 100 + " " + control_vars.submission_curency);
1198
1199    // enable stripe code
1200
1201
1202});
1203
1204
1205
1206////////////////////////////////////////////////////////////////////////////////////////////
1207/// pay package via paypal-jslint checked
1208////////////////////////////////////////////////////////////////////////////////////////////
1209function wpestate_pay_pack_via_paypal() {
1210    "use strict";
1211    var ajaxurl, packName, packId;
1212    ajaxurl = control_vars.admin_url + 'admin-ajax.php';
1213    packName = jQuery('.package_selected .pack-listing-title').text();
1214    packId = jQuery('.package_selected .pack-listing-title').attr('data-packid');
1215    var nonce = jQuery('#wpestate_payments_nonce').val();
1216    jQuery.ajax({
1217        type: 'POST',
1218        url: ajaxurl,
1219        data: {
1220            'action': 'wpestate_ajax_paypal_pack_generation',
1221            'packName': packName,
1222            'packId': packId,
1223            'security': nonce
1224        },
1225        success: function (data) {
1226            // Server returns a PayPal one-time-payment URL for the package; validate before redirecting (XSS-5).
1227            wpestate_safe_redirect(data);
1228        },
1229        error: function (errorThrown) {
1230
1231        }
1232    });//end ajax
1233
1234}
1235////////////////////////////////////////////////////////////////////////////////////////////
1236/// listing pay -jslint checked
1237////////////////////////////////////////////////////////////////////////////////////////////
1238function wpestate_listing_pay(prop_id, selected_div, is_featured, is_upgrade) {
1239    "use strict";
1240    var ajaxurl = control_vars.admin_url + 'admin-ajax.php';
1241    var nonce = jQuery('#wpestate_payments_nonce').val();
1242    jQuery.ajax({
1243        type: 'POST',
1244        url: ajaxurl,
1245        data: {
1246            'action': 'wpestate_ajax_listing_pay',
1247            'propid': prop_id,
1248            'is_featured': is_featured,
1249            'is_upgrade': is_upgrade,
1250            'security': nonce
1251        },
1252        success: function (data) {
1253            // Server returns a PayPal URL for listing payment/upgrade; validate before redirecting (XSS-5).
1254            wpestate_safe_redirect(data);
1255        },
1256        error: function (errorThrown) {
1257        }
1258    });//end ajax
1259}
1260
1261////////////////////////////////////////////////////////////////////////////////////////////
1262/// start filtering -jslint checked
1263////////////////////////////////////////////////////////////////////////////////////////////
1264
1265/**
1266 * Resolve a single-select dropdown filter to a taxonomy payload array.
1267 *
1268 * Reads the active dropdown button `data-value`, ignores placeholder values,
1269 * then resolves the selected `<li data-value="...">` to `data-term-id` when present.
1270 * Returns a one-item array for AJAX payload compatibility.
1271 *
1272 * @param {string} filterButtonSelector jQuery selector for the dropdown trigger.
1273 * @param {Array<string>} placeholderValues Values that mean "no filter".
1274 * @returns {Array<string>} Selected taxonomy value array (term id preferred).
1275 */
1276function wpestate_resolve_single_filter_taxonomy_values(filterButtonSelector, placeholderValues) {
1277    "use strict";
1278
1279    // Step 1: Resolve button and current selected value.
1280    var filterButton = jQuery(filterButtonSelector).first();
1281    if (filterButton.length === 0) {
1282        return [];
1283    }
1284
1285    var selectedValue = (filterButton.attr('data-value') || '').trim();
1286    if (selectedValue === '') {
1287        return [];
1288    }
1289
1290    // Step 2: Skip placeholder values that represent "all".
1291    var normalizedSelectedValue = selectedValue.toLowerCase();
1292    var normalizedPlaceholders = (placeholderValues || []).map(function (value) {
1293        return String(value).toLowerCase();
1294    });
1295    if (normalizedPlaceholders.indexOf(normalizedSelectedValue) !== -1) {
1296        return [];
1297    }
1298
1299    // Step 3: Resolve selected option term id from the related dropdown list.
1300    var resolvedValue = selectedValue;
1301    var dropdownId = filterButton.attr('id') || '';
1302    if (dropdownId !== '') {
1303        var dropdownMenu = jQuery('ul[aria-labelledby="' + dropdownId + '"]').first();
1304        if (dropdownMenu.length > 0) {
1305            var selectedListItem = dropdownMenu.find('li').filter(function () {
1306                return (jQuery(this).attr('data-value') || '') === selectedValue;
1307            }).first();
1308
1309            if (selectedListItem.length > 0) {
1310                var termId = wpestate_get_term_id_from_element(selectedListItem);
1311                if (termId !== '') {
1312                    resolvedValue = termId;
1313                }
1314            }
1315        }
1316    }
1317
1318    // Step 4: Return the normalized value as one-item array.
1319    return [resolvedValue];
1320}
1321
1322/**
1323 * Collect taxonomy filters for wpestate_start_filtering payload.
1324 *
1325 * Uses canonical taxonomy names as payload keys and term ids as values
1326 * whenever term metadata exists on dropdown options.
1327 *
1328 * @returns {Object} Taxonomy payload object keyed by taxonomy names.
1329 */
1330function wpestate_collect_start_filtering_taxonomies() {
1331    "use strict";
1332
1333    // Step 1: Collect each single-select filter using canonical taxonomy keys.
1334    return {
1335        property_action_category: wpestate_resolve_single_filter_taxonomy_values('#a_filter_action', ['all', 'types', 'all-actions']),
1336        property_category: wpestate_resolve_single_filter_taxonomy_values('#a_filter_categ', ['all', 'categories', 'all-types']),
1337        property_status: wpestate_resolve_single_filter_taxonomy_values('#a_filter_status', ['all', 'property status']),
1338        property_features: wpestate_resolve_single_filter_taxonomy_values('#a_filter_features', ['all', 'property features']),
1339        property_county_state: wpestate_resolve_single_filter_taxonomy_values('#a_filter_county', ['all', 'states']),
1340        property_city: wpestate_resolve_single_filter_taxonomy_values('#a_filter_cities', ['all', 'cities', 'all-cities']),
1341        property_area: wpestate_resolve_single_filter_taxonomy_values('#a_filter_areas', ['all', 'areas', 'all-areas'])
1342    };
1343}
1344
1345function wpestate_start_filtering(newpage) {
1346    "use strict";
1347    jQuery('#gmap-loading').show();
1348    jQuery('#grid_view').addClass('icon_selected');
1349    jQuery('#list_view').removeClass('icon_selected');
1350
1351    // Step 1: Collect canonical taxonomy payload (taxonomy keys + array values).
1352    var taxonomyPayload = wpestate_collect_start_filtering_taxonomies();
1353
1354    // Step 2: Read ordering and page context values.
1355    var order = jQuery('#a_filter_order').attr('data-value');
1356    var ajaxurl;
1357    var page_id;
1358    ajaxurl = ajaxcalls_vars.wpestate_ajax;
1359    page_id = jQuery('#page_idx').val();
1360
1361    // Step 3: Read listing card rendering context from wrapper.
1362    var card_version = jQuery('.wpestate_filter_list_properties_wrapper').attr('data-card_version');
1363    var rownumber = jQuery('.wpestate_filter_list_properties_wrapper').attr('data-rownumber');
1364    var ishortcode = jQuery('.wpestate_filter_list_properties_wrapper').attr('data-ishortcode');
1365    var number = jQuery('.wpestate_filter_list_properties_wrapper').attr('data-number');
1366    var context = jQuery('.wpestate_filter_list_properties_wrapper').attr('data-context');
1367
1368
1369
1370    // Step 4: Show loading skeletons before the AJAX request.
1371    var align = '';
1372    if (jQuery('.wpestate_filter_list_properties_wrapper').length > 0) {
1373
1374        wpestate_createSkeletons(jQuery('.wpestate_filter_list_properties_wrapper'));
1375        align = jQuery('.wpestate_filter_list_properties_wrapper').attr('data-align');
1376 } else {
1377         wpestate_createSkeletons(jQuery('#listing_ajax_container'));
1378    }
1379    var nonce = jQuery('#wpestate_ajax_filtering').val();
1380
1381    // Step 5: Build the request payload with canonical taxonomy keys.
1382    var ajaxData = {
1383        'action': 'wpestate_ajax_filter_listings',
1384        'property_action_category': taxonomyPayload.property_action_category,
1385        'property_category': taxonomyPayload.property_category,
1386        'property_status': taxonomyPayload.property_status,
1387        'property_features': taxonomyPayload.property_features,
1388        'property_county_state': taxonomyPayload.property_county_state,
1389        'property_city': taxonomyPayload.property_city,
1390        'property_area': taxonomyPayload.property_area,
1391        'order': order,
1392        'newpage': newpage,
1393        'page_id': page_id,
1394        'align': align,
1395        'card_version': card_version,
1396        'rownumber': rownumber,
1397        'ishortcode': ishortcode,
1398        'number': number,
1399        'context': context,
1400        'security': nonce
1401    };
1402
1403    jQuery.ajax({
1404        type: 'POST',
1405        url: ajaxurl,
1406        dataType: 'json',
1407        data: ajaxData,
1408        success: function (data) {
1409
1410            jQuery('#listing_loader').hide();
1411            if (jQuery('.wpestate_filter_list_properties_wrapper').length > 0) {
1412           
1413                  wpestate_replaceSkeletons(jQuery('.wpestate_filter_list_properties_wrapper'), data.to_show);
1414            } else {
1415                 wpestate_replaceSkeletons(jQuery('#listing_ajax_container'), data.to_show);
1416            }
1417
1418            jQuery('.pagination_nojax').hide();
1419          
1420            // map update
1421            var no_results = parseInt(data.no_results);
1422            if (no_results !== 0) {
1423                wpestate_load_on_demand_pins(data.markers, no_results, 0);
1424            } else {
1425                wpestate_show_no_results();
1426            }
1427
1428            jQuery('.col-md-12.listing_wrapper .property_unit_custom_element.image').each(function () {
1429                jQuery(this).parent().addClass('wrap_custom_image');
1430            });
1431
1432            // Re-bind click handlers on the freshly-injected DOM (pagination,
1433            // favorites, etc.). Without this, the new pagination_ajax links
1434            // fall back to their server-rendered href, which during an
1435            // admin-ajax POST resolves to /wp-admin/admin-ajax.php?paged=N.
1436            wpestate_restart_js_after_ajax();
1437
1438            jQuery('#gmap-loading').hide();
1439
1440        },
1441        error: function (errorThrown) {
1442
1443        }
1444    });//end ajax
1445}
1446
1447
1448
1449
1450
1451
1452
1453////////////////////////////////////////////////////////////////////////////////////////////
1454// on ready -jslint checked
1455//  developer listing on tab click !!
1456////////////////////////////////////////////////////////////////////////////////////////////
1457jQuery(document).ready(function ($) {
1458    "use strict";
1459
1460    wpestate_add_to_favorites();
1461    wpestate_js_pagination_ajax();
1462    wpestate_enable_not_logged_favorites();
1463    wpestate_load_agency_developer_listings();
1464
1465
1466    wpestate_agent_submit_email();
1467
1468    wpestate_theme_slider_show_contact();
1469    wpestate_enable_schedule_contact();
1470   
1471
1472
1473
1474
1475
1476    ///////////////////////////////////////////////////////////////////////////////////////////
1477    //// stripe cancel
1478    ///////////////////////////////////////////////////////////////////////////////////////////
1479    $('#stripe_cancel').on('click', function (event) {
1480
1481        var stripe_user_id, ajaxurl;
1482        stripe_user_id = $(this).attr('data-stripeid');
1483        ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
1484        $('#stripe_cancel').text(ajaxcalls_vars.saving);
1485        var nonce = jQuery('#wpestate_stripe_cancel_nonce').val();
1486
1487        $.ajax({
1488            type: 'POST',
1489            url: ajaxurl,
1490            data: {
1491                'action': 'wpestate_cancel_stripe',
1492                'stripe_customer_id': stripe_user_id,
1493                'security': nonce,
1494
1495            },
1496            success: function (data) {
1497                $('#stripe_cancel').text(ajaxcalls_vars.stripecancel);
1498            },
1499            error: function (errorThrown) {
1500            }
1501        });
1502    });
1503
1504
1505    ////////////////////////////////////////////////////////////////////////////////////////////
1506    /// resend for approval 
1507    ///////////////////////////////////////////////////////////////////////////////////////////
1508    $('.resend_pending').on('click', function (event) {
1509
1510        var prop_id = $(this).attr('data-listingid');
1511        wpestate_resend_for_approval(prop_id, $(this));
1512    });
1513
1514
1515
1516    jQuery('#wpestate_stripe_booking_recurring').on('click', function () {
1517
1518        var modalid = jQuery(this).attr('data-modalid');
1519        var modal = jQuery('#' + modalid);
1520        
1521        modal.appendTo('body');
1522        
1523        modal.show();
1524        jQuery('#' + modalid + ' .wpestate_stripe_form_1').show();
1525
1526        wpestate_start_stripe(1, modalid);
1527    });
1528
1529    ///////////////////////////////////////////////////////////////////////////////////////////
1530    ////////  pack upgrade via paypal
1531    ///////////////////////////////////////////////////////////////////////////////////////////
1532    $('#pick_pack').on('click', function (event) {
1533
1534        var pay_paypal;
1535        pay_paypal = '<div class="modal fade" id="paypal_modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-body listing-submit">' + ajaxcalls_vars.paypal + '</div></div></div></div></div>';
1536        jQuery('body').append(pay_paypal);
1537        jQuery('#paypal_modal').modal();
1538
1539
1540        if ($('#pack_recuring').is(':checked')) {
1541            wpestate_recuring_pay_pack_via_paypal();
1542        } else {
1543            wpestate_pay_pack_via_paypal();
1544        }
1545    });
1546
1547    ///////////////////////////////////////////////////////////////////////////////////////////
1548    //////// listing pay via paypal
1549    ///////////////////////////////////////////////////////////////////////////////////////////
1550    $('.listing_submit_normal').on('click', function (event) {
1551        var prop_id, featured_checker, is_featured, is_upgrade, pay_paypal;
1552        pay_paypal = '<div class="modal fade" id="paypal_modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-body listing-submit">' + ajaxcalls_vars.paypal + '</div></div></div></div></div>';
1553        jQuery('body').append(pay_paypal);
1554        jQuery('#paypal_modal').modal();
1555
1556
1557        prop_id = $(this).attr('data-listingid');
1558        featured_checker = $(this).parent().find('input');
1559        is_featured = 0;
1560        is_upgrade = 0;
1561
1562        if (featured_checker.prop('checked')) {
1563            is_featured = 1;
1564        } else {
1565            is_featured = 0;
1566        }
1567
1568        wpestate_listing_pay(prop_id, $(this), is_featured, is_upgrade);
1569    });
1570  
1571    jQuery('.woo_pay_submit').on('click', function () {
1572        var pay_paypal, prop_id, book_id, invoice_id, is_featured, is_upgrade, ajaxurl, depozit, is_submit, pack_id;
1573        prop_id = jQuery(this).attr('data-propid');
1574        depozit = jQuery(this).attr('data-deposit');
1575        is_featured = jQuery(this).attr('data-is_featured');
1576        pack_id = jQuery('.package_selected').attr('data-id');
1577        is_upgrade = 0;
1578        is_submit = 1;
1579        if (jQuery('#open_packages').length > 0) {
1580            is_submit = 0;
1581        }
1582        book_id = '';
1583        invoice_id = '';
1584
1585        ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
1586        jQuery.ajax({
1587            type: 'POST',
1588            url: ajaxurl,
1589            data: {
1590                'action': 'wpestate_woo_pay',
1591                'propid': prop_id,
1592                'depozit': depozit,
1593                'is_submit': is_submit,
1594                'is_featured': is_featured,
1595                'pack_id': pack_id,
1596                'book_id': book_id,
1597                'invoiceid': invoice_id,
1598                'security': ajaxcalls_vars.wpestate_woo_pay_nonce
1599            },
1600            success: function (data) {
1601                if (data !== false) {
1602                    window.location.href = ajaxcalls_vars.checkout_url;
1603                }
1604            },
1605            error: function (errorThrown) {}
1606        });//end ajax
1607    });
1608
1609    $('.listing_upgrade').on('click', function (event) {
1610        var is_upgrade, is_featured, prop_id;
1611        is_upgrade = 1;
1612        is_featured = 0;
1613        prop_id = $(this).attr('data-listingid');
1614        wpestate_listing_pay(prop_id, $(this), is_featured, is_upgrade);
1615    });
1616
1617
1618
1619    ///////////////////////////////////////////////////////////////////////////////////////////
1620    /////// Property page  + ajax call on contact
1621    ///////////////////////////////////////////////////////////////////////////////////////////
1622
1623
1624  
1625
1626    ///////////////////////////////////////////////////////////////////////////////////////////
1627    ////////  property listing listing
1628    ////////////////////////////////////////////////////////////////////////////////////////////
1629
1630    $('.listing_filters_head li').on('click', function (event) {
1631
1632        var pick, value, parent;
1633        pick = $(this).text();
1634        value = $(this).attr('data-value');
1635        parent = $(this).parent().parent();
1636        parent.find('.filter_menu_trigger').text(pick).append('<span class="caret caret_filter"></span>').attr('data-value', value);
1637        parent.find('input:hidden').val(value);
1638
1639        wpestate_start_filtering(1);
1640
1641
1642    });
1643
1644
1645
1646
1647    ///////////////////////////////////////////////////////////////////////////////////////////
1648    //////// advanced search filtering
1649    ////////////////////////////////////////////////////////////////////////////////////////////
1650
1651       jQuery(document).on('click', '.wpresidence_property_list_half_map_core .wpresidence_wrap_a_filter_order li', function (event) {
1652    
1653        var pick, value, parent;
1654        pick = $(this).text();
1655        value = $(this).attr('data-value');
1656        parent = $(this).parent().parent();
1657        parent.find('.filter_menu_trigger').text(pick).append('<span class="caret caret_filter"></span>').attr('data-value', value);
1658        parent.find('input:hidden').val(value);
1659
1660        if(mapfunctions_vars.custom_search==='yes'){
1661            jQuery('.half-pagination').remove();
1662
1663            wpestate_custom_search_start_filtering_ajax(1);
1664        }else{
1665
1666            wpestate_start_filtering_ajax(1);
1667        }
1668
1669    });
1670
1671
1672    $('.adv_listing_filters_head li').on('click', function (event) {
1673
1674        var pick, value, parent, args, page_id, ajaxurl;
1675        ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
1676        pick = $(this).text();
1677        value = $(this).attr('data-value');
1678        parent = $(this).parent().parent();
1679        parent.find('.filter_menu_trigger').text(pick).append('<span class="caret caret_filter"></span>').attr('data-value', value);
1680        args = $('#searcharg').val();
1681        page_id = $('#page_idx').val();
1682        //$('#listing_ajax_container').empty();
1683        //$('#listing_loader').show();
1684
1685
1686        const listingContainer = jQuery('#listing_ajax_container');
1687        wpestate_createSkeletons(listingContainer);
1688
1689
1690        var nonce = jQuery('#wpestate_search_nonce').val();
1691        $.ajax({
1692            type: 'POST',
1693            url: ajaxurl,
1694
1695            data: {
1696                'action': 'wpestate_advanced_search_filters',
1697                'args': args,
1698                'value': value,
1699                'page_id': page_id,
1700                'security': nonce
1701            },
1702            success: function (data) {
1703                //$('#listing_loader').hide();
1704                //$('#listing_ajax_container').append(data);
1705                 wpestate_replaceSkeletons(listingContainer, data);
1706                
1707                
1708                wpestate_restart_js_after_ajax();
1709                wpestate_add_pagination_orderby();
1710            },
1711            error: function (errorThrown) {
1712            }
1713        }); //end ajax
1714    });
1715
1716
1717    function wpestate_add_pagination_orderby() {
1718
1719        var order = $('#a_filter_order').attr('data-value');
1720        jQuery('.pagination a').each(function () {
1721            var href = $(this).attr('href');
1722            href = href + "&order_search=" + order;
1723            $(this).attr('href', href);
1724        });
1725    }
1726
1727    ///////////////////////////////////////////////////////////////////////////////////////////
1728    ////////  Ajax add to favorites on listing
1729    ////////////////////////////////////////////////////////////////////////////////////////////
1730    $('.icon-fav,.remove_fav_dash').on('click', function (event) {
1731        event.stopPropagation();
1732        var icon = $(this);
1733        wpestate_add_remove_favorite(icon);
1734    });
1735
1736
1737
1738
1739    // remove from fav listing on user profile
1740    $('.icon-fav-on-remove').on('click', function (event) {
1741        event.stopPropagation();
1742        $(this).parent().parent().remove();
1743
1744    });
1745
1746
1747
1748
1749
1750    ///////////////////////////////////////////////////////////////////////////////////////////
1751    ////////  update profile
1752    ////////////////////////////////////////////////////////////////////////////////////////////
1753    $('#register_agent').on('click', function (event) {
1754        var firstname = $('#firstname').val();
1755        var secondname = $('#secondname').val();
1756        var useremail = $('#useremail').val();
1757        var userphone = $('#userphone').val();
1758        var usermobile = $('#usermobile').val();
1759        var usertitle = $('#usertitle').val();
1760        var description = $('#about_me').val();
1761        var userfacebook = $('#userfacebook').val();
1762        var usertwitter = $('#usertwitter').val();
1763        var userlinkedin = $('#userlinkedin').val();
1764        var userpinterest = $('#userpinterest').val();
1765        var userinstagram = $('#userinstagram').val();
1766        var userurl = $('#website').val();
1767        var agent_username = $('#agent_username').val();
1768        var agent_password = $('#agent_password').val();
1769        var agent_repassword = $('#agent_repassword').val();
1770        var agent_category_submit = $('#agent_category_submit').val();
1771        var agent_action_submit = $('#agent_action_submit').val();
1772        var agent_city = $('#agent_city').val();
1773        var agent_county = $('#agent_county').val();
1774        var agent_member = $('#agent_member').val();
1775        var agent_address = $('#agent_address').val();
1776
1777        var agent_youtube = $('#useryoutube').val();
1778        var agent_tiktok = $('#usertiktok').val();
1779        var agent_telegram = $('#usertelegram').val();
1780        var agent_vimeo = $('#uservimeo').val();
1781        var agent_private_notes = $('#agent_private_notes').val();
1782
1783        var agent_area = $('#agent_area').val();
1784        var is_agent_edit = $('#is_agent_edit').val();
1785        var user_id = $('#user_id').val();
1786        var agent_id = $('#agent_id').val();
1787        var ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
1788        var securityprofile = $('#security-profile').val();
1789        var upload_picture = $('#upload_picture').val();
1790        var profile_image_url = $('#profile-image').attr('data-profileurl');
1791        var profile_image_url_small = $('#profile-image').attr('data-smallprofileurl');
1792        var acf_fields = wpestate_return_form_acf_fields('.field-group');
1793
1794        // customparameters
1795        var agent_custom_label = [];
1796        $('.single_parameter_row .agent_custom_label').each(function () {
1797            agent_custom_label.push($(this).val());
1798        });
1799
1800        var agent_custom_value = [];
1801        $('.single_parameter_row .agent_custom_value').each(function () {
1802            agent_custom_value.push($(this).val());
1803        });
1804        var nonce = jQuery('#wpestate_register_agent_nonce').val();
1805
1806        window.scrollTo(0, 0);
1807
1808        $('#profile_message').empty().append('<div class="login-alert">' + ajaxcalls_vars.saving + '<div>');
1809
1810
1811        $.ajax({
1812            type: 'POST',
1813            url: ajaxurl,
1814            dataType: 'json',
1815            data: {
1816                'action': 'wpestate_ajax_register_agent',
1817                'agent_username': agent_username,
1818                'agent_password': agent_password,
1819                'agent_repassword': agent_repassword,
1820                'firstname': firstname,
1821                'secondname': secondname,
1822                'useremail': useremail,
1823                'userphone': userphone,
1824                'usermobile': usermobile,
1825                'usertitle': usertitle,
1826                'description': description,
1827                'upload_picture': upload_picture,
1828                'security-profile': securityprofile,
1829                'profile_image_url': profile_image_url,
1830                'profile_image_url_small': profile_image_url_small,
1831                'userfacebook': userfacebook,
1832                'usertwitter': usertwitter,
1833                'userlinkedin': userlinkedin,
1834                'userpinterest': userpinterest,
1835                'userinstagram': userinstagram,
1836                'userurl': userurl,
1837                'agent_category_submit': agent_category_submit,
1838                'agent_action_submit': agent_action_submit,
1839                'agent_city': agent_city,
1840                'agent_county': agent_county,
1841                'agent_area': agent_area,
1842                'agentedit': is_agent_edit,
1843                'userid': user_id,
1844                'agentid': agent_id,
1845                'agent_member': agent_member,
1846                'agent_address': agent_address,
1847                'agent_youtube':agent_youtube,
1848                'agent_tiktok':agent_tiktok,
1849                'agent_telegram':agent_telegram,
1850                'agent_vimeo': agent_vimeo,
1851                'agent_private_notes': agent_private_notes,
1852                'acf_fields': JSON.stringify(acf_fields),
1853                'agent_custom_label': agent_custom_label,
1854                'agent_custom_value': agent_custom_value,
1855                'security': nonce
1856
1857            },
1858            success: function (data) {
1859                $('#profile_message').empty().append('<div class="login-alert">
1859' + data.mesaj + '<div>');
1860                if (data.added) {
1861                    setTimeout(function () {
1862                        window.open(ajaxcalls_vars.agent_list, '_self', false);
1863                    }, 1500);
1864                }
1865            },
1866            error: function (errorThrown) {
1867
1868            }
1869        });
1870    });
1871
1872
1873    $('#update_profile').on('click', function (event) {
1874
1875        $('#profile_message').find('.login-alert').remove();
1876
1877        var firstname, secondname, userurl, usermobile, userinstagram, userpinterest, userlinkedin, usertwitter, userfacebook, profile_image_url, profile_image_url_small, firstname, secondname, useremail, userphone, usertitle, description, ajaxurl, securityprofile, upload_picture;
1878        firstname = $('#firstname').val();
1879        secondname = $('#secondname').val();
1880        useremail = $('#useremail').val();
1881        userphone = $('#userphone').val();
1882        usermobile = $('#usermobile').val();
1883        usertitle = $('#usertitle').val();
1884        description = $('#about_me').val();
1885        userfacebook = $('#userfacebook').val();
1886        usertwitter = $('#usertwitter').val();
1887        userlinkedin = $('#userlinkedin').val();
1888        userpinterest = $('#userpinterest').val();
1889        userinstagram = $('#userinstagram').val();
1890        userurl = $('#website').val();
1891        var hubspot_api = $('#hubspot_api').val();
1892        var agent_member = $('#agent_member').val();
1893        var agent_address= $('#agent_address').val();
1894
1895        var agent_youtube = $('#useryoutube').val();
1896        var agent_tiktok = $('#usertiktok').val();
1897        var agent_telegram = $('#usertelegram').val();
1898        var agent_vimeo = $('#uservimeo').val();
1899        var agent_private_notes = $('#agent_private_notes').val();
1900
1901        var agent_category_submit = $('#agent_category_submit').val();
1902        var agent_action_submit = $('#agent_action_submit').val();
1903        var agent_city = $('#agent_city').val();
1904        var agent_county = $('#agent_county').val();
1905        var agent_area = $('#agent_area').val();
1906        var nonce = jQuery('#wpestate_update_profile_nonce').val();
1907
1908        ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
1909        securityprofile = $('#security-profile').val();
1910        upload_picture = $('#upload_picture').val();
1911        profile_image_url = $('#profile-image').attr('data-profileurl');
1912        profile_image_url_small = $('#profile-image').attr('data-smallprofileurl');
1913
1914        var acf_fields = wpestate_return_form_acf_fields('.field-group');
1915
1916        // customparameters
1917        var agent_custom_label = [];
1918        $('.cliche_row .agent_custom_label').each(function () {
1919            agent_custom_label.push($(this).val());
1920        });
1921
1922        var agent_custom_value = [];
1923        $('.cliche_row .agent_custom_value').each(function () {
1924            agent_custom_value.push($(this).val());
1925        });
1926
1927
1928
1929        window.scrollTo(0, 0);
1930        $('#profile_message').empty().append('<div class="login-alert">' + ajaxcalls_vars.saving + '<div>');
1931        $.ajax({
1932            type: 'POST',
1933            url: ajaxurl,
1934            data: {
1935                'action': 'wpestate_ajax_update_profile',
1936                'firstname': firstname,
1937                'secondname': secondname,
1938                'useremail': useremail,
1939                'userphone': userphone,
1940                'usermobile': usermobile,
1941                'usertitle': usertitle,
1942                'description': description,
1943                'upload_picture': upload_picture,
1944                'security-profile': securityprofile,
1945                'profile_image_url': profile_image_url,
1946                'profile_image_url_small': profile_image_url_small,
1947                'userfacebook': userfacebook,
1948                'usertwitter': usertwitter,
1949                'userlinkedin': userlinkedin,
1950                'userpinterest': userpinterest,
1951                'userinstagram': userinstagram,
1952                'userurl': userurl,
1953                'agent_category_submit': agent_category_submit,
1954                'agent_action_submit': agent_action_submit,
1955                'agent_city': agent_city,
1956                'agent_county': agent_county,
1957                'agent_area': agent_area,
1958                'agent_member': agent_member,
1959                'agent_address':agent_address,
1960                'agent_youtube':agent_youtube,
1961                'agent_tiktok':agent_tiktok,
1962                'agent_telegram':agent_telegram,
1963                'agent_vimeo': agent_vimeo,
1964                'agent_private_notes': agent_private_notes,
1965                'agent_custom_label': agent_custom_label,
1966                'agent_custom_value': agent_custom_value,
1967                'acf_fields': JSON.stringify(acf_fields),
1968                'hubspot_api': hubspot_api,
1969                'security': nonce,
1970
1971            },
1972            success: function (data) {
1973
1974                $('#profile_message').empty().append('<div class="login-alert">
1974' + data + '<div>');
1975                window.scrollTo(0, 0);
1976            },
1977            error: function (errorThrown) {
1978            }
1979        });
1980    });
1981
1982
1983
1984    $('#update_profile_agency').on('click', function () {
1985
1986        var hubspot_api, agency_opening_hours, agency_license, agency_long, agency_lat, agency_address, agency_area, agency_county, agency_city, agency_action_submit, agency_action_submit, agency_category_submit, agency_taxes, agency_website, agency_languages, agency_name, userurl, usermobile, userinstagram, userpinterest, userlinkedin, usertwitter, userfacebook, profile_image_url, profile_image_url_small, firstname, secondname, useremail, userphone, usertitle, description, ajaxurl, securityprofile, upload_picture;
1987        agency_name = $('#agency_title').val();
1988        useremail = $('#useremail').val();
1989        userphone = $('#userphone').val();
1990        usermobile = $('#usermobile').val();
1991
1992        description = $('#about_me').val();
1993        userfacebook = $('#userfacebook').val();
1994        usertwitter = $('#usertwitter').val();
1995        userlinkedin = $('#userlinkedin').val();
1996        userpinterest = $('#userpinterest').val();
1997        userinstagram = $('#userinstagram').val();
1998
1999        var useryoutube = $('#useryoutube').val();
2000        var usertiktok = $('#usertiktok').val();
2001        var usertelegram = $('#usertelegram').val();
2002        var uservimeo = $('#uservimeo').val();
2003        var agency_private_notes = $('#agency_private_notes').val();
2004
2005
2006
2007        agency_languages = $('#agency_languages').val();
2008        agency_website = $('#agency_website').val();
2009        agency_taxes = $('#agency_taxes').val();
2010        agency_license = $('#agency_license').val();
2011
2012
2013        agency_category_submit = $('#agency_category_submit').val();
2014        agency_action_submit = $('#agency_action_submit').val();
2015        agency_city = $('#agency_city').val();
2016        agency_county = $('#agency_county').val();
2017        agency_area = $('#agency_area').val();
2018        agency_address = $('#agency_address').val();
2019        agency_lat = $('#agency_lat').val();
2020        agency_long = $('#agency_long').val();
2021        agency_opening_hours = $('#agency_opening_hours ').val();
2022        hubspot_api = $('#hubspot_api ').val();
2023        securityprofile = $('#security-profile').val();
2024        upload_picture = $('#upload_picture').val();
2025        profile_image_url = $('#profile-image').attr('data-profileurl');
2026        profile_image_url_small = $('#profile-image').attr('data-smallprofileurl');
2027
2028        var acf_fields = wpestate_return_form_acf_fields('.field-group');
2029
2030        ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
2031
2032        var nonce = jQuery('#wpestate_update_profile_nonce').val();
2033        $('#profile_message').empty().append('<div class="login-alert">' + ajaxcalls_vars.saving + '<div>');
2034        window.scrollTo(0, 0);
2035        $.ajax({
2036            type: 'POST',
2037            url: ajaxurl,
2038            data: {
2039                'action': 'wpestate_ajax_update_profile_agency',
2040                'agency_name': agency_name,
2041                'useremail': useremail,
2042                'userphone': userphone,
2043                'usermobile': usermobile,
2044                'usertitle': usertitle,
2045                'description': description,
2046                'upload_picture': upload_picture,
2047                'security-profile': securityprofile,
2048                'profile_image_url': profile_image_url,
2049                'profile_image_url_small': profile_image_url_small,
2050                'userfacebook': userfacebook,
2051                'usertwitter': usertwitter,
2052                'userlinkedin': userlinkedin,
2053                'userpinterest': userpinterest,
2054                'userinstagram': userinstagram,
2055                'useryoutube':useryoutube,
2056                'usertiktok':usertiktok,
2057                'usertelegram':usertelegram,
2058                'uservimeo':uservimeo,
2059                'agency_private_notes':agency_private_notes,
2060                'userurl': userurl,
2061                'agency_languages': agency_languages,
2062                'agency_website': agency_website,
2063                'agency_taxes': agency_taxes,
2064                'agency_license': agency_license,
2065                'agency_category_submit': agency_category_submit,
2066                'agency_action_submit': agency_action_submit,
2067                'agency_city': agency_city,
2068                'agency_county': agency_county,
2069                'agency_area': agency_area,
2070                'agency_address': agency_address,
2071                'agency_lat': agency_lat,
2072                'agency_opening_hours': agency_opening_hours,
2073                'agency_long': agency_long,
2074                'hubspot_api': hubspot_api,
2075                'acf_fields': JSON.stringify(acf_fields),
2076                'security': nonce
2077            },
2078            success: function (data) {
2079                $('#profile_message').empty().append('<div class="login-alert">
2079' + data + '<div>');
2080                window.scrollTo(0, 0);
2081            },
2082            error: function (errorThrown) {
2083                console.log(errorThrown);
2084            }
2085        });
2086    });
2087
2088
2089
2090//    update developer profile
2091    $('#update_profile_developer').on('click', function () {
2092
2093        var hubspot_api, developer_license, developer_long, developer_lat, developer_address, developer_area, developer_county, developer_city, developer_action_submit, developer_action_submit, developer_category_submit, developer_taxes, developer_website, developer_languages, developer_name, userurl, usermobile, userinstagram, userpinterest, userlinkedin, usertwitter, userfacebook, profile_image_url, profile_image_url_small, firstname, secondname, useremail, userphone, usertitle, description, ajaxurl, securityprofile, upload_picture;
2094        developer_name = $('#developer_title').val();
2095        useremail = $('#useremail').val();
2096        userphone = $('#userphone').val();
2097        usermobile = $('#usermobile').val();
2098
2099        description = $('#about_me').val();
2100        userfacebook = $('#userfacebook').val();
2101        usertwitter = $('#usertwitter').val();
2102        userlinkedin = $('#userlinkedin').val();
2103        userpinterest = $('#userpinterest').val();
2104        userinstagram = $('#userinstagram').val();
2105
2106
2107       var useryoutube = $('#useryoutube').val();
2108       var usertiktok = $('#usertiktok').val();
2109       var usertelegram = $('#usertelegram').val();
2110       var  uservimeo = $('#uservimeo').val();
2111       var  developer_private_notes = $('#developer_private_notes').val();
2112
2113
2114
2115        developer_languages = $('#developer_languages').val();
2116        developer_website = $('#developer_website').val();
2117        developer_taxes = $('#developer_taxes').val();
2118        developer_license = $('#developer_license').val();
2119
2120
2121        developer_category_submit = $('#developer_category_submit').val();
2122        developer_action_submit = $('#developer_action_submit').val();
2123        developer_city = $('#developer_city').val();
2124        developer_county = $('#developer_county').val();
2125        developer_area = $('#developer_area').val();
2126        developer_address = $('#developer_address').val();
2127        developer_lat = $('#developer_lat').val();
2128        developer_long = $('#developer_long').val();
2129        securityprofile = $('#security-profile').val();
2130        upload_picture = $('#upload_picture').val();
2131        profile_image_url = $('#profile-image').attr('data-profileurl');
2132        profile_image_url_small = $('#profile-image').attr('data-smallprofileurl');
2133        hubspot_api = $('#hubspot_api').val();
2134
2135        var acf_fields = wpestate_return_form_acf_fields('.field-group');
2136
2137        ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
2138        var nonce = jQuery('#wpestate_update_profile_nonce').val();
2139        $('#profile_message').empty().append('<div class="login-alert">' + ajaxcalls_vars.saving + '<div>');
2140        window.scrollTo(0, 0);
2141        $.ajax({
2142            type: 'POST',
2143            url: ajaxurl,
2144            data: {
2145                'action': 'wpestate_ajax_update_profile_developer',
2146                'developer_name': developer_name,
2147                'useremail': useremail,
2148                'userphone': userphone,
2149                'usermobile': usermobile,
2150                'usertitle': usertitle,
2151                'description': description,
2152                'upload_picture': upload_picture,
2153                'security-profile': securityprofile,
2154                'profile_image_url': profile_image_url,
2155                'profile_image_url_small': profile_image_url_small,
2156                'userfacebook': userfacebook,
2157                'usertwitter': usertwitter,
2158                'userlinkedin': userlinkedin,
2159                'userpinterest': userpinterest,
2160                'userinstagram': userinstagram,
2161                'userurl': userurl,
2162                'developer_languages': developer_languages,
2163                'developer_website': developer_website,
2164                'developer_taxes': developer_taxes,
2165                'developer_license': developer_license,
2166                'developer_category_submit': developer_category_submit,
2167                'developer_action_submit': developer_action_submit,
2168                'developer_city': developer_city,
2169                'developer_county': developer_county,
2170                'developer_area': developer_area,
2171                'developer_address': developer_address,
2172                'developer_lat': developer_lat,
2173                'developer_long': developer_long,
2174                'hubspot_api': hubspot_api,
2175                'useryoutube':useryoutube,
2176                'usertiktok':usertiktok,
2177                'usertelegram':usertelegram,
2178                'uservimeo':uservimeo,
2179                'developer_private_notes':developer_private_notes,
2180                'acf_fields': JSON.stringify(acf_fields),
2181                'security': nonce
2182            },
2183            success: function (data) {
2184                $('#profile_message').empty().append('<div class="login-alert">' + data + '<div>');
2185                window.scrollTo(0, 0);
2186            },
2187            error: function (errorThrown) {
2188            }
2189        });
2190    });
2191
2192    //delete profile
2193
2194    $('#delete_profile').on('click', function (event) {
2195        var ajaxurl;
2196        ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
2197        var nonce = jQuery('#wpestate_update_profile_nonce').val();
2198        var result = confirm(ajaxcalls_vars.delete_account);
2199        if (result) {
2200            //Logic to delete the item
2201
2202            $.ajax({
2203                type: 'POST',
2204                url: ajaxurl,
2205                data: {
2206                    'action': 'wpestate_delete_profile',
2207                    'security': nonce,
2208                },
2209                success: function (data) {
2210                    window.location = '/';
2211                },
2212                error: function (errorThrown) {
2213
2214                }
2215            });
2216        }
2217    });
2218
2219    //end delete profile
2220
2221}); // end ready jquery
2222//End ready ********************************************************************
2223
2224
2225/**
2226 * Handles the loading of agency/developer listings via AJAX.
2227 * This function is triggered when a user clicks on a term bar item.
2228 */
2229function wpestate_load_agency_developer_listings() {
2230    jQuery('.term_bar_item, .listing_load_more').on('click', function (event) {
2231       
2232       
2233        // Select relevant DOM elements
2234        var $this = jQuery(this);
2235        var $listingParent = $this.parents('.single_listing_block');
2236        var listings_per_page= $listingParent.attr('data-listings_per_page');
2237        var rownumber= $listingParent.attr('data-rownumber');
2238
2239        var $loadMoreButton = jQuery('.load_more_ajax_cont .listing_load_more', $listingParent);
2240        var $agencyListingsWrapper = jQuery('.agency_listings_wrapper');
2241        var $listingLoader = jQuery('#listing_loader');
2242        var $termBarWrapper = jQuery('.term_bar_wrapper');
2243        var $areadyLoadedItems = jQuery('.listing_wrapper', $listingParent).length;
2244     
2245        var $page_with_sidebar = 0;
2246        if(jQuery('#primary_sidebar_wrapper').length > 0){
2247            $page_with_sidebar = 1;
2248        }
2249
2250        // Display load more button
2251        $loadMoreButton.fadeIn();
2252
2253
2254        // Perform AJAX request
2255        if(!$this.hasClass('listing_load_more')){
2256            //$agencyListingsWrapper.empty();
2257            $areadyLoadedItems=0;
2258
2259            // Update active term styling
2260            jQuery('.term_bar_item').removeClass('active_term');
2261            $this.addClass('active_term');
2262         
2263        }
2264
2265
2266        
2267        
2268        var $active_tab = jQuery('.active_term', $listingParent);
2269        var term_name = jQuery($active_tab).attr('data-term_name');
2270
2271
2272       
2273        // Prepare AJAX data
2274        var ajaxData = {
2275            action: 'wpestate_agency_listings',
2276            listings_per_page:listings_per_page,
2277            rownumber:rownumber,
2278            term_name: $this.attr('data-term_name'),
2279            agency_id: $termBarWrapper.attr('data-agency_id'),
2280            agent_id: $termBarWrapper.attr('data-agent_id'),
2281            post_id: $termBarWrapper.attr('data-post_id'),
2282            term_name: term_name,
2283            loaded: $areadyLoadedItems,
2284            is_agency: jQuery('.single-estate_agency').length > 0 ? 1 : 0,
2285            page_with_sidebar: $page_with_sidebar,
2286            security: jQuery('#wpestate_developer_listing_nonce').val()
2287        };
2288
2289     
2290
2291
2292
2293        const listingContainer = jQuery('.agency_listings_wrapper');
2294        
2295        if(!$this.hasClass('listing_load_more')){
2296            wpestate_createSkeletons(listingContainer);
2297        }else{
2298             wpestate_createSkeletons(listingContainer,true);
2299        }
2300
2301       // $listingLoader.show();
2302
2303        jQuery.ajax({
2304            type: 'POST',
2305            url: ajaxcalls_vars.admin_url + 'admin-ajax.php',
2306            data: ajaxData,
2307            dataType: 'json',
2308            success: function (response) {
2309                $listingLoader.hide();
2310                if (response.success) {
2311                    
2312                    if(response.data.found_posts > 0){
2313                        if(!$this.hasClass('listing_load_more')){
2314                            //$agencyListingsWrapper.html(response.data.html);
2315                            wpestate_replaceSkeletons(listingContainer, response.data.html);
2316   
2317                        }else{
2318             
2319                            //$agencyListingsWrapper.append(response.data.html);
2320                            wpestate_replaceSkeletons(listingContainer,response.data.html);
2321
2322                        }
2323              
2324                        wpestate_restart_js_after_ajax();
2325                    }else{
2326                        //$agencyListingsWrapper.append(response.data.html);
2327                        wpestate_replaceSkeletons(listingContainer,response.data.html);
2328                        if($this.hasClass('listing_load_more')){
2329                            $this.hide();
2330                        }
2331                    }
2332
2333
2334
2335                } else {
2336                    $agencyListingsWrapper.html('<p>' + response.data.message + '</p>');
2337                }
2338            },
2339            error: function (jqXHR, textStatus, errorThrown) {
2340                console.error('AJAX request failed:', textStatus, errorThrown);
2341                $listingLoader.hide();
2342                $agencyListingsWrapper.html('<p>Error loading listings. Please try again.</p>');
2343            }
2344        });
2345    });
2346}
2347 
2348
2349function wpestate_add_to_favorites() {
2350
2351
2352    jQuery('#add_favorites').on('click', function (event) {
2353
2354        var post_id, ajaxurl;
2355        post_id = jQuery('#add_favorites').attr('data-postid');
2356        var icon = jQuery('#add_favorites');
2357        ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
2358        var nonce = jQuery('#wpestate_ajax_filtering').val();
2359        var use_favorite_login  =   ajaxcalls_vars.favorites_login;
2360        if(use_favorite_login=="no"){
2361            wpestate_add_remove_favorite_local_storage_single(icon,post_id);
2362            return;
2363        }
2364
2365
2366
2367        if (parseInt(ajaxcalls_vars.userid, 10) === 0) {
2368            if (!Modernizr.mq('only all and (max-width: 768px)')) {
2369                jQuery('#modal_login_wrapper').show();
2370                 jQuery('#modal_login_wrapper').find('[autofocus]').focus();
2371                jQuery('.loginpop').val('1');
2372            } else {
2373                jQuery('.mobile-trigger-user').trigger('click');
2374            }
2375
2376        } else {
2377            jQuery('#add_favorites').text(ajaxcalls_vars.saving);
2378            jQuery.ajax({
2379                type: 'POST',
2380                url: ajaxurl,
2381                dataType: 'json',
2382                data: {
2383                    'action': 'wpestate_ajax_add_fav',
2384                    'post_id': post_id,
2385                    'security': nonce
2386                },
2387                success: function (data) {
2388                    if (data.added) {
2389                        jQuery('#add_favorites').removeClass('isnotfavorite').addClass('isfavorite').attr('title', ajaxcalls_vars.remove_favorite);
2390                        jQuery('#add_favorites').html('<i class="fas fa-heart"></i>' + ajaxcalls_vars.favorite);
2391
2392                        // Refresh the Bootstrap tooltip if it exists
2393                        if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
2394                            var tooltipInstance = bootstrap.Tooltip.getInstance(icon[0]);
2395                            if (tooltipInstance) {
2396                                tooltipInstance.setContent({
2397                                    '.tooltip-inner': ajaxcalls_vars.remove_fav
2398                                });
2399                            }
2400                        }
2401
2402
2403                    } else {
2404                        jQuery('#add_favorites').removeClass('isfavorite').addClass('isnotfavorite').attr('title', ajaxcalls_vars.add_favorite);
2405                        jQuery('#add_favorites').html('<i class="far fa-heart"></i>' + ajaxcalls_vars.favorite);
2406
2407                        // Refresh the Bootstrap tooltip if it exists
2408                        if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
2409                            var tooltipInstance = bootstrap.Tooltip.getInstance(icon[0]);
2410                            if (tooltipInstance) {
2411                                tooltipInstance.setContent({
2412                                    '.tooltip-inner': ajaxcalls_vars.remove_fav
2413                                });
2414                            }
2415                        }
2416
2417                    }
2418                },
2419                error: function (errorThrown) {
2420                }
2421            }); //end ajax
2422        }// end check login
2423    });
2424}
2425
2426///////////////////////////////////////////////////////////////////////////////////////////
2427/////// Property page  + ajax call on contact
2428///////////////////////////////////////////////////////////////////////////////////////////
2429function wpestate_theme_slider_show_contact() {
2430    jQuery('.wpestate_theme_slider_contact_agent').on('click', function () {
2431        var acesta = jQuery(this).parent().parent();
2432        acesta.find('#show_contact').text(ajaxcalls_vars.contact_agent);
2433        acesta.find('.theme_slider_contact_form_wrapper').show('0').toggleClass('theme_slider_contact_form_wrapper_visible');
2434        jQuery('#estate-carousel').carousel('pause');
2435      
2436    });
2437
2438
2439
2440    jQuery('.theme_slider_details_modal_close').on('click', function () {
2441        jQuery(this).parent().removeClass('theme_slider_contact_form_wrapper_visible');
2442                jQuery('#estate-carousel').carousel('cycle');
2443    });
2444
2445    jQuery('.theme_slider_contact_form_wrapper').on('click', function (event) {
2446        event.stopPropagation();
2447    });
2448
2449
2450}
2451
2452
2453/**
2454 * Enable Schedule Contact Functionality
2455 *
2456 * This function sets up event handlers for the schedule meeting functionality
2457 * and initializes the datepicker for schedule day selection.
2458 *
2459 * @package WpResidence
2460 * @subpackage PropertyDetails
2461 * @since 3.0.3
2462 *
2463 * Dependencies:
2464 * - jQuery
2465 * - jQuery UI Datepicker
2466 *
2467 * Usage: Call this function after DOM is ready and dependencies are loaded.
2468 */
2469
2470function wpestate_enable_schedule_contact() {
2471    // Toggle schedule wrapper visibility when clicking on schedule meeting button
2472    jQuery('.schedule_meeting').on('click', function (event) {
2473        var parent = jQuery(this).parent().parent();
2474        var scheduleWrapper = parent.find('.schedule_wrapper');
2475       
2476        scheduleWrapper.slideToggle(400, function() {
2477            scheduleWrapper.css('display', scheduleWrapper.is(':visible') ? 'flex' : 'none');
2478        });
2479    });
2480    
2481
2482    
2483    // Initialize datepicker for schedule day input with position correction
2484    jQuery(".schedule_day, #schedule_day").datepicker({
2485        dateFormat: "yy-mm-dd",
2486        beforeShow: function(input, inst) {
2487            // Position correction
2488            setTimeout(function() {
2489                inst.dpDiv.css({
2490                    position: 'absolute',
2491                    top: jQuery(input).offset().top + jQuery(input).outerHeight(),
2492                    left: jQuery(input).offset().left
2493                });
2494            }, 0);
2495        }
2496    }).datepicker('widget').wrap('<div class="ll-skin-melon"/>');
2497}
2498
2499
2500function wpestate_agent_submit_email() {
2501    jQuery('.wpresidence_elementor_form').on('submit', function (event) {
2502        event.preventDefault();
2503        var form_submit = jQuery('.wpresidence_elementor_form').find('.agent_submit_class_elementor');
2504        process_form_submit(form_submit);
2505    });
2506
2507    jQuery('.agent_submit_class').on('click', function (event) {
2508        process_form_submit(jQuery(this));
2509    });
2510    
2511    jQuery('.message_submit').on('click', function (event) {
2512        process_form_submit(jQuery(this));
2513    });
2514}
2515
2516
2517function process_form_submit(form_submit) {
2518
2519    var parent, contact_name, contact_email, contact_phone, contact_coment, agent_id, property_id, nonce, ajaxurl;
2520    parent          = form_submit.closest('.wpestate_contact_form_parent');
2521
2522    var is_private_message='no';
2523    if(form_submit.hasClass('message_submit')){
2524        is_private_message='yes';
2525    }
2526    
2527
2528    contact_name    = parent.find('#agent_contact_name').val();
2529    contact_email   = parent.find('#agent_user_email').val();
2530    contact_phone   = parent.find('#agent_phone').val();
2531    contact_intent  = parent.find('#agent_contact_intent input[name="contact_intent"]').val() || '';
2532    contact_coment  = parent.find('#agent_comment').val();
2533    agent_id        = parent.find('#agent_id').val();
2534    property_id     = parent.find('#agent_property_id').val();
2535    
2536    nonce   = parent.find('#agent_property_ajax_nonce').val();
2537    ajaxurl = ajaxcalls_vars.admin_url + 'admin-ajax.php';
2538    
2539    var schedule_day = parent.find('.schedule_day').val();
2540    var schedule_hour = parent.find('.schedule_hour').val();
2541    var schedule_mode ='in_person';
2542
2543
2544     
2545    if( parent.hasClass('wpestate_schedule_tour_wrapper_content') || 
2546        parent.hasClass('wpestate_shedule_tour_wrapper_type2') ||
2547        parent.hasClass('wpestate_schedule_tour_wrapper') ){
2548
2549        schedule_day    =   parent.find('.shedule_day_option_selected').attr('data-scheudle-day');
2550        schedule_hour   =   parent.find('#wpestate_schedule_tour_time').val();
2551
2552        var schedule_mode_button   =   parent.find('.shedule_option_selected');
2553        if( schedule_mode_button.hasClass('schedule_video_chat') ){
2554            schedule_mode ='video_chat';
2555        }       
2556    }
2557
2558
2559    var is_elementor = 0;
2560    if (form_submit.parents('.wpresidence_elementor_form').length) {
2561        parent                  =   form_submit.parents('.wpresidence_elementor_form');
2562        contact_name            =   parent.find('#form-field-name').val();
2563        contact_email           =   parent.find('#form-field-email').val();
2564        contact_coment          =   parent.find('#form-field-message').val();
2565        contact_phone           =   parent.find('input[name="phone"]').val();
2566        nonce                   =   parent.find('#agent_property_ajax_nonce').val();
2567        is_elementor            =   parent.find('#contact_form_elementor').val();
2568        var wpresidence_form_id =   parent.find('#wpresidence_form_id').val();
2569        var elementor_email_subject = parent.find('#elementor_email_subject').val();
2570
2571        var temp_details;
2572        temp_details        = '';
2573        var elementor_form  = form_submit.parents('.wpresidence_elementor_form');
2574        var form_items      = elementor_form.find('.elementor-field');
2575        var label_name      ='';
2576        var labelElement;
2577        form_items.each(function () {
2578
2579            labelElement=jQuery(this).parent().find('.elementor-field-label');
2580
2581            if (labelElement.length > 0) {
2582                label_name = labelElement.text();
2583                label_name = label_name.replace('*', '');
2584            } else {
2585                // Convert field ID to human-readable name (e.g. inquiry_type → Inquiry Type)
2586                // Some .elementor-field elements (e.g. dropdown buttons) have no name attribute
2587                label_name = jQuery(this).attr('name') || '';
2588                label_name = label_name.replace(/_/g, ' ').replace(/\b\w/g, function(c){ return c.toUpperCase(); });
2589            }
2590
2591            // Skip fields with no label and no value to avoid stray ":" in email
2592            var field_val = jQuery(this).val() || '';
2593            label_name = label_name.trim();
2594            if (label_name === '' && field_val === '') {
2595                return;
2596            }
2597
2598            temp_details = temp_details + label_name + ": " + field_val + "/n";
2599        });
2600
2601        contact_coment = temp_details ;
2602
2603    }
2604
2605    if (parent.find('.wpestate_agree_gdpr').length > 0) {
2606        if (!parent.find('.wpestate_agree_gdpr').is(':checked')) {
2607            parent.find('#alert-agent-contact').empty().append(ajaxcalls_vars.gdpr_terms);
2608            return;
2609        }
2610    }
2611
2612    // Validate required fields — check all inputs/textareas with data-required attribute
2613    // We use data-required instead of HTML5 required to show custom per-field messages
2614    var validationFailed = false;
2615    var firstInvalidField = null;
2616
2617    parent.find('[data-required="true"]').each(function () {
2618        var field = jQuery(this);
2619        var fieldValue = field.val();
2620
2621        // Check if empty (trim whitespace for text inputs)
2622        if (!fieldValue || fieldValue.trim() === '') {
2623            // Get the custom validation message from data attribute, or fall back to generic
2624            var customMessage = field.data('validation-message') || 'This field is required';
2625
2626            // Show the error message in the alert area
2627            if (!validationFailed) {
2628                parent.find('#alert-agent-contact').empty().append(customMessage);
2629                firstInvalidField = field;
2630            }
2631            validationFailed = true;
2632
2633            // Highlight the field group with red border error styling
2634            field.closest('.elementor-field-group').addClass('elementor-field-error');
2635        } else {
2636            // Remove error highlight if field is filled
2637            field.closest('.elementor-field-group').removeClass('elementor-field-error');
2638        }
2639    });
2640
2641    // Stop submission if any required field is empty
2642    if (validationFailed) {
2643        // Bind one-time listeners to clear red border when user starts filling a field
2644        parent.find('.elementor-field-error').each(function () {
2645            var fieldGroup = jQuery(this);
2646
2647            // Clear error on text input/textarea focus or typing
2648            fieldGroup.find('input, textarea').one('input focus', function () {
2649                fieldGroup.removeClass('elementor-field-error');
2650            });
2651
2652            // Clear error on dropdown selection (hidden input value change)
2653            fieldGroup.find('input[type="hidden"]').one('change', function () {
2654                fieldGroup.removeClass('elementor-field-error');
2655            });
2656        });
2657        return;
2658    }
2659
2660    parent.find('#alert-agent-contact').empty().append(ajaxcalls_vars.sending);
2661
2662    jQuery.ajax({
2663        type: 'POST',
2664        dataType: 'json',
2665        url: ajaxurl,
2666        data: {
2667            'action': 'wpestate_ajax_agent_contact_form',
2668            'name': contact_name,
2669            'email': contact_email,
2670            'phone': contact_phone,
2671            'contact_intent': contact_intent,
2672            'comment': contact_coment,
2673            'agent_id': agent_id,
2674            'propid': property_id,
2675            'schedule_day': schedule_day,
2676            'schedule_hour': schedule_hour,
2677            'schedule_mode':schedule_mode,
2678            'elementor_wpresidence_form_id': wpresidence_form_id,
2679            'elementor_email_subject': elementor_email_subject,
2680            'is_elementor': is_elementor,
2681            'is_private_message':is_private_message,
2682            'nonce': nonce
2683        },
2684        success: function (data) {
2685
2686            if (data.sent) {
2687                parent.find('#agent_contact_name').val('');
2688                parent.find('#agent_user_email').val('');
2689                parent.find('#agent_phone').val('');
2690                parent.find('#agent_comment').val('');
2691                parent.find('#agent_contact_intent input[name="contact_intent"]').val('');
2692                parent.find('#agent_contact_intent .dropdown-toggle').text(parent.find('#agent_contact_intent').data('placeholder') || '');
2693                parent.find('.schedule_day').val('');
2694                parent.find('#schedule_hour').val('');
2695                parent.find('#alert-agent-contact').addClass(' wpestate-agent-contact-sent ').empty().append(data.response);
2696
2697
2698                if (typeof (form_items) !== 'undefined') {
2699                    form_items.each(function () {
2700                        jQuery(this).val('');
2701                    });
2702                }
2703
2704                // Redirect after form submission if a redirect URL is configured
2705                var redirectUrl = parent.find('input[name="redirect_url"]').val();
2706                if (redirectUrl && redirectUrl.length > 0) {
2707                    setTimeout(function() {
2708                        window.location.href = redirectUrl;
2709                    }, 1000);
2710                }
2711
2712            } else {
2713                parent.find('#alert-agent-contact').empty().append(data.response);
2714            }
2715
2716        },
2717        error: function (errorThrown) {
2718
2719        }
2720    });
2721
2722
2723
2724    jQuery.ajax({
2725        type: 'POST',
2726        //  dataType: 'json',
2727        url: ajaxurl,
2728        data: {
2729            'action': 'wpestate_ajax_agent_contact_form_forcrm',
2730            'name': contact_name,
2731            'email': contact_email,
2732            'phone': contact_phone,
2733            'contact_intent': contact_intent,
2734            'comment': contact_coment,
2735            'agent_id': agent_id,
2736            'propid': property_id,
2737            'schedule_day': schedule_day,
2738            'schedule_hour': schedule_hour,
2739            'schedule_mode':schedule_mode,
2740            'nonce': nonce
2741        },
2742        success: function (data) {
2743        },
2744        error: function (errorThrown) {
2745
2746        }
2747    });
2748
2749
2750}
2751
2752
2753///////////////////////////////////////////////////////////////////////////////////////////
2754/////// Property page  + ajax call on contact
2755///////////////////////////////////////////////////////////////////////////////////////////
2756
2757
2758function wpestate_enable_share_unit(){
2759   
2760    jQuery('.share_list').unbind('click');
2761    jQuery('.share_list').on( 'click', function(event) {
2762        event.stopPropagation();   
2763        var sharediv=jQuery(this).parent().find('.share_unit');
2764        sharediv.toggle();
2765        jQuery(this).toggleClass('share_on');
2766    });
2767    
2768}
2769
2770function setCookie(cname, cvalue, exdays) {
2771    const d = new Date();
2772    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
2773    let expires = "expires="+d.toUTCString();
2774    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
2775  }
2776
2777
2778function wpestate_enable_not_logged_favorites(){
2779   
2780    var use_favorite_login  =   ajaxcalls_vars.favorites_login;
2781
2782    if(use_favorite_login=="yes"){
2783        return;
2784    }
2785
2786    var current_favorites = window.localStorage.getItem('wpestate_favorites');
2787    setCookie('wpestate_favorites',current_favorites,10);
2788    var prop_ids='';
2789
2790    if( current_favorites!==null ){
2791        prop_ids = jQuery.map(current_favorites.split(','), function(value){
2792            return parseInt(value);
2793        });
2794    }
2795  
2796    jQuery('.icon-fav').each(function(){
2797        var icon=jQuery(this);
2798        var property_id=icon.attr('data-postid');
2799        property_id=parseInt(property_id);
2800        var index = prop_ids.indexOf( property_id );    
2801
2802        if (index > -1) {
2803            icon.removeClass('icon-fav-off').addClass('icon-fav-on');
2804            icon.attr('title', ajaxcalls_vars.remove_fav);
2805    
2806        }else{
2807            icon.removeClass('icon-fav-on').addClass('icon-fav-off');
2808            icon.attr('title', ajaxcalls_vars.add_favorite);
2809    
2810        }
2811    })
2812    
2813
2814    if(jQuery('#add_favorites').length>0){
2815        var property_id=jQuery('#add_favorites').attr('data-postid');
2816        property_id=parseInt(property_id);
2817        var index = prop_ids.indexOf( property_id );    
2818       
2819        if (index > -1) {
2820            jQuery('#add_favorites').removeClass('isnotfavorite').addClass('isfavorite').attr('title', ajaxcalls_vars.remove_favorite);
2821            jQuery('#add_favorites').html('<i class="fas fa-heart"></i>' + ajaxcalls_vars.favorite);
2822    
2823        }  
2824    }
2825    
2826}
2827
2828
2829
2830/**
2831 * Returns ACF fields from a given container.
2832 *
2833 * This function collects input, textarea, and select elements within the specified container
2834 * and returns an Array containing their values, formatted according to ACF field naming conventions.
2835 *
2836 * @param {string} container - The CSS selector for the container to search within.
2837 * @returns {Array} acf_fields - An Array containing ACF field names and their corresponding values.
2838 */
2839function wpestate_return_form_acf_fields( container = '') {
2840
2841    var acf_fields = {};
2842
2843    if ( !jQuery(container).length ) {
2844        return acf_fields;
2845    }
2846
2847    jQuery(container).find('input, textarea, select').each(function() {
2848        var $field = jQuery(this);
2849        var fieldName = $field.attr('name');
2850        
2851        if (!fieldName || !fieldName.startsWith('acf_fields[')) {
2852            return;
2853        }
2854        
2855        // Extract field name from acf_fields[field_name] format
2856        var matches = fieldName.match(/acf_fields\[([^\]]+)\]/);
2857        if (!matches) {
2858            return;
2859        }
2860        
2861        var cleanFieldName = matches[1];
2862        
2863        if ($field.is(':checkbox')) {
2864            // Handle checkboxes
2865            if ($field.attr('name').includes('[]')) {
2866                // Multiple checkboxes (checkbox field type)
2867                if (!acf_fields[cleanFieldName]) {
2868                    acf_fields[cleanFieldName] = [];
2869                }
2870                if ($field.is(':checked')) {
2871                    acf_fields[cleanFieldName].push($field.val());
2872                }
2873            } else {
2874                // Single checkbox (true/false field type)
2875                acf_fields[cleanFieldName] = $field.is(':checked') ? $field.val() : '';
2876            }
2877        } else if ($field.is(':radio')) {
2878            // Handle radio buttons
2879            if ($field.is(':checked')) {
2880                acf_fields[cleanFieldName] = $field.val();
2881            }
2882        } else {
2883            // Handle other input types
2884            acf_fields[cleanFieldName] = $field.val();
2885        }
2886    });
2887    return acf_fields;
2888}
2889
2890
2891/**
2892 * Bootstrap dropdown click handler for Elementor contact form fields.
2893 * Uses the same <li role="presentation" data-value="..."> pattern as search form dropdowns.
2894 * When a dropdown <li> is clicked, updates the sibling hidden input value
2895 * and changes the dropdown button text to reflect the selection.
2896 */
2897jQuery(document).on('click', '.wpresidence_elementor_form .wpresidence-dropdown-field .filter_menu li', function(e) {
2898    e.preventDefault();
2899
2900    var selectedValue = jQuery(this).data('value');
2901    var selectedText  = jQuery(this).text();
2902
2903    // Find the dropdown wrapper that contains this <li>
2904    var dropdownWrapper = jQuery(this).closest('.wpresidence-dropdown-field');
2905
2906    // If "all" is selected, clear the hidden input value
2907    if (selectedValue === 'all') {
2908        dropdownWrapper.find('input[type="hidden"].elementor-field').val('').trigger('change');
2909    } else {
2910        dropdownWrapper.find('input[type="hidden"].elementor-field').val(selectedValue).trigger('change');
2911    }
2912
2913    // Update the dropdown button text to show the current selection
2914    dropdownWrapper.find('.dropdown-toggle').text(selectedText);
2915});
2916
2917
2918/**
2919 * Bootstrap dropdown click handler for contact intent fields.
2920 * Updates the hidden input value and button text when user selects an option.
2921 */
2922jQuery(document).on('click', '.contact_intent_dropdown_wrapper .filter_menu li', function(e) {
2923    e.preventDefault();
2924
2925    var selectedValue = jQuery(this).data('value');
2926    var selectedText  = jQuery(this).text();
2927    var wrapper       = jQuery(this).closest('.contact_intent_dropdown_wrapper');
2928
2929    // Update hidden input value and button label
2930    wrapper.find('input[name="contact_intent"]').val(selectedValue).trigger('change');
2931    wrapper.find('.dropdown-toggle').text(selectedText);
2932});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.