From acd606bb0313f6c98fffc02eca7c371a9dfdcf94 Mon Sep 17 00:00:00 2001 From: "Hanson.xyz Dev" Date: Tue, 16 Dec 2025 01:27:44 -0600 Subject: [PATCH] Sync property list with map viewport Major changes: - Property list now updates when map pans/zooms - Properties sorted by distance from map center (closest first) - Shows "X properties in view" when viewport filtering active - Min 30 properties required before grouping kicks in - Added rule to CLAUDE.md: no commits unless asked Backend: - MLS_Query: Added bounds filtering and distance-based sorting - AJAX handler: Accepts bounds/center, sorts by distance when provided Frontend: - Map move triggers property list refresh with same viewport - Loop prevention flag to avoid map->filter->map recursion - Resets to page 1 when viewport changes Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CLAUDE.md | 1 + .../includes/class-mls-cluster.php | 11 +++ .../includes/class-mls-query.php | 70 ++++++++++++++---- .../themes/homeproz/dist/assets/main.js | 2 +- .../themes/homeproz/inc/ajax-handlers.php | 31 +++++++- .../property/property-filters.js | 73 ++++++++++++++----- 6 files changed, 151 insertions(+), 37 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 97f711ac..d686d097 100755 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,6 +12,7 @@ Custom WordPress theme for HomeProz Real Estate (Albert Lea, MN). Dark/rust bran 6. **Server-side render** - no client-side templating 7. **No custom animations** - keep it static and fast 8. **ASK before architectural decisions** +9. **No git commits unless asked** - commits are for checkpoints before major work or major milestones, not for small single-file changes ## Build diff --git a/wp-content/plugins/mls-by-hansonxyz/includes/class-mls-cluster.php b/wp-content/plugins/mls-by-hansonxyz/includes/class-mls-cluster.php index eafba1cc..d4ba1336 100644 --- a/wp-content/plugins/mls-by-hansonxyz/includes/class-mls-cluster.php +++ b/wp-content/plugins/mls-by-hansonxyz/includes/class-mls-cluster.php @@ -39,6 +39,12 @@ class MLS_Cluster { */ const MAX_INDIVIDUAL_MARKERS = 500; + /** + * Minimum properties before any grouping kicks in + * Below this, always show individual markers + */ + const MIN_FOR_GROUPING = 30; + /** * Zoom thresholds for visualization modes */ @@ -270,6 +276,11 @@ class MLS_Cluster { $total = (int) $wpdb->get_var($count_sql); } + // If very few properties, always show individual markers (no grouping) + if ($total < self::MIN_FOR_GROUPING) { + return $this->get_individual_markers($where_sql, $values, $total); + } + // Calculate center latitude for Mercator adjustment $center_lat = 45.0; // Default Minnesota if ($args['bounds'] && count($args['bounds']) === 4) { diff --git a/wp-content/plugins/mls-by-hansonxyz/includes/class-mls-query.php b/wp-content/plugins/mls-by-hansonxyz/includes/class-mls-query.php index d6925f2e..90e94813 100644 --- a/wp-content/plugins/mls-by-hansonxyz/includes/class-mls-query.php +++ b/wp-content/plugins/mls-by-hansonxyz/includes/class-mls-query.php @@ -50,6 +50,8 @@ class MLS_Query { 'listing_key' => null, 'listing_id' => null, 'search' => null, // Search in address/remarks + 'bounds' => null, // Map bounds: array(sw_lat, sw_lng, ne_lat, ne_lng) + 'center' => null, // Map center for distance sort: array(lat, lng) 'limit' => 20, 'offset' => 0, 'orderby' => 'modification_timestamp', @@ -166,24 +168,51 @@ class MLS_Query { $values[] = $search_term; } + // Map bounds filtering + if ($args['bounds'] && is_array($args['bounds']) && count($args['bounds']) === 4) { + list($sw_lat, $sw_lng, $ne_lat, $ne_lng) = $args['bounds']; + $where[] = 'latitude BETWEEN %f AND %f'; + $where[] = 'longitude BETWEEN %f AND %f'; + $where[] = 'latitude IS NOT NULL'; + $where[] = 'longitude IS NOT NULL'; + $values[] = (float) $sw_lat; + $values[] = (float) $ne_lat; + $values[] = (float) $sw_lng; + $values[] = (float) $ne_lng; + } + $sql .= ' WHERE ' . implode(' AND ', $where); // ORDER BY - $allowed_orderby = array( - 'modification_timestamp', - 'list_price', - 'bedrooms_total', - 'bathrooms_total', - 'living_area', - 'year_built', - 'days_on_market', - 'city', - 'created_at', - ); + // If center provided, sort by distance from center + if ($args['center'] && is_array($args['center']) && count($args['center']) === 2) { + list($center_lat, $center_lng) = $args['center']; + // Haversine formula approximation for distance (good enough for sorting) + // Using squared Euclidean distance with latitude adjustment for speed + $lat_factor = cos(deg2rad((float) $center_lat)); + $sql .= $wpdb->prepare( + " ORDER BY (POW(latitude - %f, 2) + POW((longitude - %f) * %f, 2)) ASC", + (float) $center_lat, + (float) $center_lng, + $lat_factor + ); + } else { + $allowed_orderby = array( + 'modification_timestamp', + 'list_price', + 'bedrooms_total', + 'bathrooms_total', + 'living_area', + 'year_built', + 'days_on_market', + 'city', + 'created_at', + ); - $orderby = in_array($args['orderby'], $allowed_orderby) ? $args['orderby'] : 'modification_timestamp'; - $order = strtoupper($args['order']) === 'ASC' ? 'ASC' : 'DESC'; - $sql .= " ORDER BY {$orderby} {$order}"; + $orderby = in_array($args['orderby'], $allowed_orderby) ? $args['orderby'] : 'modification_timestamp'; + $order = strtoupper($args['order']) === 'ASC' ? 'ASC' : 'DESC'; + $sql .= " ORDER BY {$orderby} {$order}"; + } // LIMIT/OFFSET $sql .= ' LIMIT %d OFFSET %d'; @@ -378,6 +407,19 @@ class MLS_Query { $values[] = (int) $args['min_baths']; } + // Map bounds filtering + if (!empty($args['bounds']) && is_array($args['bounds']) && count($args['bounds']) === 4) { + list($sw_lat, $sw_lng, $ne_lat, $ne_lng) = $args['bounds']; + $where[] = 'latitude BETWEEN %f AND %f'; + $where[] = 'longitude BETWEEN %f AND %f'; + $where[] = 'latitude IS NOT NULL'; + $where[] = 'longitude IS NOT NULL'; + $values[] = (float) $sw_lat; + $values[] = (float) $ne_lat; + $values[] = (float) $sw_lng; + $values[] = (float) $ne_lng; + } + $sql = "SELECT COUNT(*) FROM {$table} WHERE " . implode(' AND ', $where); if (!empty($values)) { diff --git a/wp-content/themes/homeproz/dist/assets/main.js b/wp-content/themes/homeproz/dist/assets/main.js index 08e56f9f..1fca61df 100644 --- a/wp-content/themes/homeproz/dist/assets/main.js +++ b/wp-content/themes/homeproz/dist/assets/main.js @@ -1 +1 @@ -(function(a){var c=a(".menu-toggle"),n=a(".mobile-navigation");c.length&&(c.on("click",function(){var s=a(this).attr("aria-expanded")==="true";a(this).attr("aria-expanded",!s),n.toggleClass("is-open"),s?a("body").removeClass("mobile-menu-open"):a("body").addClass("mobile-menu-open")}),a(document).on("keydown",function(s){s.key==="Escape"&&n.hasClass("is-open")&&(c.attr("aria-expanded","false"),n.removeClass("is-open"),a("body").removeClass("mobile-menu-open"))}),a(document).on("click",function(s){n.hasClass("is-open")&&!a(s.target).closest(".mobile-navigation").length&&!a(s.target).closest(".menu-toggle").length&&(c.attr("aria-expanded","false"),n.removeClass("is-open"),a("body").removeClass("mobile-menu-open"))}))})(jQuery);(function(a){var c=6e3,n=1450,s=1e3,o=[],l=0,t=null,e=!1,i=!1,r=null;function u(){if(a(".Home_Page").length&&(r=a(".hero-split-image"),!!r.length)){var d=r.data("gallery-images");!d||!d.length||(o=d,h(),a(window).on("resize",b(h,150)))}}function h(){var d=a(window).width();d>=n?e||m():e&&f()}function m(){e=!0,i||(v(),i=!0),t=setInterval(y,c)}function f(){e=!1,t&&(clearInterval(t),t=null)}function v(){a.each(o,function(d,p){var g=new Image;g.src=p})}function y(){l=(l+1)%o.length;var d=o[l],p=a('
');p.css({position:"absolute",top:0,left:0,right:0,bottom:0,"background-image":"url("+d+")","background-size":"cover","background-position":"center center","background-repeat":"no-repeat",opacity:0,transform:"scale(1.02)",transition:"opacity "+s+"ms ease-in-out, transform "+s+"ms ease-in-out","z-index":1}),r.css("position","relative"),r.append(p),p[0].offsetHeight,p.css({opacity:1,transform:"scale(1)"}),setTimeout(function(){r.css("background-image","url("+d+")"),p.remove()},s)}function b(d,p){var g;return function(){var I=this,x=arguments;clearTimeout(g),g=setTimeout(function(){d.apply(I,x)},p)}}a(document).ready(u)})(jQuery);(function(a){var c={map:null,markers:{},densityLayer:null,clusterLayer:null,markerCluster:null,selectedPropertyId:null,hoveredPropertyId:null,baseZIndex:400,currentFilters:{},isLoading:!1,loadTimeout:null,currentMode:null,init:function(t){var e=a("#property-map");if(!(!e.length||typeof L>"u")){this.currentFilters=t||{},this.map=L.map("property-map").setView([45,-93.5],7),L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",{attribution:'© OpenStreetMap'}).addTo(this.map),this.densityLayer=L.layerGroup().addTo(this.map),this.clusterLayer=L.layerGroup().addTo(this.map),this.markerCluster=L.markerClusterGroup({maxClusterRadius:50,spiderfyOnMaxZoom:!0,showCoverageOnHover:!1,zoomToBoundsOnClick:!0,disableClusteringAtZoom:18,chunkedLoading:!0,chunkInterval:200,chunkDelay:50,iconCreateFunction:function(r){var u=r.getChildCount(),h="small";return u>=100?h="large":u>=10&&(h="medium"),L.divIcon({html:"
"+u+"
",className:"marker-cluster marker-cluster-"+h,iconSize:L.point(40,40)})}}),this.map.addLayer(this.markerCluster);var i=this;this.map.on("moveend zoomend",function(){i.loadClusters()}),this.bindCardHoverEvents(),this.loadClusters()}},loadClusters:function(){if(this.map){var t=this;clearTimeout(this.loadTimeout),this.loadTimeout=setTimeout(function(){t._doLoadClusters()},150)}},_doLoadClusters:function(){if(!this.isLoading){var t=this,e=this.map.getBounds(),i=this.map.getZoom();this.isLoading=!0;var r={action:"mls_get_clusters",zoom:i,bounds:[e.getSouthWest().lat,e.getSouthWest().lng,e.getNorthEast().lat,e.getNorthEast().lng],status:this.currentFilters.status||"Active",property_type:this.currentFilters.property_type||"",city:this.currentFilters.city||"",min_price:this.currentFilters.min_price||"",max_price:this.currentFilters.max_price||"",min_beds:this.currentFilters.min_beds||""};a.ajax({url:homeprozMapData.clusterEndpoint,type:"GET",data:r,success:function(u){if(u.success){var h=u.data;switch(t.currentMode=h.type,h.type){case"density":t.renderDensity(h.dots);break;case"clusters":t.renderClusters(h.clusters);break;case"markers":t.renderMarkers(h.markers);break}}},complete:function(){t.isLoading=!1}})}},clearAllLayers:function(){this.densityLayer.clearLayers(),this.clusterLayer.clearLayers(),this.markerCluster.clearLayers(),this.markers={}},renderDensity:function(t){this.clearAllLayers();var e=this,i=this.map.getZoom();t.forEach(function(r){var u=e.getDensityColor(r.count,i),h=e.getDensitySize(r.count,i),m=L.divIcon({html:'
',className:"density-dot-container",iconSize:[h,h],iconAnchor:[h/2,h/2]}),f=L.marker([r.lat,r.lng],{icon:m});f.on("click",function(){e.map.setView([r.lat,r.lng],e.map.getZoom()+2)}),f.bindTooltip(r.count+" properties",{className:"density-tooltip"}),e.densityLayer.addLayer(f)})},getDensityThreshold:function(t){return Math.max(40,Math.round(600/Math.pow(1.4,t-3)))},getDensityColor:function(t,e){var i=this.getDensityThreshold(e),r=t/i;return r>=1.5?"rgba(180, 83, 9, 0.8)":r>=1?"rgba(217, 119, 6, 0.8)":r>=.6?"rgba(245, 158, 11, 0.8)":r>=.3?"rgba(234, 179, 8, 0.8)":r>=.15?"rgba(132, 204, 22, 0.8)":"rgba(34, 197, 94, 0.8)"},getDensitySize:function(t,e){var i=this.getDensityThreshold(e),r=t/i;return r>=1.5?11:r>=1?10:r>=.6?8:r>=.3?7:6},renderClusters:function(t){this.clearAllLayers();var e=this;t.forEach(function(i){var r="small";i.count>=100?r="large":i.count>=10&&(r="medium");var u=L.divIcon({html:"
"+i.count+"
",className:"marker-cluster marker-cluster-"+r+" server-cluster",iconSize:L.point(40,40)}),h=L.marker([i.lat,i.lng],{icon:u});h.on("click",function(){e.map.setView([i.lat,i.lng],e.map.getZoom()+2)});var m="$"+e.formatNumber(i.min_price);i.max_price!==i.min_price&&(m+=" - $"+e.formatNumber(i.max_price)),h.bindTooltip(i.count+" properties
"+m,{className:"cluster-tooltip"}),e.clusterLayer.addLayer(h)})},renderMarkers:function(t){this.clearAllLayers(),this.selectedPropertyId=null,this.hoveredPropertyId=null,a(".property-card").removeClass("property-card-highlighted");var e=this,i=[];t.forEach(function(r,u){if(r.lat&&r.lng){var h=L.marker([r.lat,r.lng],{icon:e.createIcon("red"),zIndexOffset:e.baseZIndex+u});h.propertyId=r.id,h.defaultZIndex=e.baseZIndex+u,h.bindPopup('
'+r.price+"
"+r.address+'
View Details
'),h.on("click",function(m){e.onMarkerClick(r.id)}),i.push(h),e.markers[r.id]=h}}),this.markerCluster.addLayers(i)},updateFilters:function(t){this.currentFilters=t||{},this.loadClusters()},formatNumber:function(t){return t.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")},createIcon:function(t){return t=t||"red",L.divIcon({className:"property-marker property-marker-"+t,html:'
',iconSize:[17,22],iconAnchor:[8,22],popupAnchor:[0,-22]})},onMarkerClick:function(t){var e=this;if(this.selectedPropertyId!==t){this.selectedPropertyId&&(this.setMarkerColor(this.selectedPropertyId,"red"),this.resetMarkerZIndex(this.selectedPropertyId),a("#property-"+this.selectedPropertyId).removeClass("property-card-highlighted")),this.selectedPropertyId=t,this.setMarkerColor(t,"amber"),this.setMarkerZIndex(t,1e4);var i=a("#property-"+t);if(i.length){var r=i.offset().top,u=r+i.outerHeight(),h=a(window).scrollTop(),m=h+a(window).height(),f=r>=h&&u<=m;f?e.flashCard(i):a("html, body").animate({scrollTop:r-120},400,function(){e.flashCard(i)})}}},flashCard:function(t){t.removeClass("property-card-highlighted"),setTimeout(function(){t.addClass("property-card-highlighted"),setTimeout(function(){t.removeClass("property-card-highlighted"),setTimeout(function(){t.addClass("property-card-highlighted")},150)},150)},50)},setMarkerColor:function(t,e){var i=this.markers[t];i&&i.setIcon(this.createIcon(e))},setMarkerZIndex:function(t,e){var i=this.markers[t];i&&i.setZIndexOffset(e)},resetMarkerZIndex:function(t){var e=this.markers[t];e&&e.setZIndexOffset(e.defaultZIndex)},bindCardHoverEvents:function(){var t=this;a(document).on("mouseenter",".property-card[data-property-id]",function(){var e=a(this).data("property-id");e!==t.selectedPropertyId&&(t.hoveredPropertyId=e,t.setMarkerColor(e,"blue"),t.setMarkerZIndex(e,9e3))}),a(document).on("mouseleave",".property-card[data-property-id]",function(){var e=a(this).data("property-id");e!==t.selectedPropertyId&&(t.hoveredPropertyId===e&&(t.hoveredPropertyId=null),t.setMarkerColor(e,"red"),t.resetMarkerZIndex(e))})}},n={$form:null,$results:null,$filters:null,isFirstLoad:!0,isLoading:!1,init:function(){this.$form=a(".filters-form"),this.$results=a("#property-results"),this.$filters=a("#property-filters"),!(!this.$form.length||!this.$results.length)&&(this.bindEvents(),this.initFromUrl())},bindEvents:function(){var t=this;this.$form.on("submit",function(e){e.preventDefault(),t.filterProperties(1)}),this.$form.find("select").on("change",function(){t.filterProperties(1)}),a(".filters-reset").on("click",function(e){e.preventDefault(),t.resetFilters()}),this.$results.on("click",".pagination a",function(e){e.preventDefault();var i=t.getPageFromUrl(a(this).attr("href"));t.filterProperties(i)}),a(window).on("hashchange",function(){var e=t.getPageFromHash();t.filterProperties(e,!1)})},initFromUrl:function(){var t=new URLSearchParams(window.location.search);this.$form.find("select").each(function(){var i=a(this).attr("name");t.has(i)&&a(this).val(t.get(i))});var e=this.getPageFromHash();e>1&&this.filterProperties(e,!1)},getPageFromHash:function(){var t=window.location.hash,e=t.match(/#page=(\d+)/);return e?parseInt(e[1]):1},filterProperties:function(t,e){if(!this.isLoading){e=e!==!1,t=t||1;var i=this,r=this.getFormData();this.isLoading=!0,this.$filters.addClass("is-loading"),this.isFirstLoad&&this.$results.html('
'),a.ajax({url:homeprozAjax.ajaxUrl,type:"POST",data:{action:"homeproz_filter_properties",nonce:homeprozAjax.nonce,property_type:r.property_type,property_location:r.property_location,min_price:r.min_price,max_price:r.max_price,beds:r.beds,paged:t},success:function(u){u.success&&(i.$results.html(u.data.html),i.isFirstLoad=!1,u.data.filters&&c.updateFilters(u.data.filters),typeof o<"u"&&o.calculate(),e&&i.updateUrl(r,t),t>1&&a("html, body").animate({scrollTop:i.$filters.offset().top-100},300))},error:function(){i.$results.html('

Error

Something went wrong. Please try again.

')},complete:function(){i.isLoading=!1,i.$filters.removeClass("is-loading")}})}},getFormData:function(){return{property_type:this.$form.find('[name="property_type"]').val()||"",property_location:this.$form.find('[name="property_location"]').val()||"",min_price:this.$form.find('[name="min_price"]').val()||"",max_price:this.$form.find('[name="max_price"]').val()||"",beds:this.$form.find('[name="beds"]').val()||""}},getFormState:function(){return this.getFormData()},setFormFromState:function(t){for(var e in t)this.$form.find('[name="'+e+'"]').val(t[e])},updateUrl:function(t,e){var i=new URL(homeprozAjax.archiveUrl);for(var r in t)t[r]&&i.searchParams.set(r,t[r]);e>1?i.hash="page="+e:i.hash="",history.replaceState(null,"",i.toString())},getPageFromUrl:function(t){var e=t.match(/#page=(\d+)/);if(e)return parseInt(e[1]);var i=t.match(/[?&]paged=(\d+)/);return i?parseInt(i[1]):1},resetFilters:function(){this.$form.find("select").val(""),this.filterProperties(1)}},s={breakpoint:1024,isMapView:!0,isAboveBreakpoint:!0,mapInitialized:!1,init:function(){var t=this;typeof homeprozMapData<"u"&&(this.isMapView=homeprozMapData.isMapView!==!1),this.isAboveBreakpoint=window.innerWidth>=this.breakpoint,this.isAboveBreakpoint&&this.isMapView&&typeof homeprozMapData<"u"&&(c.init(homeprozMapData.initialFilters||{}),this.mapInitialized=!0);var e;a(window).on("resize",function(){clearTimeout(e),e=setTimeout(function(){t.handleResize()},150)})},handleResize:function(){var t=this.isAboveBreakpoint;this.isAboveBreakpoint=window.innerWidth>=this.breakpoint;var e=a(".property-archive-main");!t&&this.isAboveBreakpoint&&(this.isMapView?(e.removeClass("is-grid-view").addClass("is-map-view"),!this.mapInitialized&&typeof homeprozMapData<"u"?(c.init(homeprozMapData.initialFilters||{}),this.mapInitialized=!0):c.map&&setTimeout(function(){c.map.invalidateSize()},100)):e.removeClass("is-map-view").addClass("is-grid-view"),typeof o<"u"&&setTimeout(function(){o.calculate()},150))},setMapView:function(t){this.isMapView=t}},o={cardWidth:400,cardGap:24,mapGap:32,mapRatio:.33,breakpoint:1024,containerPadding:24,init:function(){this.calculate();var t=this,e;a(window).on("resize",function(){clearTimeout(e),e=setTimeout(function(){t.calculate()},100)})},calculate:function(){if(window.innerWidth .container"),i=t.hasClass("is-map-view"),r=e.width();i?this.calculateMapLayout(r):this.calculateGridLayout(r)},calculateMapLayout:function(t){for(var e=5;e>=1;e--){var i=e*this.cardWidth+(e-1)*this.cardGap,r=(this.mapGap+i)/(1-this.mapRatio);if(r<=t){this.setProperties(r,e,".property-map-layout"),this.setProperties(r,e,".property-list-container");return}}var i=this.cardWidth,r=(this.mapGap+i)/(1-this.mapRatio);this.setProperties(Math.min(r,t),1,".property-map-layout"),this.setProperties(Math.min(r,t),1,".property-list-container")},calculateGridLayout:function(t){for(var e=6;e>=1;e--){var i=e*this.cardWidth+(e-1)*this.cardGap;if(i<=t){this.setProperties(i,e,".grid-view-container");return}}this.setProperties(this.cardWidth,1,".grid-view-container")},setProperties:function(t,e,i){var r=a(i);r.length&&(r.css("--layout-width",t+"px"),r.css("--card-columns",e))},clearProperties:function(){a(".property-map-layout, .grid-view-container, .property-list-container").css({"--layout-width":"","--card-columns":""})}},l={init:function(){this.loadVisibleImages(),this.bindEvents()},bindEvents:function(){var t=this,e;if(a(window).on("scroll",function(){clearTimeout(e),e=setTimeout(function(){t.loadVisibleImages()},100)}),typeof MutationObserver<"u"){var i=new MutationObserver(function(h){t.loadVisibleImages()}),r=document.getElementById("property-results");r&&i.observe(r,{childList:!0,subtree:!0});var u=document.getElementById("property-results-grid");u&&i.observe(u,{childList:!0,subtree:!0})}},loadVisibleImages:function(){var t=this,e=a(window).height(),i=a(window).scrollTop(),r=200;a(".property-card-image.is-loading[data-bg]").each(function(){var u=a(this),h=u.offset().top,m=h+u.outerHeight();m>=i-r&&h<=i+e+r&&t.loadImage(u)})},loadImage:function(t){var e=t.data("bg");if(e){t.removeClass("is-loading").removeAttr("data-bg");var i=new Image;i.onload=function(){t.css("background-image","url("+e+")"),t.addClass("is-loaded")},i.onerror=function(){t.addClass("is-loaded"),t.removeClass("has-image")},i.src=e}}};a(function(){n.init(),s.init(),o.init(),l.init()})})(jQuery);(function(a){var c={$gallery:null,$lightbox:null,$mainImage:null,$mainImageContainer:null,$thumbnails:null,$thumbnailsContainer:null,$thumbnailsViewport:null,$playbackBtn:null,$prevBtn:null,$nextBtn:null,$lightboxImage:null,$lightboxImageContainer:null,$lightboxCounter:null,images:[],currentIndex:0,isPlaying:!0,isTransitioning:!1,autoplayInterval:null,autoplayDelay:5e3,fadeDuration:1e3,slideDuration:300,thumbnailsPerPage:5,thumbnailPage:0,swipeStartX:0,swipeStartY:0,swipeThreshold:50,isSwiping:!1,init:function(){if(this.$gallery=a(".property-gallery"),this.$lightbox=a("#property-lightbox"),!!this.$gallery.length){this.$mainImageContainer=this.$gallery.find(".gallery-main-image"),this.$mainImage=this.$mainImageContainer.find("img"),this.$thumbnailsContainer=this.$gallery.find(".gallery-thumbnails-container"),this.$thumbnailsViewport=this.$gallery.find(".gallery-thumbnails-viewport"),this.$thumbnails=this.$gallery.find(".gallery-thumbnail"),this.$playbackBtn=this.$gallery.find(".gallery-playback-btn"),this.$prevBtn=this.$gallery.find(".gallery-thumbnails-prev"),this.$nextBtn=this.$gallery.find(".gallery-thumbnails-next"),this.$lightboxImage=this.$lightbox.find(".lightbox-image"),this.$lightboxImageContainer=this.$lightbox.find(".lightbox-image-container"),this.$lightboxCounter=this.$lightbox.find(".lightbox-current");var n=a("#gallery-images-data");n.length&&(this.images=JSON.parse(n.text())),this.images.length!==0&&(this.calculateThumbnailsPerPage(),this.bindEvents(),this.bindSwipeEvents(),this.updateThumbnailNavigation(),this.images.length>1&&this.startAutoplay())}},calculateThumbnailsPerPage:function(){a(window).width()<=640?this.thumbnailsPerPage=4:this.thumbnailsPerPage=5},bindEvents:function(){var n=this;this.$thumbnails.on("click",function(s){s.stopPropagation();var o=parseInt(a(this).data("index"));n.stopAutoplay(),n.setMainImage(o,!1)}),this.$playbackBtn.on("click",function(s){s.stopPropagation(),s.preventDefault(),n.isPlaying?n.stopAutoplay():n.startAutoplay()}),this.$prevBtn.on("click",function(){n.prevThumbnailPage()}),this.$nextBtn.on("click",function(){n.nextThumbnailPage()}),this.$gallery.find("[data-lightbox-trigger]").on("click",function(s){if(n.isSwiping){n.isSwiping=!1;return}n.stopAutoplay(),n.openLightbox(n.currentIndex)}),this.$lightbox.find(".lightbox-close, .lightbox-overlay").on("click",function(){n.closeLightbox()}),this.$lightbox.find(".lightbox-prev").on("click",function(){n.slideLightboxImage("prev")}),this.$lightbox.find(".lightbox-next").on("click",function(){n.slideLightboxImage("next")}),a(document).on("keydown",function(s){if(n.$lightbox.is('[aria-hidden="false"]'))switch(s.key){case"Escape":n.closeLightbox();break;case"ArrowLeft":n.slideLightboxImage("prev");break;case"ArrowRight":n.slideLightboxImage("next");break}}),a(window).on("resize",function(){n.calculateThumbnailsPerPage(),n.updateThumbnailNavigation()})},bindSwipeEvents:function(){var n=this;this.$mainImageContainer[0].addEventListener("touchstart",function(s){n.handleSwipeStart(s)},{passive:!0}),this.$mainImageContainer[0].addEventListener("touchend",function(s){n.handleMainGallerySwipeEnd(s)},{passive:!0}),this.$lightboxImageContainer[0].addEventListener("touchstart",function(s){n.handleSwipeStart(s)},{passive:!0}),this.$lightboxImageContainer[0].addEventListener("touchend",function(s){n.handleLightboxSwipeEnd(s)},{passive:!0})},handleSwipeStart:function(n){n.touches.length===1&&(this.swipeStartX=n.touches[0].clientX,this.swipeStartY=n.touches[0].clientY)},handleMainGallerySwipeEnd:function(n){if(n.changedTouches.length===1){var s=n.changedTouches[0].clientX-this.swipeStartX,o=n.changedTouches[0].clientY-this.swipeStartY;Math.abs(s)>Math.abs(o)&&Math.abs(s)>this.swipeThreshold&&(this.isSwiping=!0,this.stopAutoplay(),s>0?this.slideMainImage("prev"):this.slideMainImage("next"))}},handleLightboxSwipeEnd:function(n){if(n.changedTouches.length===1){var s=n.changedTouches[0].clientX-this.swipeStartX,o=n.changedTouches[0].clientY-this.swipeStartY;Math.abs(s)>Math.abs(o)&&Math.abs(s)>this.swipeThreshold&&(s>0?this.slideLightboxImage("prev"):this.slideLightboxImage("next"))}},startAutoplay:function(){var n=this;this.images.length<=1||(this.isPlaying=!0,this.$playbackBtn.addClass("is-playing"),this.$playbackBtn.attr("aria-label","Pause slideshow"),this.autoplayInterval=setInterval(function(){n.advanceImage()},this.autoplayDelay))},stopAutoplay:function(){this.isPlaying=!1,this.$playbackBtn.removeClass("is-playing"),this.$playbackBtn.attr("aria-label","Play slideshow"),this.autoplayInterval&&(clearInterval(this.autoplayInterval),this.autoplayInterval=null)},advanceImage:function(){if(!this.isTransitioning){var n=this.currentIndex+1;n>=this.images.length&&(n=0),this.setMainImage(n,!0)}},slideMainImage:function(n){var s=this;if(!(this.isTransitioning||this.images.length<=1)){var o;n==="prev"?(o=this.currentIndex-1,o<0&&(o=this.images.length-1)):(o=this.currentIndex+1,o>=this.images.length&&(o=0)),this.isTransitioning=!0;var l=this.images[o],t=n==="next"?"100%":"-100%",e=n==="next"?"-100%":"100%",i=a('');i.attr("src",l.url),i.attr("alt",l.alt||"Property photo"),i.css({position:"absolute",top:0,left:0,width:"100%",height:"100%","object-fit":"cover",transform:"translateX("+t+")","z-index":2,"border-radius":"0.5rem"}),this.$mainImageContainer.css({position:"relative",overflow:"hidden"}),this.$mainImageContainer.append(i),this.$mainImage.css({transition:"transform "+this.slideDuration+"ms ease-out"}),i.css({transition:"transform "+this.slideDuration+"ms ease-out"}),i[0].offsetHeight,this.$mainImage.css("transform","translateX("+e+")"),i.css("transform","translateX(0)"),setTimeout(function(){s.$mainImage.attr("src",l.url),s.$mainImage.attr("alt",l.alt||"Property photo"),s.$mainImage.css({transition:"",transform:""}),i.remove(),s.isTransitioning=!1},this.slideDuration),this.currentIndex=o,this.$thumbnails.removeClass("is-active"),this.$thumbnails.filter('[data-index="'+o+'"]').addClass("is-active"),this.scrollToThumbnail(o)}},setMainImage:function(n,s){var o=this;if(!(n<0||n>=this.images.length)&&!this.isTransitioning){var l=this.images[n];if(s){this.isTransitioning=!0;var t=a('');t.attr("src",l.url),t.attr("alt",l.alt||"Property photo"),t.css({position:"absolute",top:0,left:0,width:"100%",height:"100%","object-fit":"cover",opacity:0,transform:"scale(1.02)",transition:"opacity "+this.fadeDuration+"ms ease-in-out, transform "+this.fadeDuration+"ms ease-in-out","z-index":2,"border-radius":"0.5rem"}),this.$mainImageContainer.css("position","relative"),this.$mainImageContainer.append(t),t[0].offsetHeight,t.css({opacity:1,transform:"scale(1)"}),setTimeout(function(){o.$mainImage.attr("src",l.url),o.$mainImage.attr("alt",l.alt||"Property photo"),t.remove(),o.isTransitioning=!1},this.fadeDuration)}else this.$mainImage.attr("src",l.url),this.$mainImage.attr("alt",l.alt||"Property photo");this.currentIndex=n,this.$thumbnails.removeClass("is-active"),this.$thumbnails.filter('[data-index="'+n+'"]').addClass("is-active"),this.scrollToThumbnail(n)}},scrollToThumbnail:function(n){var s=Math.floor(n/this.thumbnailsPerPage);s!==this.thumbnailPage&&(this.thumbnailPage=s,this.scrollThumbnails())},scrollThumbnails:function(){var n=this.$gallery.find(".gallery-thumbnails"),s=this.$thumbnails.first().outerWidth(!0),o=this.thumbnailPage*this.thumbnailsPerPage*s;n.css("transform","translateX(-"+o+"px)"),this.updateThumbnailNavigation()},updateThumbnailNavigation:function(){var n=Math.ceil(this.images.length/this.thumbnailsPerPage);this.$prevBtn.prop("disabled",this.thumbnailPage===0),this.$nextBtn.prop("disabled",this.thumbnailPage>=n-1),n<=1?(this.$prevBtn.hide(),this.$nextBtn.hide()):(this.$prevBtn.show(),this.$nextBtn.show())},prevThumbnailPage:function(){this.thumbnailPage>0&&(this.thumbnailPage--,this.scrollThumbnails())},nextThumbnailPage:function(){var n=Math.ceil(this.images.length/this.thumbnailsPerPage);this.thumbnailPage=this.images.length&&(o=0)),this.isTransitioning=!0;var l=this.images[o],t=n==="next"?"100%":"-100%",e=n==="next"?"-100%":"100%",i=a('');i.attr("src",l.url),i.attr("alt",l.alt||"Property photo"),i.css({position:"absolute","max-width":"100%","max-height":"calc(100vh - 8rem)","object-fit":"contain",transform:"translateX("+t+")",left:"50%",top:"50%","margin-left":"-45vw","margin-top":"calc(-50vh + 4rem)"}),this.$lightboxImageContainer.css({position:"relative",overflow:"hidden"}),this.$lightboxImageContainer.append(i),this.$lightboxImage.css({transition:"transform "+this.slideDuration+"ms ease-out"}),i.css({transition:"transform "+this.slideDuration+"ms ease-out"}),i[0].offsetHeight,this.$lightboxImage.css("transform","translateX("+e+")"),i.css("transform","translateX(0)"),setTimeout(function(){s.$lightboxImage.attr("src",l.url),s.$lightboxImage.attr("alt",l.alt||"Property photo"),s.$lightboxImage.css({transition:"",transform:""}),i.remove(),s.isTransitioning=!1,s.$lightboxCounter.text(o+1)},this.slideDuration),this.currentIndex=o}},prevImage:function(){this.slideLightboxImage("prev")},nextImage:function(){this.slideLightboxImage("next")},updateLightboxImage:function(){var n=this.images[this.currentIndex];this.$lightboxImage.attr("src",n.url),this.$lightboxImage.attr("alt",n.alt||"Property photo"),this.$lightboxCounter.text(this.currentIndex+1)}};a(function(){c.init()})})(jQuery);(function(a){if(!a(".mortgage-calculator-main").length)return;let c=!1;a.fn.currencyInput=function(s=!0){return this.data("ci_show_symbol",s),c||(c=!0,a.fn._CIOriginalVal=a.fn.val,a.fn.val=function(l){if(a(this).data("_currencyInput"))if(arguments.length===0){var t=a(this)._CIOriginalVal();if(t=="")return"";var e=parseInt(t.replace(/[^0-9]/g,""));return e}else{if(l=String(l).replace(/[^0-9]/g,""),l!=""){var i=parseInt(l).toLocaleString("en-US",{style:"currency",currency:"USD",minimumFractionDigits:0,maximumFractionDigits:0});return a(this).data("ci_show_symbol")||(i=i.replace("$","")),a(this)._CIOriginalVal(i)}return a(this)._CIOriginalVal(l)}else if(a(this).data("_percentInput"))if(arguments.length===0){var t=a(this)._CIOriginalVal();if(t=="")return"";var e=parseFloat(t.replace(/[^0-9.]/g,""));return isNaN(e)?"":e}else{l=String(l).replace(/[^0-9.]/g,"");var r=l.split(".");return r.length>2&&(l=r[0]+"."+r.slice(1).join("")),a(this)._CIOriginalVal(l)}else return arguments.length===0?a(this)._CIOriginalVal():a(this)._CIOriginalVal(l)}),this.data("_currencyInput")?this:(this.data("_currencyInput",!0),this.on("focus",function(){a(this).select()}),this.on("input",function(l){var t=this.selectionStart,e=a(this)._CIOriginalVal(),i=e.length;a(this).val(e);var r=a(this)._CIOriginalVal().length;r>i?t+=r-i:r2&&(e=i[0]+"."+i.slice(1).join("")),a(this)._CIOriginalVal(e);var r=e.length;r0){var l=o/s*100;this.$downPaymentPercent._CIOriginalVal(l.toFixed(1))}},syncDownPaymentFromPercent:function(){var s=this.$homePrice.val(),o=this.$downPaymentPercent.val();if(s&&s>0&&o!==""&&o>=0){var l=Math.round(s*o/100);this.$downPayment.val(l)}},calculate:function(){var s=this.$homePrice.val()||0,o=this.$downPayment.val()||0,l=parseInt(this.$loanTerm.val(),10),t=this.$interestRate.val()||0,e=s-o;e<0&&(e=0);var i=t/100/12,r=l*12,u=0,h=0;if(e>0&&i>0&&r>0){var m=Math.pow(1+i,r);u=e*(i*m)/(m-1),h=u*r-e}else e>0&&i===0&&(u=e/r,h=0);this.$monthlyPayment.text(this.formatCurrencyDisplay(u)),this.$principalInterest.text(this.formatCurrencyDisplay(u)),this.$loanAmount.text(this.formatCurrencyDisplay(e)),this.$totalInterest.text(this.formatCurrencyDisplay(h))}};a(document).ready(function(){n.init()})})(jQuery);(function(a){a(function(){})})(jQuery); +(function(a){var c=a(".menu-toggle"),n=a(".mobile-navigation");c.length&&(c.on("click",function(){var s=a(this).attr("aria-expanded")==="true";a(this).attr("aria-expanded",!s),n.toggleClass("is-open"),s?a("body").removeClass("mobile-menu-open"):a("body").addClass("mobile-menu-open")}),a(document).on("keydown",function(s){s.key==="Escape"&&n.hasClass("is-open")&&(c.attr("aria-expanded","false"),n.removeClass("is-open"),a("body").removeClass("mobile-menu-open"))}),a(document).on("click",function(s){n.hasClass("is-open")&&!a(s.target).closest(".mobile-navigation").length&&!a(s.target).closest(".menu-toggle").length&&(c.attr("aria-expanded","false"),n.removeClass("is-open"),a("body").removeClass("mobile-menu-open"))}))})(jQuery);(function(a){var c=6e3,n=1450,s=1e3,o=[],l=0,t=null,e=!1,i=!1,r=null;function u(){if(a(".Home_Page").length&&(r=a(".hero-split-image"),!!r.length)){var d=r.data("gallery-images");!d||!d.length||(o=d,h(),a(window).on("resize",b(h,150)))}}function h(){var d=a(window).width();d>=n?e||m():e&&p()}function m(){e=!0,i||(g(),i=!0),t=setInterval(y,c)}function p(){e=!1,t&&(clearInterval(t),t=null)}function g(){a.each(o,function(d,f){var v=new Image;v.src=f})}function y(){l=(l+1)%o.length;var d=o[l],f=a('
');f.css({position:"absolute",top:0,left:0,right:0,bottom:0,"background-image":"url("+d+")","background-size":"cover","background-position":"center center","background-repeat":"no-repeat",opacity:0,transform:"scale(1.02)",transition:"opacity "+s+"ms ease-in-out, transform "+s+"ms ease-in-out","z-index":1}),r.css("position","relative"),r.append(f),f[0].offsetHeight,f.css({opacity:1,transform:"scale(1)"}),setTimeout(function(){r.css("background-image","url("+d+")"),f.remove()},s)}function b(d,f){var v;return function(){var I=this,x=arguments;clearTimeout(v),v=setTimeout(function(){d.apply(I,x)},f)}}a(document).ready(u)})(jQuery);(function(a){var c={map:null,markers:{},densityLayer:null,clusterLayer:null,markerCluster:null,selectedPropertyId:null,hoveredPropertyId:null,baseZIndex:400,currentFilters:{},isLoading:!1,loadTimeout:null,currentMode:null,init:function(t){var e=a("#property-map");if(!(!e.length||typeof L>"u")){this.currentFilters=t||{},this.map=L.map("property-map").setView([45,-93.5],7),L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",{attribution:'© OpenStreetMap'}).addTo(this.map),this.densityLayer=L.layerGroup().addTo(this.map),this.clusterLayer=L.layerGroup().addTo(this.map),this.markerCluster=L.markerClusterGroup({maxClusterRadius:50,spiderfyOnMaxZoom:!0,showCoverageOnHover:!1,zoomToBoundsOnClick:!0,disableClusteringAtZoom:18,chunkedLoading:!0,chunkInterval:200,chunkDelay:50,iconCreateFunction:function(r){var u=r.getChildCount(),h="small";return u>=100?h="large":u>=10&&(h="medium"),L.divIcon({html:"
"+u+"
",className:"marker-cluster marker-cluster-"+h,iconSize:L.point(40,40)})}}),this.map.addLayer(this.markerCluster);var i=this;this.map.on("moveend zoomend",function(){i.loadClusters()}),this.bindCardHoverEvents(),this.loadClusters()}},loadClusters:function(){if(this.map){var t=this;clearTimeout(this.loadTimeout),this.loadTimeout=setTimeout(function(){t._doLoadClusters()},150)}},_doLoadClusters:function(){if(!this.isLoading){var t=this,e=this.map.getBounds(),i=this.map.getCenter(),r=this.map.getZoom();this.isLoading=!0;var u=[e.getSouthWest().lat,e.getSouthWest().lng,e.getNorthEast().lat,e.getNorthEast().lng],h=[i.lat,i.lng],m={action:"mls_get_clusters",zoom:r,bounds:u,status:this.currentFilters.status||"Active",property_type:this.currentFilters.property_type||"",city:this.currentFilters.city||"",min_price:this.currentFilters.min_price||"",max_price:this.currentFilters.max_price||"",min_beds:this.currentFilters.min_beds||""};n.updateFromMap(u,h),a.ajax({url:homeprozMapData.clusterEndpoint,type:"GET",data:m,success:function(p){if(p.success){var g=p.data;switch(t.currentMode=g.type,g.type){case"density":t.renderDensity(g.dots);break;case"clusters":t.renderClusters(g.clusters);break;case"markers":t.renderMarkers(g.markers);break}}},complete:function(){t.isLoading=!1}})}},clearAllLayers:function(){this.densityLayer.clearLayers(),this.clusterLayer.clearLayers(),this.markerCluster.clearLayers(),this.markers={}},renderDensity:function(t){this.clearAllLayers();var e=this,i=this.map.getZoom();t.forEach(function(r){var u=e.getDensityColor(r.count,i),h=e.getDensitySize(r.count,i),m=L.divIcon({html:'
',className:"density-dot-container",iconSize:[h,h],iconAnchor:[h/2,h/2]}),p=L.marker([r.lat,r.lng],{icon:m});p.on("click",function(){e.map.setView([r.lat,r.lng],e.map.getZoom()+2)}),p.bindTooltip(r.count+" properties",{className:"density-tooltip"}),e.densityLayer.addLayer(p)})},getDensityThreshold:function(t){return Math.max(40,Math.round(600/Math.pow(1.4,t-3)))},getDensityColor:function(t,e){var i=this.getDensityThreshold(e),r=t/i;return r>=1.5?"rgba(180, 83, 9, 0.8)":r>=1?"rgba(217, 119, 6, 0.8)":r>=.6?"rgba(245, 158, 11, 0.8)":r>=.3?"rgba(234, 179, 8, 0.8)":r>=.15?"rgba(132, 204, 22, 0.8)":"rgba(34, 197, 94, 0.8)"},getDensitySize:function(t,e){var i=this.getDensityThreshold(e),r=t/i;return r>=1.5?11:r>=1?10:r>=.6?8:r>=.3?7:6},renderClusters:function(t){this.clearAllLayers();var e=this;t.forEach(function(i){var r="small";i.count>=100?r="large":i.count>=10&&(r="medium");var u=L.divIcon({html:"
"+i.count+"
",className:"marker-cluster marker-cluster-"+r+" server-cluster",iconSize:L.point(40,40)}),h=L.marker([i.lat,i.lng],{icon:u});h.on("click",function(){e.map.setView([i.lat,i.lng],e.map.getZoom()+2)});var m="$"+e.formatNumber(i.min_price);i.max_price!==i.min_price&&(m+=" - $"+e.formatNumber(i.max_price)),h.bindTooltip(i.count+" properties
"+m,{className:"cluster-tooltip"}),e.clusterLayer.addLayer(h)})},renderMarkers:function(t){this.clearAllLayers(),this.selectedPropertyId=null,this.hoveredPropertyId=null,a(".property-card").removeClass("property-card-highlighted");var e=this,i=[];t.forEach(function(r,u){if(r.lat&&r.lng){var h=L.marker([r.lat,r.lng],{icon:e.createIcon("red"),zIndexOffset:e.baseZIndex+u});h.propertyId=r.id,h.defaultZIndex=e.baseZIndex+u,h.bindPopup('
'+r.price+"
"+r.address+'
View Details
'),h.on("click",function(m){e.onMarkerClick(r.id)}),i.push(h),e.markers[r.id]=h}}),this.markerCluster.addLayers(i)},updateFilters:function(t){this.currentFilters=t||{},this.loadClusters()},formatNumber:function(t){return t.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")},createIcon:function(t){return t=t||"red",L.divIcon({className:"property-marker property-marker-"+t,html:'
',iconSize:[17,22],iconAnchor:[8,22],popupAnchor:[0,-22]})},onMarkerClick:function(t){var e=this;if(this.selectedPropertyId!==t){this.selectedPropertyId&&(this.setMarkerColor(this.selectedPropertyId,"red"),this.resetMarkerZIndex(this.selectedPropertyId),a("#property-"+this.selectedPropertyId).removeClass("property-card-highlighted")),this.selectedPropertyId=t,this.setMarkerColor(t,"amber"),this.setMarkerZIndex(t,1e4);var i=a("#property-"+t);if(i.length){var r=i.offset().top,u=r+i.outerHeight(),h=a(window).scrollTop(),m=h+a(window).height(),p=r>=h&&u<=m;p?e.flashCard(i):a("html, body").animate({scrollTop:r-120},400,function(){e.flashCard(i)})}}},flashCard:function(t){t.removeClass("property-card-highlighted"),setTimeout(function(){t.addClass("property-card-highlighted"),setTimeout(function(){t.removeClass("property-card-highlighted"),setTimeout(function(){t.addClass("property-card-highlighted")},150)},150)},50)},setMarkerColor:function(t,e){var i=this.markers[t];i&&i.setIcon(this.createIcon(e))},setMarkerZIndex:function(t,e){var i=this.markers[t];i&&i.setZIndexOffset(e)},resetMarkerZIndex:function(t){var e=this.markers[t];e&&e.setZIndexOffset(e.defaultZIndex)},bindCardHoverEvents:function(){var t=this;a(document).on("mouseenter",".property-card[data-property-id]",function(){var e=a(this).data("property-id");e!==t.selectedPropertyId&&(t.hoveredPropertyId=e,t.setMarkerColor(e,"blue"),t.setMarkerZIndex(e,9e3))}),a(document).on("mouseleave",".property-card[data-property-id]",function(){var e=a(this).data("property-id");e!==t.selectedPropertyId&&(t.hoveredPropertyId===e&&(t.hoveredPropertyId=null),t.setMarkerColor(e,"red"),t.resetMarkerZIndex(e))})}},n={$form:null,$results:null,$filters:null,isFirstLoad:!0,isLoading:!1,mapBounds:null,mapCenter:null,isMapUpdate:!1,init:function(){this.$form=a(".filters-form"),this.$results=a("#property-results"),this.$filters=a("#property-filters"),!(!this.$form.length||!this.$results.length)&&(this.bindEvents(),this.initFromUrl())},bindEvents:function(){var t=this;this.$form.on("submit",function(e){e.preventDefault(),t.filterProperties(1)}),this.$form.find("select").on("change",function(){t.filterProperties(1)}),a(".filters-reset").on("click",function(e){e.preventDefault(),t.resetFilters()}),this.$results.on("click",".pagination a",function(e){e.preventDefault();var i=t.getPageFromUrl(a(this).attr("href"));t.filterProperties(i)}),a(window).on("hashchange",function(){var e=t.getPageFromHash();t.filterProperties(e,!1)})},initFromUrl:function(){var t=new URLSearchParams(window.location.search);this.$form.find("select").each(function(){var i=a(this).attr("name");t.has(i)&&a(this).val(t.get(i))});var e=this.getPageFromHash();e>1&&this.filterProperties(e,!1)},updateFromMap:function(t,e){this.mapBounds=t,this.mapCenter=e,this.isMapUpdate=!0,this.filterProperties(1,!1)},getPageFromHash:function(){var t=window.location.hash,e=t.match(/#page=(\d+)/);return e?parseInt(e[1]):1},filterProperties:function(t,e){if(!this.isLoading){e=e!==!1,t=t||1;var i=this,r=this.getFormData();this.isLoading=!0,this.$filters.addClass("is-loading"),this.isFirstLoad&&this.$results.html('
');var u={action:"homeproz_filter_properties",nonce:homeprozAjax.nonce,property_type:r.property_type,property_location:r.property_location,min_price:r.min_price,max_price:r.max_price,beds:r.beds,paged:t};this.mapBounds&&(u.bounds=this.mapBounds),this.mapCenter&&(u.center=this.mapCenter),a.ajax({url:homeprozAjax.ajaxUrl,type:"POST",data:u,success:function(h){h.success&&(i.$results.html(h.data.html),i.isFirstLoad=!1,h.data.filters&&!i.isMapUpdate&&c.updateFilters(h.data.filters),i.isMapUpdate=!1,typeof o<"u"&&o.calculate(),e&&i.updateUrl(r,t),t>1&&a("html, body").animate({scrollTop:i.$filters.offset().top-100},300))},error:function(){i.$results.html('

Error

Something went wrong. Please try again.

')},complete:function(){i.isLoading=!1,i.$filters.removeClass("is-loading")}})}},getFormData:function(){return{property_type:this.$form.find('[name="property_type"]').val()||"",property_location:this.$form.find('[name="property_location"]').val()||"",min_price:this.$form.find('[name="min_price"]').val()||"",max_price:this.$form.find('[name="max_price"]').val()||"",beds:this.$form.find('[name="beds"]').val()||""}},getFormState:function(){return this.getFormData()},setFormFromState:function(t){for(var e in t)this.$form.find('[name="'+e+'"]').val(t[e])},updateUrl:function(t,e){var i=new URL(homeprozAjax.archiveUrl);for(var r in t)t[r]&&i.searchParams.set(r,t[r]);e>1?i.hash="page="+e:i.hash="",history.replaceState(null,"",i.toString())},getPageFromUrl:function(t){var e=t.match(/#page=(\d+)/);if(e)return parseInt(e[1]);var i=t.match(/[?&]paged=(\d+)/);return i?parseInt(i[1]):1},resetFilters:function(){this.$form.find("select").val(""),this.filterProperties(1)}},s={breakpoint:1024,isMapView:!0,isAboveBreakpoint:!0,mapInitialized:!1,init:function(){var t=this;typeof homeprozMapData<"u"&&(this.isMapView=homeprozMapData.isMapView!==!1),this.isAboveBreakpoint=window.innerWidth>=this.breakpoint,this.isAboveBreakpoint&&this.isMapView&&typeof homeprozMapData<"u"&&(c.init(homeprozMapData.initialFilters||{}),this.mapInitialized=!0);var e;a(window).on("resize",function(){clearTimeout(e),e=setTimeout(function(){t.handleResize()},150)})},handleResize:function(){var t=this.isAboveBreakpoint;this.isAboveBreakpoint=window.innerWidth>=this.breakpoint;var e=a(".property-archive-main");!t&&this.isAboveBreakpoint&&(this.isMapView?(e.removeClass("is-grid-view").addClass("is-map-view"),!this.mapInitialized&&typeof homeprozMapData<"u"?(c.init(homeprozMapData.initialFilters||{}),this.mapInitialized=!0):c.map&&setTimeout(function(){c.map.invalidateSize()},100)):e.removeClass("is-map-view").addClass("is-grid-view"),typeof o<"u"&&setTimeout(function(){o.calculate()},150))},setMapView:function(t){this.isMapView=t}},o={cardWidth:400,cardGap:24,mapGap:32,mapRatio:.33,breakpoint:1024,containerPadding:24,init:function(){this.calculate();var t=this,e;a(window).on("resize",function(){clearTimeout(e),e=setTimeout(function(){t.calculate()},100)})},calculate:function(){if(window.innerWidth .container"),i=t.hasClass("is-map-view"),r=e.width();i?this.calculateMapLayout(r):this.calculateGridLayout(r)},calculateMapLayout:function(t){for(var e=5;e>=1;e--){var i=e*this.cardWidth+(e-1)*this.cardGap,r=(this.mapGap+i)/(1-this.mapRatio);if(r<=t){this.setProperties(r,e,".property-map-layout"),this.setProperties(r,e,".property-list-container");return}}var i=this.cardWidth,r=(this.mapGap+i)/(1-this.mapRatio);this.setProperties(Math.min(r,t),1,".property-map-layout"),this.setProperties(Math.min(r,t),1,".property-list-container")},calculateGridLayout:function(t){for(var e=6;e>=1;e--){var i=e*this.cardWidth+(e-1)*this.cardGap;if(i<=t){this.setProperties(i,e,".grid-view-container");return}}this.setProperties(this.cardWidth,1,".grid-view-container")},setProperties:function(t,e,i){var r=a(i);r.length&&(r.css("--layout-width",t+"px"),r.css("--card-columns",e))},clearProperties:function(){a(".property-map-layout, .grid-view-container, .property-list-container").css({"--layout-width":"","--card-columns":""})}},l={init:function(){this.loadVisibleImages(),this.bindEvents()},bindEvents:function(){var t=this,e;if(a(window).on("scroll",function(){clearTimeout(e),e=setTimeout(function(){t.loadVisibleImages()},100)}),typeof MutationObserver<"u"){var i=new MutationObserver(function(h){t.loadVisibleImages()}),r=document.getElementById("property-results");r&&i.observe(r,{childList:!0,subtree:!0});var u=document.getElementById("property-results-grid");u&&i.observe(u,{childList:!0,subtree:!0})}},loadVisibleImages:function(){var t=this,e=a(window).height(),i=a(window).scrollTop(),r=200;a(".property-card-image.is-loading[data-bg]").each(function(){var u=a(this),h=u.offset().top,m=h+u.outerHeight();m>=i-r&&h<=i+e+r&&t.loadImage(u)})},loadImage:function(t){var e=t.data("bg");if(e){t.removeClass("is-loading").removeAttr("data-bg");var i=new Image;i.onload=function(){t.css("background-image","url("+e+")"),t.addClass("is-loaded")},i.onerror=function(){t.addClass("is-loaded"),t.removeClass("has-image")},i.src=e}}};a(function(){n.init(),s.init(),o.init(),l.init()})})(jQuery);(function(a){var c={$gallery:null,$lightbox:null,$mainImage:null,$mainImageContainer:null,$thumbnails:null,$thumbnailsContainer:null,$thumbnailsViewport:null,$playbackBtn:null,$prevBtn:null,$nextBtn:null,$lightboxImage:null,$lightboxImageContainer:null,$lightboxCounter:null,images:[],currentIndex:0,isPlaying:!0,isTransitioning:!1,autoplayInterval:null,autoplayDelay:5e3,fadeDuration:1e3,slideDuration:300,thumbnailsPerPage:5,thumbnailPage:0,swipeStartX:0,swipeStartY:0,swipeThreshold:50,isSwiping:!1,init:function(){if(this.$gallery=a(".property-gallery"),this.$lightbox=a("#property-lightbox"),!!this.$gallery.length){this.$mainImageContainer=this.$gallery.find(".gallery-main-image"),this.$mainImage=this.$mainImageContainer.find("img"),this.$thumbnailsContainer=this.$gallery.find(".gallery-thumbnails-container"),this.$thumbnailsViewport=this.$gallery.find(".gallery-thumbnails-viewport"),this.$thumbnails=this.$gallery.find(".gallery-thumbnail"),this.$playbackBtn=this.$gallery.find(".gallery-playback-btn"),this.$prevBtn=this.$gallery.find(".gallery-thumbnails-prev"),this.$nextBtn=this.$gallery.find(".gallery-thumbnails-next"),this.$lightboxImage=this.$lightbox.find(".lightbox-image"),this.$lightboxImageContainer=this.$lightbox.find(".lightbox-image-container"),this.$lightboxCounter=this.$lightbox.find(".lightbox-current");var n=a("#gallery-images-data");n.length&&(this.images=JSON.parse(n.text())),this.images.length!==0&&(this.calculateThumbnailsPerPage(),this.bindEvents(),this.bindSwipeEvents(),this.updateThumbnailNavigation(),this.images.length>1&&this.startAutoplay())}},calculateThumbnailsPerPage:function(){a(window).width()<=640?this.thumbnailsPerPage=4:this.thumbnailsPerPage=5},bindEvents:function(){var n=this;this.$thumbnails.on("click",function(s){s.stopPropagation();var o=parseInt(a(this).data("index"));n.stopAutoplay(),n.setMainImage(o,!1)}),this.$playbackBtn.on("click",function(s){s.stopPropagation(),s.preventDefault(),n.isPlaying?n.stopAutoplay():n.startAutoplay()}),this.$prevBtn.on("click",function(){n.prevThumbnailPage()}),this.$nextBtn.on("click",function(){n.nextThumbnailPage()}),this.$gallery.find("[data-lightbox-trigger]").on("click",function(s){if(n.isSwiping){n.isSwiping=!1;return}n.stopAutoplay(),n.openLightbox(n.currentIndex)}),this.$lightbox.find(".lightbox-close, .lightbox-overlay").on("click",function(){n.closeLightbox()}),this.$lightbox.find(".lightbox-prev").on("click",function(){n.slideLightboxImage("prev")}),this.$lightbox.find(".lightbox-next").on("click",function(){n.slideLightboxImage("next")}),a(document).on("keydown",function(s){if(n.$lightbox.is('[aria-hidden="false"]'))switch(s.key){case"Escape":n.closeLightbox();break;case"ArrowLeft":n.slideLightboxImage("prev");break;case"ArrowRight":n.slideLightboxImage("next");break}}),a(window).on("resize",function(){n.calculateThumbnailsPerPage(),n.updateThumbnailNavigation()})},bindSwipeEvents:function(){var n=this;this.$mainImageContainer[0].addEventListener("touchstart",function(s){n.handleSwipeStart(s)},{passive:!0}),this.$mainImageContainer[0].addEventListener("touchend",function(s){n.handleMainGallerySwipeEnd(s)},{passive:!0}),this.$lightboxImageContainer[0].addEventListener("touchstart",function(s){n.handleSwipeStart(s)},{passive:!0}),this.$lightboxImageContainer[0].addEventListener("touchend",function(s){n.handleLightboxSwipeEnd(s)},{passive:!0})},handleSwipeStart:function(n){n.touches.length===1&&(this.swipeStartX=n.touches[0].clientX,this.swipeStartY=n.touches[0].clientY)},handleMainGallerySwipeEnd:function(n){if(n.changedTouches.length===1){var s=n.changedTouches[0].clientX-this.swipeStartX,o=n.changedTouches[0].clientY-this.swipeStartY;Math.abs(s)>Math.abs(o)&&Math.abs(s)>this.swipeThreshold&&(this.isSwiping=!0,this.stopAutoplay(),s>0?this.slideMainImage("prev"):this.slideMainImage("next"))}},handleLightboxSwipeEnd:function(n){if(n.changedTouches.length===1){var s=n.changedTouches[0].clientX-this.swipeStartX,o=n.changedTouches[0].clientY-this.swipeStartY;Math.abs(s)>Math.abs(o)&&Math.abs(s)>this.swipeThreshold&&(s>0?this.slideLightboxImage("prev"):this.slideLightboxImage("next"))}},startAutoplay:function(){var n=this;this.images.length<=1||(this.isPlaying=!0,this.$playbackBtn.addClass("is-playing"),this.$playbackBtn.attr("aria-label","Pause slideshow"),this.autoplayInterval=setInterval(function(){n.advanceImage()},this.autoplayDelay))},stopAutoplay:function(){this.isPlaying=!1,this.$playbackBtn.removeClass("is-playing"),this.$playbackBtn.attr("aria-label","Play slideshow"),this.autoplayInterval&&(clearInterval(this.autoplayInterval),this.autoplayInterval=null)},advanceImage:function(){if(!this.isTransitioning){var n=this.currentIndex+1;n>=this.images.length&&(n=0),this.setMainImage(n,!0)}},slideMainImage:function(n){var s=this;if(!(this.isTransitioning||this.images.length<=1)){var o;n==="prev"?(o=this.currentIndex-1,o<0&&(o=this.images.length-1)):(o=this.currentIndex+1,o>=this.images.length&&(o=0)),this.isTransitioning=!0;var l=this.images[o],t=n==="next"?"100%":"-100%",e=n==="next"?"-100%":"100%",i=a('');i.attr("src",l.url),i.attr("alt",l.alt||"Property photo"),i.css({position:"absolute",top:0,left:0,width:"100%",height:"100%","object-fit":"cover",transform:"translateX("+t+")","z-index":2,"border-radius":"0.5rem"}),this.$mainImageContainer.css({position:"relative",overflow:"hidden"}),this.$mainImageContainer.append(i),this.$mainImage.css({transition:"transform "+this.slideDuration+"ms ease-out"}),i.css({transition:"transform "+this.slideDuration+"ms ease-out"}),i[0].offsetHeight,this.$mainImage.css("transform","translateX("+e+")"),i.css("transform","translateX(0)"),setTimeout(function(){s.$mainImage.attr("src",l.url),s.$mainImage.attr("alt",l.alt||"Property photo"),s.$mainImage.css({transition:"",transform:""}),i.remove(),s.isTransitioning=!1},this.slideDuration),this.currentIndex=o,this.$thumbnails.removeClass("is-active"),this.$thumbnails.filter('[data-index="'+o+'"]').addClass("is-active"),this.scrollToThumbnail(o)}},setMainImage:function(n,s){var o=this;if(!(n<0||n>=this.images.length)&&!this.isTransitioning){var l=this.images[n];if(s){this.isTransitioning=!0;var t=a('');t.attr("src",l.url),t.attr("alt",l.alt||"Property photo"),t.css({position:"absolute",top:0,left:0,width:"100%",height:"100%","object-fit":"cover",opacity:0,transform:"scale(1.02)",transition:"opacity "+this.fadeDuration+"ms ease-in-out, transform "+this.fadeDuration+"ms ease-in-out","z-index":2,"border-radius":"0.5rem"}),this.$mainImageContainer.css("position","relative"),this.$mainImageContainer.append(t),t[0].offsetHeight,t.css({opacity:1,transform:"scale(1)"}),setTimeout(function(){o.$mainImage.attr("src",l.url),o.$mainImage.attr("alt",l.alt||"Property photo"),t.remove(),o.isTransitioning=!1},this.fadeDuration)}else this.$mainImage.attr("src",l.url),this.$mainImage.attr("alt",l.alt||"Property photo");this.currentIndex=n,this.$thumbnails.removeClass("is-active"),this.$thumbnails.filter('[data-index="'+n+'"]').addClass("is-active"),this.scrollToThumbnail(n)}},scrollToThumbnail:function(n){var s=Math.floor(n/this.thumbnailsPerPage);s!==this.thumbnailPage&&(this.thumbnailPage=s,this.scrollThumbnails())},scrollThumbnails:function(){var n=this.$gallery.find(".gallery-thumbnails"),s=this.$thumbnails.first().outerWidth(!0),o=this.thumbnailPage*this.thumbnailsPerPage*s;n.css("transform","translateX(-"+o+"px)"),this.updateThumbnailNavigation()},updateThumbnailNavigation:function(){var n=Math.ceil(this.images.length/this.thumbnailsPerPage);this.$prevBtn.prop("disabled",this.thumbnailPage===0),this.$nextBtn.prop("disabled",this.thumbnailPage>=n-1),n<=1?(this.$prevBtn.hide(),this.$nextBtn.hide()):(this.$prevBtn.show(),this.$nextBtn.show())},prevThumbnailPage:function(){this.thumbnailPage>0&&(this.thumbnailPage--,this.scrollThumbnails())},nextThumbnailPage:function(){var n=Math.ceil(this.images.length/this.thumbnailsPerPage);this.thumbnailPage=this.images.length&&(o=0)),this.isTransitioning=!0;var l=this.images[o],t=n==="next"?"100%":"-100%",e=n==="next"?"-100%":"100%",i=a('');i.attr("src",l.url),i.attr("alt",l.alt||"Property photo"),i.css({position:"absolute","max-width":"100%","max-height":"calc(100vh - 8rem)","object-fit":"contain",transform:"translateX("+t+")",left:"50%",top:"50%","margin-left":"-45vw","margin-top":"calc(-50vh + 4rem)"}),this.$lightboxImageContainer.css({position:"relative",overflow:"hidden"}),this.$lightboxImageContainer.append(i),this.$lightboxImage.css({transition:"transform "+this.slideDuration+"ms ease-out"}),i.css({transition:"transform "+this.slideDuration+"ms ease-out"}),i[0].offsetHeight,this.$lightboxImage.css("transform","translateX("+e+")"),i.css("transform","translateX(0)"),setTimeout(function(){s.$lightboxImage.attr("src",l.url),s.$lightboxImage.attr("alt",l.alt||"Property photo"),s.$lightboxImage.css({transition:"",transform:""}),i.remove(),s.isTransitioning=!1,s.$lightboxCounter.text(o+1)},this.slideDuration),this.currentIndex=o}},prevImage:function(){this.slideLightboxImage("prev")},nextImage:function(){this.slideLightboxImage("next")},updateLightboxImage:function(){var n=this.images[this.currentIndex];this.$lightboxImage.attr("src",n.url),this.$lightboxImage.attr("alt",n.alt||"Property photo"),this.$lightboxCounter.text(this.currentIndex+1)}};a(function(){c.init()})})(jQuery);(function(a){if(!a(".mortgage-calculator-main").length)return;let c=!1;a.fn.currencyInput=function(s=!0){return this.data("ci_show_symbol",s),c||(c=!0,a.fn._CIOriginalVal=a.fn.val,a.fn.val=function(l){if(a(this).data("_currencyInput"))if(arguments.length===0){var t=a(this)._CIOriginalVal();if(t=="")return"";var e=parseInt(t.replace(/[^0-9]/g,""));return e}else{if(l=String(l).replace(/[^0-9]/g,""),l!=""){var i=parseInt(l).toLocaleString("en-US",{style:"currency",currency:"USD",minimumFractionDigits:0,maximumFractionDigits:0});return a(this).data("ci_show_symbol")||(i=i.replace("$","")),a(this)._CIOriginalVal(i)}return a(this)._CIOriginalVal(l)}else if(a(this).data("_percentInput"))if(arguments.length===0){var t=a(this)._CIOriginalVal();if(t=="")return"";var e=parseFloat(t.replace(/[^0-9.]/g,""));return isNaN(e)?"":e}else{l=String(l).replace(/[^0-9.]/g,"");var r=l.split(".");return r.length>2&&(l=r[0]+"."+r.slice(1).join("")),a(this)._CIOriginalVal(l)}else return arguments.length===0?a(this)._CIOriginalVal():a(this)._CIOriginalVal(l)}),this.data("_currencyInput")?this:(this.data("_currencyInput",!0),this.on("focus",function(){a(this).select()}),this.on("input",function(l){var t=this.selectionStart,e=a(this)._CIOriginalVal(),i=e.length;a(this).val(e);var r=a(this)._CIOriginalVal().length;r>i?t+=r-i:r2&&(e=i[0]+"."+i.slice(1).join("")),a(this)._CIOriginalVal(e);var r=e.length;r0){var l=o/s*100;this.$downPaymentPercent._CIOriginalVal(l.toFixed(1))}},syncDownPaymentFromPercent:function(){var s=this.$homePrice.val(),o=this.$downPaymentPercent.val();if(s&&s>0&&o!==""&&o>=0){var l=Math.round(s*o/100);this.$downPayment.val(l)}},calculate:function(){var s=this.$homePrice.val()||0,o=this.$downPayment.val()||0,l=parseInt(this.$loanTerm.val(),10),t=this.$interestRate.val()||0,e=s-o;e<0&&(e=0);var i=t/100/12,r=l*12,u=0,h=0;if(e>0&&i>0&&r>0){var m=Math.pow(1+i,r);u=e*(i*m)/(m-1),h=u*r-e}else e>0&&i===0&&(u=e/r,h=0);this.$monthlyPayment.text(this.formatCurrencyDisplay(u)),this.$principalInterest.text(this.formatCurrencyDisplay(u)),this.$loanAmount.text(this.formatCurrencyDisplay(e)),this.$totalInterest.text(this.formatCurrencyDisplay(h))}};a(document).ready(function(){n.init()})})(jQuery);(function(a){a(function(){})})(jQuery); diff --git a/wp-content/themes/homeproz/inc/ajax-handlers.php b/wp-content/themes/homeproz/inc/ajax-handlers.php index b6a3842d..eaeae88a 100755 --- a/wp-content/themes/homeproz/inc/ajax-handlers.php +++ b/wp-content/themes/homeproz/inc/ajax-handlers.php @@ -34,6 +34,19 @@ function homeproz_ajax_filter_properties() { $sort = isset($_POST['sort']) ? sanitize_text_field($_POST['sort']) : 'newest'; $paged = isset($_POST['paged']) ? intval($_POST['paged']) : 1; + // Map bounds and center (for map-synced list view) + $bounds = null; + $center = null; + $has_map_bounds = false; + + if (isset($_POST['bounds']) && is_array($_POST['bounds']) && count($_POST['bounds']) === 4) { + $bounds = array_map('floatval', $_POST['bounds']); + $has_map_bounds = true; + } + if (isset($_POST['center']) && is_array($_POST['center']) && count($_POST['center']) === 2) { + $center = array_map('floatval', $_POST['center']); + } + // Build filter args for count and properties $per_page = 12; $filter_args = array( @@ -55,6 +68,9 @@ function homeproz_ajax_filter_properties() { if ($beds) { $filter_args['min_beds'] = $beds; } + if ($bounds) { + $filter_args['bounds'] = $bounds; + } // Get total count efficiently from database $total = mls_get_property_count($filter_args); @@ -64,10 +80,16 @@ function homeproz_ajax_filter_properties() { $mls_args = array_merge($filter_args, array( 'limit' => $per_page, 'offset' => ($paged - 1) * $per_page, - 'orderby' => 'modification_timestamp', - 'order' => 'DESC', )); + // If we have map center, sort by distance; otherwise by date + if ($center) { + $mls_args['center'] = $center; + } else { + $mls_args['orderby'] = 'modification_timestamp'; + $mls_args['order'] = 'DESC'; + } + // Fetch only the properties we need for this page $paged_properties = mls_get_properties($mls_args); @@ -80,8 +102,11 @@ function homeproz_ajax_filter_properties() { 0) : ?> Showing + + in view + - No properties found + No properties in view

diff --git a/wp-content/themes/homeproz/template-parts/property/property-filters.js b/wp-content/themes/homeproz/template-parts/property/property-filters.js index 4fa985f4..1584aaab 100755 --- a/wp-content/themes/homeproz/template-parts/property/property-filters.js +++ b/wp-content/themes/homeproz/template-parts/property/property-filters.js @@ -113,19 +113,24 @@ var self = this; var bounds = this.map.getBounds(); + var center = this.map.getCenter(); var zoom = this.map.getZoom(); this.isLoading = true; + // Bounds array for both map clusters and property list + var boundsArray = [ + bounds.getSouthWest().lat, + bounds.getSouthWest().lng, + bounds.getNorthEast().lat, + bounds.getNorthEast().lng + ]; + var centerArray = [center.lat, center.lng]; + var requestData = { action: 'mls_get_clusters', zoom: zoom, - bounds: [ - bounds.getSouthWest().lat, - bounds.getSouthWest().lng, - bounds.getNorthEast().lat, - bounds.getNorthEast().lng - ], + bounds: boundsArray, status: this.currentFilters.status || 'Active', property_type: this.currentFilters.property_type || '', city: this.currentFilters.city || '', @@ -134,6 +139,9 @@ min_beds: this.currentFilters.min_beds || '' }; + // Also update the property list with the same viewport + PropertyFilters.updateFromMap(boundsArray, centerArray); + $.ajax({ url: homeprozMapData.clusterEndpoint, type: 'GET', @@ -518,6 +526,9 @@ // State isFirstLoad: true, isLoading: false, + mapBounds: null, // Current map viewport bounds + mapCenter: null, // Current map center for distance sorting + isMapUpdate: false, // Flag to prevent map->filter->map loop /** * Initialize @@ -597,6 +608,18 @@ } }, + /** + * Update property list based on map viewport + * Called by PropertyMap when map moves/zooms + */ + updateFromMap: function(bounds, center) { + this.mapBounds = bounds; + this.mapCenter = center; + this.isMapUpdate = true; + // Reset to page 1 when map viewport changes + this.filterProperties(1, false); + }, + /** * Get page number from URL hash */ @@ -629,35 +652,47 @@ this.$results.html('
'); } + // Build request data + var requestData = { + action: 'homeproz_filter_properties', + nonce: homeprozAjax.nonce, + property_type: formData.property_type, + property_location: formData.property_location, + min_price: formData.min_price, + max_price: formData.max_price, + beds: formData.beds, + paged: page + }; + + // Add map bounds and center if available + if (this.mapBounds) { + requestData.bounds = this.mapBounds; + } + if (this.mapCenter) { + requestData.center = this.mapCenter; + } + $.ajax({ url: homeprozAjax.ajaxUrl, type: 'POST', - data: { - action: 'homeproz_filter_properties', - nonce: homeprozAjax.nonce, - property_type: formData.property_type, - property_location: formData.property_location, - min_price: formData.min_price, - max_price: formData.max_price, - beds: formData.beds, - paged: page - }, + data: requestData, success: function(response) { if (response.success) { self.$results.html(response.data.html); self.isFirstLoad = false; - // Update map with new filter params - if (response.data.filters) { + // Update map with new filter params (but not if this was triggered by map move) + if (response.data.filters && !self.isMapUpdate) { PropertyMap.updateFilters(response.data.filters); } + self.isMapUpdate = false; // Recalculate layout after content update if (typeof LayoutCalculator !== 'undefined') { LayoutCalculator.calculate(); } - // Update URL + // Update URL (skip when map-triggered) if (updateHistory) { self.updateUrl(formData, page); }