diff --git a/wp-content/themes/homeproz/dist/assets/main.js b/wp-content/themes/homeproz/dist/assets/main.js index aa3b78b3..c043d3eb 100644 --- a/wp-content/themes/homeproz/dist/assets/main.js +++ b/wp-content/themes/homeproz/dist/assets/main.js @@ -1 +1 @@ -(function(a){var f=a(".menu-toggle"),i=a(".mobile-navigation");f.length&&(f.on("click",function(){var s=a(this).attr("aria-expanded")==="true";a(this).attr("aria-expanded",!s),i.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"&&i.hasClass("is-open")&&(f.attr("aria-expanded","false"),i.removeClass("is-open"),a("body").removeClass("mobile-menu-open"))}),a(document).on("click",function(s){i.hasClass("is-open")&&!a(s.target).closest(".mobile-navigation").length&&!a(s.target).closest(".menu-toggle").length&&(f.attr("aria-expanded","false"),i.removeClass("is-open"),a("body").removeClass("mobile-menu-open"))}))})(jQuery);(function(a){var f=6e3,i=1450,s=1e3,l=[],o=0,u=null,d=!1,t=!1,e=null;function r(){if(a(".Home_Page").length&&(e=a(".hero-split-image"),!!e.length)){var g=e.data("gallery-images");!g||!g.length||(l=g,n(),a(window).on("resize",b(n,150)))}}function n(){var g=a(window).width();g>=i?d||h():d&&c()}function h(){d=!0,t||(p(),t=!0),u=setInterval(m,f)}function c(){d=!1,u&&(clearInterval(u),u=null)}function p(){a.each(l,function(g,v){var y=new Image;y.src=v})}function m(){o=(o+1)%l.length;var g=l[o],v=a('
');v.css({position:"absolute",top:0,left:0,right:0,bottom:0,"background-image":"url("+g+")","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}),e.css("position","relative"),e.append(v),v[0].offsetHeight,v.css({opacity:1,transform:"scale(1)"}),setTimeout(function(){e.css("background-image","url("+g+")"),v.remove()},s)}function b(g,v){var y;return function(){var I=this,P=arguments;clearTimeout(y),y=setTimeout(function(){g.apply(I,P)},v)}}a(document).ready(r)})(jQuery);(function(a){if(a("body").hasClass("Home_Page")){var f={grid:null,emptyMessage:null,listings:[],init:function(){this.grid=a("#featured-listings-grid"),this.emptyMessage=a("#featured-listings-empty"),this.grid.length&&(this.loadListingsData(),this.renderListings())},loadListingsData:function(){var i=document.getElementById("featured-mls-data");if(!i){this.listings=[];return}try{this.listings=JSON.parse(i.textContent)}catch(s){console.error("Failed to parse featured listings data:",s),this.listings=[]}},shuffleArray:function(i){for(var s=i.slice(),l=s.length-1;l>0;l--){var o=Math.floor(Math.random()*(l+1)),u=s[l];s[l]=s[o],s[o]=u}return s},renderListings:function(){if(!this.listings||this.listings.length===0){this.grid.hide(),this.emptyMessage.show();return}for(var i=this.shuffleArray(this.listings),s=i.slice(0,3),l="",o=0;o',i.bedrooms&&(o+='
  • '+i.bedrooms+" "+s+"
  • "),i.bathrooms&&(o+='
  • '+i.bathrooms+" "+l+"
  • "),i.sqft&&(o+='
  • '+i.sqft.toLocaleString()+" sqft
  • "),o+=""),'
    Active
    '+this.escapeHtml(i.price_formatted)+'

    '+this.escapeHtml(i.address)+"

    "+o+'View Details
    '},escapeHtml:function(i){if(!i)return"";var s=document.createElement("div");return s.textContent=i,s.innerHTML}};a(document).ready(function(){f.init()})}})(jQuery);(function(a){var f={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(n){var h=n.getChildCount(),c="small";return h>=100?c="large":h>=10&&(c="medium"),L.divIcon({html:"
    "+h+"
    ",className:"marker-cluster marker-cluster-"+c,iconSize:L.point(40,40)})}}),this.map.addLayer(this.markerCluster);var r=this;this.map.on("moveend zoomend",function(){r.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(),r=this.map.getCenter(),n=this.map.getZoom();this.isLoading=!0;var h=[e.getSouthWest().lat,e.getSouthWest().lng,e.getNorthEast().lat,e.getNorthEast().lng],c=[r.lat,r.lng],p={action:"mls_get_clusters",zoom:n,bounds:h,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||""};i.updateFromMap(h,c),a.ajax({url:homeprozMapData.clusterEndpoint,type:"GET",data:p,success:function(m){if(m.success){var b=m.data;switch(t.currentMode=b.type,b.type){case"density":t.renderDensity(b.dots);break;case"clusters":t.renderClusters(b.clusters);break;case"markers":t.renderMarkers(b.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,r=this.map.getZoom();t.forEach(function(n){var h=e.getDensityColor(n.count,r),c=e.getDensitySize(n.count,r),p=L.divIcon({html:'
    ',className:"density-dot-container",iconSize:[c,c],iconAnchor:[c/2,c/2]}),m=L.marker([n.lat,n.lng],{icon:p});m.on("click",function(){e.map.setView([n.lat,n.lng],e.map.getZoom()+2)}),m.bindTooltip(n.count+" properties",{className:"density-tooltip"}),e.densityLayer.addLayer(m)})},getDensityThreshold:function(t){return Math.max(40,Math.round(600/Math.pow(1.4,t-3)))},getDensityColor:function(t,e){var r=this.getDensityThreshold(e),n=t/r;return n>=1.5?"rgba(180, 83, 9, 0.8)":n>=1?"rgba(217, 119, 6, 0.8)":n>=.6?"rgba(245, 158, 11, 0.8)":n>=.3?"rgba(234, 179, 8, 0.8)":n>=.15?"rgba(132, 204, 22, 0.8)":"rgba(34, 197, 94, 0.8)"},getDensitySize:function(t,e){var r=this.getDensityThreshold(e),n=t/r;return n>=1.5?11:n>=1?10:n>=.6?8:n>=.3?7:6},renderClusters:function(t){this.clearAllLayers();var e=this;t.forEach(function(r){var n="small";r.count>=100?n="large":r.count>=10&&(n="medium");var h=L.divIcon({html:"
    "+r.count+"
    ",className:"marker-cluster marker-cluster-"+n+" server-cluster",iconSize:L.point(40,40)}),c=L.marker([r.lat,r.lng],{icon:h});c.on("click",function(){e.map.setView([r.lat,r.lng],e.map.getZoom()+2)});var p="$"+e.formatNumber(r.min_price);r.max_price!==r.min_price&&(p+=" - $"+e.formatNumber(r.max_price)),c.bindTooltip(r.count+" properties
    "+p,{className:"cluster-tooltip"}),e.clusterLayer.addLayer(c)})},renderMarkers:function(t){this.clearAllLayers(),this.selectedPropertyId=null,this.hoveredPropertyId=null,a(".property-card").removeClass("property-card-highlighted");var e=this,r=[];t.forEach(function(n,h){if(n.lat&&n.lng){var c=L.marker([n.lat,n.lng],{icon:e.createIcon("red"),zIndexOffset:e.baseZIndex+h});c.propertyId=n.id,c.defaultZIndex=e.baseZIndex+h,c.bindPopup('
    '+n.price+"
    "+n.address+'
    View Details
    '),c.on("click",function(p){e.onMarkerClick(n.id)}),r.push(c),e.markers[n.id]=c}}),this.markerCluster.addLayers(r)},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 r=a("#property-"+t);if(r.length){var n=r.offset().top,h=n+r.outerHeight(),c=a(window).scrollTop(),p=c+a(window).height(),m=n>=c&&h<=p;m?e.flashCard(r):a("html, body").animate({scrollTop:n-120},400,function(){e.flashCard(r)})}}},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 r=this.markers[t];r&&r.setIcon(this.createIcon(e))},setMarkerZIndex:function(t,e){var r=this.markers[t];r&&r.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))})}},i={$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 r=t.getPageFromUrl(a(this).attr("href"));t.filterProperties(r)}),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 r=a(this).attr("name");t.has(r)&&a(this).val(t.get(r))}),this.$form.find('input[type="text"]').each(function(){var r=a(this).attr("name");t.has(r)&&a(this).val(t.get(r))});var e=this.getPageFromHash();e>1&&this.filterProperties(e,!1)},updateFromMap:function(t,e){this.mapBounds=t,this.mapCenter=e,this.isMapUpdate=!0,o.isEnabled&&u.reset(),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 r=this,n=this.getFormData();this.isLoading=!0,this.$filters.addClass("is-loading"),this.isFirstLoad&&this.$results.html('
    ');var h={action:"homeproz_filter_properties",nonce:homeprozAjax.nonce,property_type:n.property_type,property_location:n.property_location,zip:n.zip,min_price:n.min_price,max_price:n.max_price,beds:n.beds,paged:t};this.mapBounds&&(h.bounds=this.mapBounds),this.mapCenter&&(h.center=this.mapCenter),a.ajax({url:homeprozAjax.ajaxUrl,type:"POST",data:h,success:function(c){c.success&&(r.$results.html(c.data.html),r.isFirstLoad=!1,c.data.filters&&!r.isMapUpdate&&f.updateFilters(c.data.filters),r.isMapUpdate=!1,typeof l<"u"&&l.calculate(),window.innerWidth>=1024&&a(".is-map-view").length&&(u.destroy(),setTimeout(function(){u.init()},100)),e&&r.updateUrl(n,t),t>1&&a("html, body").animate({scrollTop:r.$filters.offset().top-100},300))},error:function(){r.$results.html('

    Error

    Something went wrong. Please try again.

    ')},complete:function(){r.isLoading=!1,r.$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()||"",zip:this.$form.find('[name="zip"]').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 r=new URL(homeprozAjax.archiveUrl);for(var n in t)t[n]&&r.searchParams.set(n,t[n]);e>1?r.hash="page="+e:r.hash="",history.replaceState(null,"",r.toString())},getPageFromUrl:function(t){var e=t.match(/#page=(\d+)/);if(e)return parseInt(e[1]);var r=t.match(/[?&]paged=(\d+)/);return r?parseInt(r[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"&&(f.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&&o.isEnabled&&u.destroy(),!t&&this.isAboveBreakpoint&&(this.isMapView?(e.removeClass("is-grid-view").addClass("is-map-view"),!this.mapInitialized&&typeof homeprozMapData<"u"?(f.init(homeprozMapData.initialFilters||{}),this.mapInitialized=!0):f.map&&setTimeout(function(){f.map.invalidateSize()},100),setTimeout(function(){u.init()},200)):e.removeClass("is-map-view").addClass("is-grid-view"),typeof l<"u"&&setTimeout(function(){l.calculate()},150))},setMapView:function(t){this.isMapView=t}},l={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"),r=t.hasClass("is-map-view"),n=e.width();r?this.calculateMapLayout(n):this.calculateGridLayout(n)},calculateMapLayout:function(t){for(var e=5;e>=1;e--){var r=e*this.cardWidth+(e-1)*this.cardGap,n=(this.mapGap+r)/(1-this.mapRatio);if(n<=t){this.setProperties(n,e,".property-map-layout"),this.setProperties(n,e,".property-list-container");return}}var r=this.cardWidth,n=(this.mapGap+r)/(1-this.mapRatio);this.setProperties(Math.min(n,t),1,".property-map-layout"),this.setProperties(Math.min(n,t),1,".property-list-container")},calculateGridLayout:function(t){for(var e=6;e>=1;e--){var r=e*this.cardWidth+(e-1)*this.cardGap;if(r<=t){this.setProperties(r,e,".grid-view-container");return}}this.setProperties(this.cardWidth,1,".grid-view-container")},setProperties:function(t,e,r){var n=a(r);n.length&&(n.css("--layout-width",t+"px"),n.css("--card-columns",e))},clearProperties:function(){a(".property-map-layout, .grid-view-container, .property-list-container").css({"--layout-width":"","--card-columns":""})}},o={pages:{},visibleRange:{first:1,last:1},totalPages:0,totalPosts:0,pendingRequests:{},isEnabled:!1,config:{maxPagesInDOM:5,bufferPages:2,cardsPerPage:12}},u={$container:null,$grid:null,$topSentinel:null,$bottomSentinel:null,$topLoader:null,$bottomLoader:null,topObserver:null,bottomObserver:null,scrollContainer:null,init:function(){if(!(window.innerWidth<1024||!a(".is-map-view").length)&&(this.$container=a(".property-list-container"),this.$grid=this.$container.find(".properties-grid"),!(!this.$container.length||!this.$grid.length))){var t=this.$container.find(".properties-meta"),e=t.find(".properties-count strong").text();o.totalPosts=parseInt(e)||0,o.totalPages=Math.ceil(o.totalPosts/o.config.cardsPerPage),!(o.totalPages<=1)&&(this.resetState(),this.setupDOM(),this.wrapInitialCards(),this.setupObservers(),o.isEnabled=!0,this.$container.addClass("infinite-scroll-enabled"))}},resetState:function(){o.pages={},o.visibleRange={first:1,last:1},o.pendingRequests={}},setupDOM:function(){this.$container.find(".infinite-scroll-sentinel, .infinite-scroll-loader").remove(),this.$topSentinel=a('
    '),this.$topLoader=a('
    '),this.$bottomSentinel=a('
    '),this.$bottomLoader=a('
    '),this.$grid.before(this.$topSentinel).before(this.$topLoader),this.$grid.after(this.$bottomLoader).after(this.$bottomSentinel),this.scrollContainer=this.$container[0]},wrapInitialCards:function(){var t=this.$grid.find(".property-card");if(t.length){var e=a('
    ');t.wrapAll(e);var r=this.$grid.find('.infinite-scroll-page[data-page="1"]').outerHeight();o.pages[1]={html:null,height:r,loaded:!0},o.visibleRange={first:1,last:1}}},setupObservers:function(){var t=this,e={root:this.scrollContainer,rootMargin:"400px 0px",threshold:0};this.bottomObserver=new IntersectionObserver(function(r){r.forEach(function(n){n.isIntersecting&&t.loadNextPage()})},e),this.topObserver=new IntersectionObserver(function(r){r.forEach(function(n){n.isIntersecting&&t.loadPrevPage()})},e),this.bottomObserver.observe(this.$bottomSentinel[0]),this.topObserver.observe(this.$topSentinel[0])},loadNextPage:function(){var t=o.visibleRange.last+1;if(!(t>o.totalPages)){if(o.pages[t]&&o.pages[t].loaded){this.showPage(t,"append");return}o.pendingRequests[t]||this.loadPage(t,"append")}},loadPrevPage:function(){var t=o.visibleRange.first-1;if(!(t<1)){if(o.pages[t]&&o.pages[t].loaded){this.showPage(t,"prepend");return}o.pendingRequests[t]||this.loadPage(t,"prepend")}},loadPage:function(t,e){var r=this,n=e==="prepend"?this.$topLoader:this.$bottomLoader;n.addClass("is-loading");var h=i.getFormData(),c={action:"homeproz_filter_properties",nonce:homeprozAjax.nonce,property_type:h.property_type,property_location:h.property_location,zip:h.zip,min_price:h.min_price,max_price:h.max_price,beds:h.beds,paged:t,cards_only:"true"};i.mapBounds&&(c.bounds=i.mapBounds),i.mapCenter&&(c.center=i.mapCenter);var p=a.ajax({url:homeprozAjax.ajaxUrl,type:"POST",data:c,success:function(m){m.success&&m.data.html&&r.renderPage(t,m.data.html,e)},complete:function(){n.removeClass("is-loading"),delete o.pendingRequests[t]}});o.pendingRequests[t]=p},renderPage:function(t,e,r){var n=this.saveScrollAnchor(),h=a('
    ');if(h.html(e),r==="prepend"){var c=this.$grid.find(".infinite-scroll-page").first();c.length?c.before(h):this.$grid.prepend(h),o.visibleRange.first=t}else{var p=this.$grid.find(".infinite-scroll-page").last();p.length?p.after(h):this.$grid.append(h),o.visibleRange.last=t}o.pages[t]={html:e,height:h.outerHeight(),loaded:!0},this.restoreScrollAnchor(n),typeof d<"u"&&d.loadVisibleImages(),this.cleanupExcessPages()},showPage:function(t,e){var r=o.pages[t];if(!(!r||!r.html)){var n=this.$grid.find('.infinite-scroll-placeholder[data-page="'+t+'"]');if(n.length){var h=this.saveScrollAnchor(),c=a('
    ');c.html(r.html),n.replaceWith(c),this.restoreScrollAnchor(h)}e==="prepend"?o.visibleRange.first=t:o.visibleRange.last=t,typeof d<"u"&&d.loadVisibleImages(),this.cleanupExcessPages()}},cleanupExcessPages:function(){var t=o.visibleRange,e=o.config,r=t.last-t.first+1;if(!(r<=e.maxPagesInDOM)){for(;t.first');h.replaceWith(m),o.pages[n]&&(o.pages[n].height=c),this.restoreScrollAnchor(p)}t.first++}for(;t.last>t.first+e.bufferPages;){var n=t.last,h=this.$grid.find('.infinite-scroll-page[data-page="'+n+'"]');if(h.length){var c=h.outerHeight(),p=this.saveScrollAnchor(),m=a('
    ');h.replaceWith(m),o.pages[n]&&(o.pages[n].height=c),this.restoreScrollAnchor(p)}t.last--}}},saveScrollAnchor:function(){if(!this.scrollContainer)return null;var t=this.scrollContainer.getBoundingClientRect(),e=this.$grid.find(".property-card"),r=null;return e.each(function(){var n=this.getBoundingClientRect();if(n.bottom>t.top&&n.top1&&(this.scrollContainer.scrollTop+=h)}},reset:function(){for(var t in o.pendingRequests)o.pendingRequests[t]&&o.pendingRequests[t].abort();this.resetState(),this.scrollContainer&&(this.scrollContainer.scrollTop=0)},destroy:function(){if(o.isEnabled){for(var t in o.pendingRequests)o.pendingRequests[t]&&o.pendingRequests[t].abort();this.topObserver&&(this.topObserver.disconnect(),this.topObserver=null),this.bottomObserver&&(this.bottomObserver.disconnect(),this.bottomObserver=null),this.$container&&(this.$container.find(".infinite-scroll-sentinel, .infinite-scroll-loader").remove(),this.$container.removeClass("infinite-scroll-enabled")),this.$grid&&(this.$grid.find(".infinite-scroll-page").children().unwrap(),this.$grid.find(".infinite-scroll-placeholder").remove()),this.resetState(),o.isEnabled=!1}}},d={init:function(){this.loadVisibleImages(),this.bindEvents()},bindEvents:function(){var t=this;if(typeof MutationObserver<"u"){var e=new MutationObserver(function(h){t.loadVisibleImages()}),r=document.getElementById("property-results");r&&e.observe(r,{childList:!0,subtree:!0});var n=document.getElementById("property-results-grid");n&&e.observe(n,{childList:!0,subtree:!0})}},loadVisibleImages:function(){var t=this;a(".property-card-image.is-loading[data-bg]").each(function(){t.loadImage(a(this))})},loadImage:function(t){var e=t.data("bg");if(e){t.removeClass("is-loading").removeAttr("data-bg");var r=new Image;r.onload=function(){t.css("background-image","url("+e+")"),t.addClass("is-loaded")},r.onerror=function(){t.addClass("is-loaded"),t.removeClass("has-image")},r.src=e}}};a(function(){i.init(),s.init(),l.init(),d.init(),window.innerWidth>=1024&&a(".is-map-view").length&&setTimeout(function(){u.init()},300)})})(jQuery);(function(a){var f={$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 i=a("#gallery-images-data");i.length&&(this.images=JSON.parse(i.text())),this.images.length!==0&&(this.calculateThumbnailsPerPage(),this.bindEvents(),this.bindSwipeEvents(),this.updateThumbnailNavigation(),this.setupThumbnailLoading(),this.preloadThumbnailPages(0,2),this.images.length>1&&this.startAutoplay())}},calculateThumbnailsPerPage:function(){a(window).width()<=640?this.thumbnailsPerPage=4:this.thumbnailsPerPage=5},bindEvents:function(){var i=this;this.$thumbnails.on("click",function(s){s.stopPropagation();var l=parseInt(a(this).data("index"));i.stopAutoplay(),i.setMainImage(l,!1)}),this.$playbackBtn.on("click",function(s){s.stopPropagation(),s.preventDefault(),i.isPlaying?i.stopAutoplay():i.startAutoplay()}),this.$prevBtn.on("click",function(){i.stopAutoplay(),i.prevThumbnailPage()}),this.$nextBtn.on("click",function(){i.stopAutoplay(),i.nextThumbnailPage()}),this.$gallery.find("[data-lightbox-trigger]").on("click",function(s){if(i.isSwiping){i.isSwiping=!1;return}i.stopAutoplay(),i.openLightbox(i.currentIndex)}),this.$lightbox.find(".lightbox-close, .lightbox-overlay").on("click",function(){i.closeLightbox()}),this.$lightbox.find(".lightbox-prev").on("click",function(){i.slideLightboxImage("prev")}),this.$lightbox.find(".lightbox-next").on("click",function(){i.slideLightboxImage("next")}),a(document).on("keydown",function(s){if(i.$lightbox.is('[aria-hidden="false"]'))switch(s.key){case"Escape":i.closeLightbox();break;case"ArrowLeft":i.slideLightboxImage("prev");break;case"ArrowRight":i.slideLightboxImage("next");break}}),a(window).on("resize",function(){i.calculateThumbnailsPerPage(),i.updateThumbnailNavigation()})},bindSwipeEvents:function(){var i=this;this.$mainImageContainer[0].addEventListener("touchstart",function(s){i.handleSwipeStart(s)},{passive:!0}),this.$mainImageContainer[0].addEventListener("touchend",function(s){i.handleMainGallerySwipeEnd(s)},{passive:!0}),this.$lightboxImageContainer[0].addEventListener("touchstart",function(s){i.handleSwipeStart(s)},{passive:!0}),this.$lightboxImageContainer[0].addEventListener("touchend",function(s){i.handleLightboxSwipeEnd(s)},{passive:!0})},handleSwipeStart:function(i){i.touches.length===1&&(this.swipeStartX=i.touches[0].clientX,this.swipeStartY=i.touches[0].clientY)},handleMainGallerySwipeEnd:function(i){if(i.changedTouches.length===1){var s=i.changedTouches[0].clientX-this.swipeStartX,l=i.changedTouches[0].clientY-this.swipeStartY;Math.abs(s)>Math.abs(l)&&Math.abs(s)>this.swipeThreshold&&(this.isSwiping=!0,this.stopAutoplay(),s>0?this.slideMainImage("prev"):this.slideMainImage("next"))}},handleLightboxSwipeEnd:function(i){if(i.changedTouches.length===1){var s=i.changedTouches[0].clientX-this.swipeStartX,l=i.changedTouches[0].clientY-this.swipeStartY;Math.abs(s)>Math.abs(l)&&Math.abs(s)>this.swipeThreshold&&(s>0?this.slideLightboxImage("prev"):this.slideLightboxImage("next"))}},startAutoplay:function(){var i=this;this.images.length<=1||(this.isPlaying=!0,this.$playbackBtn.addClass("is-playing"),this.$playbackBtn.attr("aria-label","Pause slideshow"),this.autoplayInterval=setInterval(function(){i.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 i=this.currentIndex+1;i>=this.images.length&&(i=0),this.setMainImage(i,!0)}},slideMainImage:function(i){var s=this;if(!(this.isTransitioning||this.images.length<=1)){var l;i==="prev"?(l=this.currentIndex-1,l<0&&(l=this.images.length-1)):(l=this.currentIndex+1,l>=this.images.length&&(l=0)),this.isTransitioning=!0;var o=this.images[l],u=i==="next"?"100%":"-100%",d=i==="next"?"-100%":"100%",t=a('');t.attr("src",o.url),t.attr("alt",o.alt||"Property photo"),t.css({position:"absolute",top:0,left:0,width:"100%",height:"100%","object-fit":"cover",transform:"translateX("+u+")","z-index":2,"border-radius":"0.5rem"}),this.$mainImageContainer.css({position:"relative",overflow:"hidden"}),this.$mainImageContainer.append(t),this.$mainImage.css({transition:"transform "+this.slideDuration+"ms ease-out"}),t.css({transition:"transform "+this.slideDuration+"ms ease-out"}),t[0].offsetHeight,this.$mainImage.css("transform","translateX("+d+")"),t.css("transform","translateX(0)"),setTimeout(function(){s.$mainImage.attr("src",o.url),s.$mainImage.attr("alt",o.alt||"Property photo"),s.$mainImage.css({transition:"",transform:""}),t.remove(),s.isTransitioning=!1},this.slideDuration),this.currentIndex=l,this.$thumbnails.removeClass("is-active"),this.$thumbnails.filter('[data-index="'+l+'"]').addClass("is-active"),this.scrollToThumbnail(l)}},setMainImage:function(i,s){var l=this;if(!(i<0||i>=this.images.length)&&!this.isTransitioning){var o=this.images[i];if(s){this.isTransitioning=!0;var u=a('');u.attr("src",o.url),u.attr("alt",o.alt||"Property photo"),u.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(u),u[0].offsetHeight,u.css({opacity:1,transform:"scale(1)"}),setTimeout(function(){l.$mainImage.attr("src",o.url),l.$mainImage.attr("alt",o.alt||"Property photo"),u.remove(),l.isTransitioning=!1},this.fadeDuration)}else this.$mainImage.attr("src",o.url),this.$mainImage.attr("alt",o.alt||"Property photo");this.currentIndex=i,this.$thumbnails.removeClass("is-active"),this.$thumbnails.filter('[data-index="'+i+'"]').addClass("is-active"),this.scrollToThumbnail(i)}},scrollToThumbnail:function(i){var s=Math.floor(i/this.thumbnailsPerPage);s!==this.thumbnailPage&&(this.thumbnailPage=s,this.scrollThumbnails())},scrollThumbnails:function(){var i=this.$gallery.find(".gallery-thumbnails"),s=this.$thumbnails.first().outerWidth(!0),l=this.thumbnailPage*this.thumbnailsPerPage*s;i.css("transform","translateX(-"+l+"px)"),this.updateThumbnailNavigation()},updateThumbnailNavigation:function(){var i=Math.ceil(this.images.length/this.thumbnailsPerPage);this.$prevBtn.prop("disabled",this.thumbnailPage===0),this.$nextBtn.prop("disabled",this.thumbnailPage>=i-1),i<=1?(this.$prevBtn.hide(),this.$nextBtn.hide()):(this.$prevBtn.show(),this.$nextBtn.show())},prevThumbnailPage:function(){this.thumbnailPage>0&&(this.thumbnailPage--,this.scrollThumbnails(),this.preloadPrevThumbnailPage())},nextThumbnailPage:function(){var i=Math.ceil(this.images.length/this.thumbnailsPerPage);this.thumbnailPage=this.images.length&&(l=0)),this.isTransitioning=!0;var o=this.images[l],u=i==="next"?"100%":"-100%",d=i==="next"?"-100%":"100%",t=a('');t.attr("src",o.url),t.attr("alt",o.alt||"Property photo"),t.css({position:"absolute","max-width":"100%","max-height":"calc(100vh - 8rem)","object-fit":"contain",transform:"translateX("+u+")",left:"50%",top:"50%","margin-left":"-45vw","margin-top":"calc(-50vh + 4rem)"}),this.$lightboxImageContainer.css({position:"relative",overflow:"hidden"}),this.$lightboxImageContainer.append(t),this.$lightboxImage.css({transition:"transform "+this.slideDuration+"ms ease-out"}),t.css({transition:"transform "+this.slideDuration+"ms ease-out"}),t[0].offsetHeight,this.$lightboxImage.css("transform","translateX("+d+")"),t.css("transform","translateX(0)"),setTimeout(function(){s.$lightboxImage.attr("src",o.url),s.$lightboxImage.attr("alt",o.alt||"Property photo"),s.$lightboxImage.css({transition:"",transform:""}),t.remove(),s.isTransitioning=!1,s.$lightboxCounter.text(l+1)},this.slideDuration),this.currentIndex=l}},prevImage:function(){this.slideLightboxImage("prev")},nextImage:function(){this.slideLightboxImage("next")},updateLightboxImage:function(){var i=this.images[this.currentIndex];this.$lightboxImage.attr("src",i.url),this.$lightboxImage.attr("alt",i.alt||"Property photo"),this.$lightboxCounter.text(this.currentIndex+1)},setupThumbnailLoading:function(){this.$thumbnails.each(function(){var i=a(this),s=i.find("img");i.addClass("is-loading"),i.find(".thumbnail-spinner").length||i.append('
    '),s[0].complete?i.removeClass("is-loading"):(s.on("load",function(){i.removeClass("is-loading")}),s.on("error",function(){i.removeClass("is-loading")}))})},preloadThumbnailPages:function(i,s){for(var l=this,o=i*this.thumbnailsPerPage,u=Math.min((i+s)*this.thumbnailsPerPage,this.images.length),d=o;d=0&&this.preloadThumbnailPages(i,1)}};a(function(){f.init()})})(jQuery);(function(a){if(!a(".mortgage-calculator-main").length)return;let f=!1;a.fn.currencyInput=function(s=!0){return this.data("ci_show_symbol",s),f||(f=!0,a.fn._CIOriginalVal=a.fn.val,a.fn.val=function(o){if(a(this).data("_currencyInput"))if(arguments.length===0){var u=a(this)._CIOriginalVal();if(u=="")return"";var d=parseInt(u.replace(/[^0-9]/g,""));return d}else{if(o=String(o).replace(/[^0-9]/g,""),o!=""){var t=parseInt(o).toLocaleString("en-US",{style:"currency",currency:"USD",minimumFractionDigits:0,maximumFractionDigits:0});return a(this).data("ci_show_symbol")||(t=t.replace("$","")),a(this)._CIOriginalVal(t)}return a(this)._CIOriginalVal(o)}else if(a(this).data("_percentInput"))if(arguments.length===0){var u=a(this)._CIOriginalVal();if(u=="")return"";var d=parseFloat(u.replace(/[^0-9.]/g,""));return isNaN(d)?"":d}else{o=String(o).replace(/[^0-9.]/g,"");var e=o.split(".");return e.length>2&&(o=e[0]+"."+e.slice(1).join("")),a(this)._CIOriginalVal(o)}else return arguments.length===0?a(this)._CIOriginalVal():a(this)._CIOriginalVal(o)}),this.data("_currencyInput")?this:(this.data("_currencyInput",!0),this.on("focus",function(){a(this).select()}),this.on("input",function(o){var u=this.selectionStart,d=a(this)._CIOriginalVal(),t=d.length;a(this).val(d);var e=a(this)._CIOriginalVal().length;e>t?u+=e-t:e2&&(d=t[0]+"."+t.slice(1).join("")),a(this)._CIOriginalVal(d);var e=d.length;e0){var o=l/s*100;this.$downPaymentPercent._CIOriginalVal(o.toFixed(1))}},syncDownPaymentFromPercent:function(){var s=this.$homePrice.val(),l=this.$downPaymentPercent.val();if(s&&s>0&&l!==""&&l>=0){var o=Math.round(s*l/100);this.$downPayment.val(o)}},calculate:function(){var s=this.$homePrice.val()||0,l=this.$downPayment.val()||0,o=parseInt(this.$loanTerm.val(),10),u=this.$interestRate.val()||0,d=s-l;d<0&&(d=0);var t=u/100/12,e=o*12,r=0,n=0;if(d>0&&t>0&&e>0){var h=Math.pow(1+t,e);r=d*(t*h)/(h-1),n=r*e-d}else d>0&&t===0&&(r=d/e,n=0);this.$monthlyPayment.text(this.formatCurrencyDisplay(r)),this.$principalInterest.text(this.formatCurrencyDisplay(r)),this.$loanAmount.text(this.formatCurrencyDisplay(d)),this.$totalInterest.text(this.formatCurrencyDisplay(n))}};a(document).ready(function(){i.init()})})(jQuery);(function(a){a(function(){})})(jQuery); +(function(a){var v=a(".menu-toggle"),i=a(".mobile-navigation");v.length&&(v.on("click",function(){var s=a(this).attr("aria-expanded")==="true";a(this).attr("aria-expanded",!s),i.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"&&i.hasClass("is-open")&&(v.attr("aria-expanded","false"),i.removeClass("is-open"),a("body").removeClass("mobile-menu-open"))}),a(document).on("click",function(s){i.hasClass("is-open")&&!a(s.target).closest(".mobile-navigation").length&&!a(s.target).closest(".menu-toggle").length&&(v.attr("aria-expanded","false"),i.removeClass("is-open"),a("body").removeClass("mobile-menu-open"))}))})(jQuery);(function(a){var v=6e3,i=1450,s=1e3,l=[],h=0,o=null,p=!1,d=!1,t=null;function e(){if(a(".Home_Page").length&&(t=a(".hero-split-image"),!!t.length)){var b=t.data("gallery-images");!b||!b.length||(l=b,r(),a(window).on("resize",m(r,150)))}}function r(){var b=a(window).width();b>=i?p||n():p&&u()}function n(){p=!0,d||(c(),d=!0),o=setInterval(f,v)}function u(){p=!1,o&&(clearInterval(o),o=null)}function c(){a.each(l,function(b,g){var y=new Image;y.src=g})}function f(){h=(h+1)%l.length;var b=l[h],g=a('
    ');g.css({position:"absolute",top:0,left:0,right:0,bottom:0,"background-image":"url("+b+")","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}),t.css("position","relative"),t.append(g),g[0].offsetHeight,g.css({opacity:1,transform:"scale(1)"}),setTimeout(function(){t.css("background-image","url("+b+")"),g.remove()},s)}function m(b,g){var y;return function(){var I=this,P=arguments;clearTimeout(y),y=setTimeout(function(){b.apply(I,P)},g)}}a(document).ready(e)})(jQuery);(function(a){if(a("body").hasClass("Home_Page")){var v={grid:null,emptyMessage:null,listings:[],init:function(){this.grid=a("#featured-listings-grid"),this.emptyMessage=a("#featured-listings-empty"),this.grid.length&&(this.loadListingsData(),this.renderListings())},loadListingsData:function(){var i=document.getElementById("featured-mls-data");if(!i){this.listings=[];return}try{this.listings=JSON.parse(i.textContent)}catch(s){console.error("Failed to parse featured listings data:",s),this.listings=[]}},shuffleArray:function(i){for(var s=i.slice(),l=s.length-1;l>0;l--){var h=Math.floor(Math.random()*(l+1)),o=s[l];s[l]=s[h],s[h]=o}return s},renderListings:function(){if(!this.listings||this.listings.length===0){this.grid.hide(),this.emptyMessage.show();return}for(var i=this.shuffleArray(this.listings),s=i.slice(0,3),l="",h=0;h',i.bedrooms&&(h+='
  • '+i.bedrooms+" "+s+"
  • "),i.bathrooms&&(h+='
  • '+i.bathrooms+" "+l+"
  • "),i.sqft&&(h+='
  • '+i.sqft.toLocaleString()+" sqft
  • "),h+=""),'
    Active
    '+this.escapeHtml(i.price_formatted)+'

    '+this.escapeHtml(i.address)+"

    "+h+'View Details
    '},escapeHtml:function(i){if(!i)return"";var s=document.createElement("div");return s.textContent=i,s.innerHTML}};a(document).ready(function(){v.init()})}})(jQuery);(function(a){var v={pending:{clusters:null,properties:null},timeouts:{clusters:null,properties:null},requestIds:{clusters:0,properties:0},debounceDelay:200,queue:function(t,e,r,n,u){var c=this;this.timeouts[t]&&(clearTimeout(this.timeouts[t]),this.timeouts[t]=null),this.pending[t]&&(this.pending[t].abort(),this.pending[t]=null),this.requestIds[t]++;var f=this.requestIds[t];this.timeouts[t]=setTimeout(function(){c.timeouts[t]=null;var m=e(f);m&&m.then&&(c.pending[t]=m,m.done(function(b){f===c.requestIds[t]&&r(b,f)}),m.fail(function(b,g,y){u&&f===c.requestIds[t]&&g!=="abort"&&u(b,g,y)}),m.always(function(){c.pending[t]===m&&(c.pending[t]=null),n&&f===c.requestIds[t]&&n()}))},this.debounceDelay)},cancel:function(t){var e=t?[t]:["clusters","properties"],r=this;e.forEach(function(n){r.timeouts[n]&&(clearTimeout(r.timeouts[n]),r.timeouts[n]=null),r.pending[n]&&(r.pending[n].abort(),r.pending[n]=null)})},isLoading:function(t){return!!(this.pending[t]||this.timeouts[t])}},i={map:null,markers:{},densityLayer:null,clusterLayer:null,markerCluster:null,selectedPropertyId:null,hoveredPropertyId:null,baseZIndex:400,currentFilters:{},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(n){var u=n.getChildCount(),c="small";return u>=100?c="large":u>=10&&(c="medium"),L.divIcon({html:"
    "+u+"
    ",className:"marker-cluster marker-cluster-"+c,iconSize:L.point(40,40)})}}),this.map.addLayer(this.markerCluster);var r=this;this.map.on("moveend zoomend",function(){r.loadClusters()}),this.bindCardHoverEvents(),this.loadClusters()}},loadClusters:function(){if(this.map){var t=this,e=this.map.getBounds(),r=this.map.getCenter(),n=this.map.getZoom(),u=[e.getSouthWest().lat,e.getSouthWest().lng,e.getNorthEast().lat,e.getNorthEast().lng],c=[r.lat,r.lng],f={action:"mls_get_clusters",zoom:n,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||""};s.updateFromMap(u,c),v.queue("clusters",function(m){return a.ajax({url:homeprozMapData.clusterEndpoint,type:"GET",data:f})},function(m,b){if(m.success){var g=m.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}}})}},clearAllLayers:function(){this.densityLayer.clearLayers(),this.clusterLayer.clearLayers(),this.markerCluster.clearLayers(),this.markers={}},renderDensity:function(t){this.clearAllLayers();var e=this,r=this.map.getZoom();t.forEach(function(n){var u=e.getDensityColor(n.count,r),c=e.getDensitySize(n.count,r),f=L.divIcon({html:'
    ',className:"density-dot-container",iconSize:[c,c],iconAnchor:[c/2,c/2]}),m=L.marker([n.lat,n.lng],{icon:f});m.on("click",function(){e.map.setView([n.lat,n.lng],e.map.getZoom()+2)}),m.bindTooltip(n.count+" properties",{className:"density-tooltip"}),e.densityLayer.addLayer(m)})},getDensityThreshold:function(t){return Math.max(40,Math.round(600/Math.pow(1.4,t-3)))},getDensityColor:function(t,e){var r=this.getDensityThreshold(e),n=t/r;return n>=1.5?"rgba(180, 83, 9, 0.8)":n>=1?"rgba(217, 119, 6, 0.8)":n>=.6?"rgba(245, 158, 11, 0.8)":n>=.3?"rgba(234, 179, 8, 0.8)":n>=.15?"rgba(132, 204, 22, 0.8)":"rgba(34, 197, 94, 0.8)"},getDensitySize:function(t,e){var r=this.getDensityThreshold(e),n=t/r;return n>=1.5?11:n>=1?10:n>=.6?8:n>=.3?7:6},renderClusters:function(t){this.clearAllLayers();var e=this;t.forEach(function(r){var n="small";r.count>=100?n="large":r.count>=10&&(n="medium");var u=L.divIcon({html:"
    "+r.count+"
    ",className:"marker-cluster marker-cluster-"+n+" server-cluster",iconSize:L.point(40,40)}),c=L.marker([r.lat,r.lng],{icon:u});c.on("click",function(){e.map.setView([r.lat,r.lng],e.map.getZoom()+2)});var f="$"+e.formatNumber(r.min_price);r.max_price!==r.min_price&&(f+=" - $"+e.formatNumber(r.max_price)),c.bindTooltip(r.count+" properties
    "+f,{className:"cluster-tooltip"}),e.clusterLayer.addLayer(c)})},renderMarkers:function(t){this.clearAllLayers(),this.selectedPropertyId=null,this.hoveredPropertyId=null,a(".property-card").removeClass("property-card-highlighted");var e=this,r=[];t.forEach(function(n,u){if(n.lat&&n.lng){var c=L.marker([n.lat,n.lng],{icon:e.createIcon("red"),zIndexOffset:e.baseZIndex+u});c.propertyId=n.id,c.defaultZIndex=e.baseZIndex+u,c.bindPopup('
    '+n.price+"
    "+n.address+'
    View Details
    '),c.on("click",function(f){e.onMarkerClick(n.id)}),r.push(c),e.markers[n.id]=c}}),this.markerCluster.addLayers(r)},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 r=a("#property-"+t);if(r.length){var n=r.offset().top,u=n+r.outerHeight(),c=a(window).scrollTop(),f=c+a(window).height(),m=n>=c&&u<=f;m?e.flashCard(r):a("html, body").animate({scrollTop:n-120},400,function(){e.flashCard(r)})}}},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 r=this.markers[t];r&&r.setIcon(this.createIcon(e))},setMarkerZIndex:function(t,e){var r=this.markers[t];r&&r.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))})}},s={$form:null,$results:null,$filters:null,isFirstLoad:!0,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 r=t.getPageFromUrl(a(this).attr("href"));t.filterProperties(r)}),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 r=a(this).attr("name");t.has(r)&&a(this).val(t.get(r))}),this.$form.find('input[type="text"]').each(function(){var r=a(this).attr("name");t.has(r)&&a(this).val(t.get(r))});var e=this.getPageFromHash();e>1&&this.filterProperties(e,!1)},updateFromMap:function(t,e){this.mapBounds=t,this.mapCenter=e,this.isMapUpdate=!0,o.isEnabled&&p.reset(),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){e=e!==!1,t=t||1;var r=this,n=this.getFormData();this.$filters.addClass("is-loading"),this.isFirstLoad&&this.$results.html('
    ');var u={action:"homeproz_filter_properties",nonce:homeprozAjax.nonce,property_type:n.property_type,property_location:n.property_location,zip:n.zip,min_price:n.min_price,max_price:n.max_price,beds:n.beds,paged:t};this.mapBounds&&(u.bounds=this.mapBounds),this.mapCenter&&(u.center=this.mapCenter);var c=this.isMapUpdate;this.isMapUpdate=!1,v.queue("properties",function(f){return a.ajax({url:homeprozAjax.ajaxUrl,type:"POST",data:u})},function(f,m){f.success&&(r.$results.html(f.data.html),r.isFirstLoad=!1,f.data.filters&&!c&&i.updateFilters(f.data.filters),typeof h<"u"&&h.calculate(),window.innerWidth>=1024&&a(".is-map-view").length&&(p.destroy(),setTimeout(function(){p.init()},100)),e&&r.updateUrl(n,t),t>1&&a("html, body").animate({scrollTop:r.$filters.offset().top-100},300))},function(){r.$filters.removeClass("is-loading")},function(){r.$results.html('

    Error

    Something went wrong. Please try again.

    ')})},getFormData:function(){return{property_type:this.$form.find('[name="property_type"]').val()||"",property_location:this.$form.find('[name="property_location"]').val()||"",zip:this.$form.find('[name="zip"]').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 r=new URL(homeprozAjax.archiveUrl);for(var n in t)t[n]&&r.searchParams.set(n,t[n]);e>1?r.hash="page="+e:r.hash="",history.replaceState(null,"",r.toString())},getPageFromUrl:function(t){var e=t.match(/#page=(\d+)/);if(e)return parseInt(e[1]);var r=t.match(/[?&]paged=(\d+)/);return r?parseInt(r[1]):1},resetFilters:function(){this.$form.find("select").val(""),this.filterProperties(1)}},l={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"&&(i.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&&o.isEnabled&&p.destroy(),!t&&this.isAboveBreakpoint&&(this.isMapView?(e.removeClass("is-grid-view").addClass("is-map-view"),!this.mapInitialized&&typeof homeprozMapData<"u"?(i.init(homeprozMapData.initialFilters||{}),this.mapInitialized=!0):i.map&&setTimeout(function(){i.map.invalidateSize()},100),setTimeout(function(){p.init()},200)):e.removeClass("is-map-view").addClass("is-grid-view"),typeof h<"u"&&setTimeout(function(){h.calculate()},150))},setMapView:function(t){this.isMapView=t}},h={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"),r=t.hasClass("is-map-view"),n=e.width();r?this.calculateMapLayout(n):this.calculateGridLayout(n)},calculateMapLayout:function(t){for(var e=5;e>=1;e--){var r=e*this.cardWidth+(e-1)*this.cardGap,n=(this.mapGap+r)/(1-this.mapRatio);if(n<=t){this.setProperties(n,e,".property-map-layout"),this.setProperties(n,e,".property-list-container");return}}var r=this.cardWidth,n=(this.mapGap+r)/(1-this.mapRatio);this.setProperties(Math.min(n,t),1,".property-map-layout"),this.setProperties(Math.min(n,t),1,".property-list-container")},calculateGridLayout:function(t){for(var e=6;e>=1;e--){var r=e*this.cardWidth+(e-1)*this.cardGap;if(r<=t){this.setProperties(r,e,".grid-view-container");return}}this.setProperties(this.cardWidth,1,".grid-view-container")},setProperties:function(t,e,r){var n=a(r);n.length&&(n.css("--layout-width",t+"px"),n.css("--card-columns",e))},clearProperties:function(){a(".property-map-layout, .grid-view-container, .property-list-container").css({"--layout-width":"","--card-columns":""})}},o={pages:{},visibleRange:{first:1,last:1},totalPages:0,totalPosts:0,pendingRequests:{},isEnabled:!1,config:{maxPagesInDOM:5,bufferPages:2,cardsPerPage:12}},p={$container:null,$grid:null,$topSentinel:null,$bottomSentinel:null,$topLoader:null,$bottomLoader:null,topObserver:null,bottomObserver:null,scrollContainer:null,init:function(){if(!(window.innerWidth<1024||!a(".is-map-view").length)&&(this.$container=a(".property-list-container"),this.$grid=this.$container.find(".properties-grid"),!(!this.$container.length||!this.$grid.length))){var t=this.$container.find(".properties-meta"),e=t.find(".properties-count strong").text();o.totalPosts=parseInt(e)||0,o.totalPages=Math.ceil(o.totalPosts/o.config.cardsPerPage),!(o.totalPages<=1)&&(this.resetState(),this.setupDOM(),this.wrapInitialCards(),this.setupObservers(),o.isEnabled=!0,this.$container.addClass("infinite-scroll-enabled"))}},resetState:function(){o.pages={},o.visibleRange={first:1,last:1},o.pendingRequests={}},setupDOM:function(){this.$container.find(".infinite-scroll-sentinel, .infinite-scroll-loader").remove(),this.$topSentinel=a('
    '),this.$topLoader=a('
    '),this.$bottomSentinel=a('
    '),this.$bottomLoader=a('
    '),this.$grid.before(this.$topSentinel).before(this.$topLoader),this.$grid.after(this.$bottomLoader).after(this.$bottomSentinel),this.scrollContainer=this.$container[0]},wrapInitialCards:function(){var t=this.$grid.find(".property-card");if(t.length){var e=a('
    ');t.wrapAll(e);var r=this.$grid.find('.infinite-scroll-page[data-page="1"]').outerHeight();o.pages[1]={html:null,height:r,loaded:!0},o.visibleRange={first:1,last:1}}},setupObservers:function(){var t=this,e={root:this.scrollContainer,rootMargin:"400px 0px",threshold:0};this.bottomObserver=new IntersectionObserver(function(r){r.forEach(function(n){n.isIntersecting&&t.loadNextPage()})},e),this.topObserver=new IntersectionObserver(function(r){r.forEach(function(n){n.isIntersecting&&t.loadPrevPage()})},e),this.bottomObserver.observe(this.$bottomSentinel[0]),this.topObserver.observe(this.$topSentinel[0])},loadNextPage:function(){var t=o.visibleRange.last+1;if(!(t>o.totalPages)){if(o.pages[t]&&o.pages[t].loaded){this.showPage(t,"append");return}o.pendingRequests[t]||this.loadPage(t,"append")}},loadPrevPage:function(){var t=o.visibleRange.first-1;if(!(t<1)){if(o.pages[t]&&o.pages[t].loaded){this.showPage(t,"prepend");return}o.pendingRequests[t]||this.loadPage(t,"prepend")}},loadPage:function(t,e){var r=this,n=e==="prepend"?this.$topLoader:this.$bottomLoader;n.addClass("is-loading");var u=s.getFormData(),c={action:"homeproz_filter_properties",nonce:homeprozAjax.nonce,property_type:u.property_type,property_location:u.property_location,zip:u.zip,min_price:u.min_price,max_price:u.max_price,beds:u.beds,paged:t,cards_only:"true"};s.mapBounds&&(c.bounds=s.mapBounds),s.mapCenter&&(c.center=s.mapCenter);var f=a.ajax({url:homeprozAjax.ajaxUrl,type:"POST",data:c,success:function(m){m.success&&m.data.html&&r.renderPage(t,m.data.html,e)},complete:function(){n.removeClass("is-loading"),delete o.pendingRequests[t]}});o.pendingRequests[t]=f},renderPage:function(t,e,r){var n=this.saveScrollAnchor(),u=a('
    ');if(u.html(e),r==="prepend"){var c=this.$grid.find(".infinite-scroll-page").first();c.length?c.before(u):this.$grid.prepend(u),o.visibleRange.first=t}else{var f=this.$grid.find(".infinite-scroll-page").last();f.length?f.after(u):this.$grid.append(u),o.visibleRange.last=t}o.pages[t]={html:e,height:u.outerHeight(),loaded:!0},this.restoreScrollAnchor(n),typeof d<"u"&&d.loadVisibleImages(),this.cleanupExcessPages()},showPage:function(t,e){var r=o.pages[t];if(!(!r||!r.html)){var n=this.$grid.find('.infinite-scroll-placeholder[data-page="'+t+'"]');if(n.length){var u=this.saveScrollAnchor(),c=a('
    ');c.html(r.html),n.replaceWith(c),this.restoreScrollAnchor(u)}e==="prepend"?o.visibleRange.first=t:o.visibleRange.last=t,typeof d<"u"&&d.loadVisibleImages(),this.cleanupExcessPages()}},cleanupExcessPages:function(){var t=o.visibleRange,e=o.config,r=t.last-t.first+1;if(!(r<=e.maxPagesInDOM)){for(;t.first');u.replaceWith(m),o.pages[n]&&(o.pages[n].height=c),this.restoreScrollAnchor(f)}t.first++}for(;t.last>t.first+e.bufferPages;){var n=t.last,u=this.$grid.find('.infinite-scroll-page[data-page="'+n+'"]');if(u.length){var c=u.outerHeight(),f=this.saveScrollAnchor(),m=a('
    ');u.replaceWith(m),o.pages[n]&&(o.pages[n].height=c),this.restoreScrollAnchor(f)}t.last--}}},saveScrollAnchor:function(){if(!this.scrollContainer)return null;var t=this.scrollContainer.getBoundingClientRect(),e=this.$grid.find(".property-card"),r=null;return e.each(function(){var n=this.getBoundingClientRect();if(n.bottom>t.top&&n.top1&&(this.scrollContainer.scrollTop+=u)}},reset:function(){for(var t in o.pendingRequests)o.pendingRequests[t]&&o.pendingRequests[t].abort();v.cancel(),this.resetState(),this.scrollContainer&&(this.scrollContainer.scrollTop=0)},destroy:function(){if(o.isEnabled){for(var t in o.pendingRequests)o.pendingRequests[t]&&o.pendingRequests[t].abort();this.topObserver&&(this.topObserver.disconnect(),this.topObserver=null),this.bottomObserver&&(this.bottomObserver.disconnect(),this.bottomObserver=null),this.$container&&(this.$container.find(".infinite-scroll-sentinel, .infinite-scroll-loader").remove(),this.$container.removeClass("infinite-scroll-enabled")),this.$grid&&(this.$grid.find(".infinite-scroll-page").children().unwrap(),this.$grid.find(".infinite-scroll-placeholder").remove()),this.resetState(),o.isEnabled=!1}}},d={init:function(){this.loadVisibleImages(),this.bindEvents()},bindEvents:function(){var t=this;if(typeof MutationObserver<"u"){var e=new MutationObserver(function(u){t.loadVisibleImages()}),r=document.getElementById("property-results");r&&e.observe(r,{childList:!0,subtree:!0});var n=document.getElementById("property-results-grid");n&&e.observe(n,{childList:!0,subtree:!0})}},loadVisibleImages:function(){var t=this;a(".property-card-image.is-loading[data-bg]").each(function(){t.loadImage(a(this))})},loadImage:function(t){var e=t.data("bg");if(e){t.removeClass("is-loading").removeAttr("data-bg");var r=new Image;r.onload=function(){t.css("background-image","url("+e+")"),t.addClass("is-loaded")},r.onerror=function(){t.addClass("is-loaded"),t.removeClass("has-image")},r.src=e}}};a(function(){s.init(),l.init(),h.init(),d.init(),window.innerWidth>=1024&&a(".is-map-view").length&&setTimeout(function(){p.init()},300)})})(jQuery);(function(a){var v={$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 i=a("#gallery-images-data");i.length&&(this.images=JSON.parse(i.text())),this.images.length!==0&&(this.calculateThumbnailsPerPage(),this.bindEvents(),this.bindSwipeEvents(),this.updateThumbnailNavigation(),this.setupThumbnailLoading(),this.preloadThumbnailPages(0,2),this.images.length>1&&this.startAutoplay())}},calculateThumbnailsPerPage:function(){a(window).width()<=640?this.thumbnailsPerPage=4:this.thumbnailsPerPage=5},bindEvents:function(){var i=this;this.$thumbnails.on("click",function(s){s.stopPropagation();var l=parseInt(a(this).data("index"));i.stopAutoplay(),i.setMainImage(l,!1)}),this.$playbackBtn.on("click",function(s){s.stopPropagation(),s.preventDefault(),i.isPlaying?i.stopAutoplay():i.startAutoplay()}),this.$prevBtn.on("click",function(){i.stopAutoplay(),i.prevThumbnailPage()}),this.$nextBtn.on("click",function(){i.stopAutoplay(),i.nextThumbnailPage()}),this.$gallery.find("[data-lightbox-trigger]").on("click",function(s){if(i.isSwiping){i.isSwiping=!1;return}i.stopAutoplay(),i.openLightbox(i.currentIndex)}),this.$lightbox.find(".lightbox-close, .lightbox-overlay").on("click",function(){i.closeLightbox()}),this.$lightbox.find(".lightbox-prev").on("click",function(){i.slideLightboxImage("prev")}),this.$lightbox.find(".lightbox-next").on("click",function(){i.slideLightboxImage("next")}),a(document).on("keydown",function(s){if(i.$lightbox.is('[aria-hidden="false"]'))switch(s.key){case"Escape":i.closeLightbox();break;case"ArrowLeft":i.slideLightboxImage("prev");break;case"ArrowRight":i.slideLightboxImage("next");break}}),a(window).on("resize",function(){i.calculateThumbnailsPerPage(),i.updateThumbnailNavigation()})},bindSwipeEvents:function(){var i=this;this.$mainImageContainer[0].addEventListener("touchstart",function(s){i.handleSwipeStart(s)},{passive:!0}),this.$mainImageContainer[0].addEventListener("touchend",function(s){i.handleMainGallerySwipeEnd(s)},{passive:!0}),this.$lightboxImageContainer[0].addEventListener("touchstart",function(s){i.handleSwipeStart(s)},{passive:!0}),this.$lightboxImageContainer[0].addEventListener("touchend",function(s){i.handleLightboxSwipeEnd(s)},{passive:!0})},handleSwipeStart:function(i){i.touches.length===1&&(this.swipeStartX=i.touches[0].clientX,this.swipeStartY=i.touches[0].clientY)},handleMainGallerySwipeEnd:function(i){if(i.changedTouches.length===1){var s=i.changedTouches[0].clientX-this.swipeStartX,l=i.changedTouches[0].clientY-this.swipeStartY;Math.abs(s)>Math.abs(l)&&Math.abs(s)>this.swipeThreshold&&(this.isSwiping=!0,this.stopAutoplay(),s>0?this.slideMainImage("prev"):this.slideMainImage("next"))}},handleLightboxSwipeEnd:function(i){if(i.changedTouches.length===1){var s=i.changedTouches[0].clientX-this.swipeStartX,l=i.changedTouches[0].clientY-this.swipeStartY;Math.abs(s)>Math.abs(l)&&Math.abs(s)>this.swipeThreshold&&(s>0?this.slideLightboxImage("prev"):this.slideLightboxImage("next"))}},startAutoplay:function(){var i=this;this.images.length<=1||(this.isPlaying=!0,this.$playbackBtn.addClass("is-playing"),this.$playbackBtn.attr("aria-label","Pause slideshow"),this.autoplayInterval=setInterval(function(){i.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 i=this.currentIndex+1;i>=this.images.length&&(i=0),this.setMainImage(i,!0)}},slideMainImage:function(i){var s=this;if(!(this.isTransitioning||this.images.length<=1)){var l;i==="prev"?(l=this.currentIndex-1,l<0&&(l=this.images.length-1)):(l=this.currentIndex+1,l>=this.images.length&&(l=0)),this.isTransitioning=!0;var h=this.images[l],o=i==="next"?"100%":"-100%",p=i==="next"?"-100%":"100%",d=a('');d.attr("src",h.url),d.attr("alt",h.alt||"Property photo"),d.css({position:"absolute",top:0,left:0,width:"100%",height:"100%","object-fit":"cover",transform:"translateX("+o+")","z-index":2,"border-radius":"0.5rem"}),this.$mainImageContainer.css({position:"relative",overflow:"hidden"}),this.$mainImageContainer.append(d),this.$mainImage.css({transition:"transform "+this.slideDuration+"ms ease-out"}),d.css({transition:"transform "+this.slideDuration+"ms ease-out"}),d[0].offsetHeight,this.$mainImage.css("transform","translateX("+p+")"),d.css("transform","translateX(0)"),setTimeout(function(){s.$mainImage.attr("src",h.url),s.$mainImage.attr("alt",h.alt||"Property photo"),s.$mainImage.css({transition:"",transform:""}),d.remove(),s.isTransitioning=!1},this.slideDuration),this.currentIndex=l,this.$thumbnails.removeClass("is-active"),this.$thumbnails.filter('[data-index="'+l+'"]').addClass("is-active"),this.scrollToThumbnail(l)}},setMainImage:function(i,s){var l=this;if(!(i<0||i>=this.images.length)&&!this.isTransitioning){var h=this.images[i];if(s){this.isTransitioning=!0;var o=a('');o.attr("src",h.url),o.attr("alt",h.alt||"Property photo"),o.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(o),o[0].offsetHeight,o.css({opacity:1,transform:"scale(1)"}),setTimeout(function(){l.$mainImage.attr("src",h.url),l.$mainImage.attr("alt",h.alt||"Property photo"),o.remove(),l.isTransitioning=!1},this.fadeDuration)}else this.$mainImage.attr("src",h.url),this.$mainImage.attr("alt",h.alt||"Property photo");this.currentIndex=i,this.$thumbnails.removeClass("is-active"),this.$thumbnails.filter('[data-index="'+i+'"]').addClass("is-active"),this.scrollToThumbnail(i)}},scrollToThumbnail:function(i){var s=Math.floor(i/this.thumbnailsPerPage);s!==this.thumbnailPage&&(this.thumbnailPage=s,this.scrollThumbnails())},scrollThumbnails:function(){var i=this.$gallery.find(".gallery-thumbnails"),s=this.$thumbnails.first().outerWidth(!0),l=this.thumbnailPage*this.thumbnailsPerPage*s;i.css("transform","translateX(-"+l+"px)"),this.updateThumbnailNavigation()},updateThumbnailNavigation:function(){var i=Math.ceil(this.images.length/this.thumbnailsPerPage);this.$prevBtn.prop("disabled",this.thumbnailPage===0),this.$nextBtn.prop("disabled",this.thumbnailPage>=i-1),i<=1?(this.$prevBtn.hide(),this.$nextBtn.hide()):(this.$prevBtn.show(),this.$nextBtn.show())},prevThumbnailPage:function(){this.thumbnailPage>0&&(this.thumbnailPage--,this.scrollThumbnails(),this.preloadPrevThumbnailPage())},nextThumbnailPage:function(){var i=Math.ceil(this.images.length/this.thumbnailsPerPage);this.thumbnailPage=this.images.length&&(l=0)),this.isTransitioning=!0;var h=this.images[l],o=i==="next"?"100%":"-100%",p=i==="next"?"-100%":"100%",d=a('');d.attr("src",h.url),d.attr("alt",h.alt||"Property photo"),d.css({position:"absolute","max-width":"100%","max-height":"calc(100vh - 8rem)","object-fit":"contain",transform:"translateX("+o+")",left:"50%",top:"50%","margin-left":"-45vw","margin-top":"calc(-50vh + 4rem)"}),this.$lightboxImageContainer.css({position:"relative",overflow:"hidden"}),this.$lightboxImageContainer.append(d),this.$lightboxImage.css({transition:"transform "+this.slideDuration+"ms ease-out"}),d.css({transition:"transform "+this.slideDuration+"ms ease-out"}),d[0].offsetHeight,this.$lightboxImage.css("transform","translateX("+p+")"),d.css("transform","translateX(0)"),setTimeout(function(){s.$lightboxImage.attr("src",h.url),s.$lightboxImage.attr("alt",h.alt||"Property photo"),s.$lightboxImage.css({transition:"",transform:""}),d.remove(),s.isTransitioning=!1,s.$lightboxCounter.text(l+1)},this.slideDuration),this.currentIndex=l}},prevImage:function(){this.slideLightboxImage("prev")},nextImage:function(){this.slideLightboxImage("next")},updateLightboxImage:function(){var i=this.images[this.currentIndex];this.$lightboxImage.attr("src",i.url),this.$lightboxImage.attr("alt",i.alt||"Property photo"),this.$lightboxCounter.text(this.currentIndex+1)},setupThumbnailLoading:function(){this.$thumbnails.each(function(){var i=a(this),s=i.find("img");i.addClass("is-loading"),i.find(".thumbnail-spinner").length||i.append('
    '),s[0].complete?i.removeClass("is-loading"):(s.on("load",function(){i.removeClass("is-loading")}),s.on("error",function(){i.removeClass("is-loading")}))})},preloadThumbnailPages:function(i,s){for(var l=this,h=i*this.thumbnailsPerPage,o=Math.min((i+s)*this.thumbnailsPerPage,this.images.length),p=h;p=0&&this.preloadThumbnailPages(i,1)}};a(function(){v.init()})})(jQuery);(function(a){if(!a(".mortgage-calculator-main").length)return;let v=!1;a.fn.currencyInput=function(s=!0){return this.data("ci_show_symbol",s),v||(v=!0,a.fn._CIOriginalVal=a.fn.val,a.fn.val=function(h){if(a(this).data("_currencyInput"))if(arguments.length===0){var o=a(this)._CIOriginalVal();if(o=="")return"";var p=parseInt(o.replace(/[^0-9]/g,""));return p}else{if(h=String(h).replace(/[^0-9]/g,""),h!=""){var d=parseInt(h).toLocaleString("en-US",{style:"currency",currency:"USD",minimumFractionDigits:0,maximumFractionDigits:0});return a(this).data("ci_show_symbol")||(d=d.replace("$","")),a(this)._CIOriginalVal(d)}return a(this)._CIOriginalVal(h)}else if(a(this).data("_percentInput"))if(arguments.length===0){var o=a(this)._CIOriginalVal();if(o=="")return"";var p=parseFloat(o.replace(/[^0-9.]/g,""));return isNaN(p)?"":p}else{h=String(h).replace(/[^0-9.]/g,"");var t=h.split(".");return t.length>2&&(h=t[0]+"."+t.slice(1).join("")),a(this)._CIOriginalVal(h)}else return arguments.length===0?a(this)._CIOriginalVal():a(this)._CIOriginalVal(h)}),this.data("_currencyInput")?this:(this.data("_currencyInput",!0),this.on("focus",function(){a(this).select()}),this.on("input",function(h){var o=this.selectionStart,p=a(this)._CIOriginalVal(),d=p.length;a(this).val(p);var t=a(this)._CIOriginalVal().length;t>d?o+=t-d:t2&&(p=d[0]+"."+d.slice(1).join("")),a(this)._CIOriginalVal(p);var t=p.length;t0){var h=l/s*100;this.$downPaymentPercent._CIOriginalVal(h.toFixed(1))}},syncDownPaymentFromPercent:function(){var s=this.$homePrice.val(),l=this.$downPaymentPercent.val();if(s&&s>0&&l!==""&&l>=0){var h=Math.round(s*l/100);this.$downPayment.val(h)}},calculate:function(){var s=this.$homePrice.val()||0,l=this.$downPayment.val()||0,h=parseInt(this.$loanTerm.val(),10),o=this.$interestRate.val()||0,p=s-l;p<0&&(p=0);var d=o/100/12,t=h*12,e=0,r=0;if(p>0&&d>0&&t>0){var n=Math.pow(1+d,t);e=p*(d*n)/(n-1),r=e*t-p}else p>0&&d===0&&(e=p/t,r=0);this.$monthlyPayment.text(this.formatCurrencyDisplay(e)),this.$principalInterest.text(this.formatCurrencyDisplay(e)),this.$loanAmount.text(this.formatCurrencyDisplay(p)),this.$totalInterest.text(this.formatCurrencyDisplay(r))}};a(document).ready(function(){i.init()})})(jQuery);(function(a){a(function(){})})(jQuery); 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 22238873..bc9d026a 100755 --- a/wp-content/themes/homeproz/template-parts/property/property-filters.js +++ b/wp-content/themes/homeproz/template-parts/property/property-filters.js @@ -9,6 +9,126 @@ (function($) { 'use strict'; + /** + * Request Queue Manager + * Handles debouncing and cancellation of AJAX requests to prevent race conditions. + * When filters, zoom, or viewport change, pending requests are cancelled and + * only the final requested state is fetched. + */ + var RequestQueue = { + // Pending XHR objects by type + pending: { + clusters: null, + properties: null + }, + // Debounce timeouts by type + timeouts: { + clusters: null, + properties: null + }, + // Request IDs to track staleness + requestIds: { + clusters: 0, + properties: 0 + }, + // Debounce delay in ms + debounceDelay: 200, + + /** + * Queue a request with debouncing and cancellation + * @param {string} type - 'clusters' or 'properties' + * @param {function} requestFn - Function that returns jqXHR (receives requestId) + * @param {function} successFn - Success callback (receives response, requestId) + * @param {function} completeFn - Complete callback (optional) + * @param {function} errorFn - Error callback (optional, receives jqXHR, textStatus, errorThrown) + */ + queue: function(type, requestFn, successFn, completeFn, errorFn) { + var self = this; + + // Cancel any pending debounce timeout + if (this.timeouts[type]) { + clearTimeout(this.timeouts[type]); + this.timeouts[type] = null; + } + + // Abort any in-flight request + if (this.pending[type]) { + this.pending[type].abort(); + this.pending[type] = null; + } + + // Increment request ID + this.requestIds[type]++; + var requestId = this.requestIds[type]; + + // Schedule the request with debounce + this.timeouts[type] = setTimeout(function() { + self.timeouts[type] = null; + + // Execute the request + var xhr = requestFn(requestId); + + if (xhr && xhr.then) { + self.pending[type] = xhr; + + xhr.done(function(response) { + // Only process if this is still the latest request + if (requestId === self.requestIds[type]) { + successFn(response, requestId); + } + }); + + xhr.fail(function(jqXHR, textStatus, errorThrown) { + // Only call error handler if this is still the latest request + // and it wasn't aborted (which is intentional) + if (errorFn && requestId === self.requestIds[type] && textStatus !== 'abort') { + errorFn(jqXHR, textStatus, errorThrown); + } + }); + + xhr.always(function() { + // Clear pending reference if this is the current request + if (self.pending[type] === xhr) { + self.pending[type] = null; + } + if (completeFn && requestId === self.requestIds[type]) { + completeFn(); + } + }); + } + }, this.debounceDelay); + }, + + /** + * Cancel all pending requests of a specific type + * @param {string} type - 'clusters' or 'properties' (optional, cancels all if omitted) + */ + cancel: function(type) { + var types = type ? [type] : ['clusters', 'properties']; + var self = this; + + types.forEach(function(t) { + if (self.timeouts[t]) { + clearTimeout(self.timeouts[t]); + self.timeouts[t] = null; + } + if (self.pending[t]) { + self.pending[t].abort(); + self.pending[t] = null; + } + }); + }, + + /** + * Check if a request type is currently loading + * @param {string} type - 'clusters' or 'properties' + * @returns {boolean} + */ + isLoading: function(type) { + return !!(this.pending[type] || this.timeouts[type]); + } + }; + /** * Property Map Manager * Uses server-side clustering for efficient rendering of 30k+ properties @@ -23,8 +143,6 @@ hoveredPropertyId: null, baseZIndex: 400, currentFilters: {}, - isLoading: false, - loadTimeout: null, currentMode: null, // Track current visualization mode /** @@ -95,29 +213,16 @@ /** * Load clusters/markers from server based on viewport + * Uses RequestQueue for debouncing and cancellation */ loadClusters: function() { if (!this.map) return; - var self = this; - - // Debounce rapid requests - clearTimeout(this.loadTimeout); - this.loadTimeout = setTimeout(function() { - self._doLoadClusters(); - }, 150); - }, - - _doLoadClusters: function() { - if (this.isLoading) return; - 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, @@ -139,14 +244,20 @@ min_beds: this.currentFilters.min_beds || '' }; - // Also update the property list with the same viewport + // Also update the property list with the same viewport (queued separately) PropertyFilters.updateFromMap(boundsArray, centerArray); - $.ajax({ - url: homeprozMapData.clusterEndpoint, - type: 'GET', - data: requestData, - success: function(response) { + // Queue the cluster request with debounce and cancellation + RequestQueue.queue( + 'clusters', + function(requestId) { + return $.ajax({ + url: homeprozMapData.clusterEndpoint, + type: 'GET', + data: requestData + }); + }, + function(response, requestId) { if (response.success) { var data = response.data; self.currentMode = data.type; @@ -163,11 +274,8 @@ break; } } - }, - complete: function() { - self.isLoading = false; } - }); + ); }, /** @@ -525,7 +633,6 @@ // 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 @@ -646,20 +753,16 @@ /** * Filter properties via AJAX + * Uses RequestQueue for debouncing and cancellation to prevent race conditions */ filterProperties: function(page, updateHistory) { - if (this.isLoading) { - return; - } - updateHistory = updateHistory !== false; page = page || 1; var self = this; var formData = this.getFormData(); - // Show loading state - this.isLoading = true; + // Show loading state immediately this.$filters.addClass('is-loading'); // Show spinner only on first load @@ -688,20 +791,29 @@ requestData.center = this.mapCenter; } - $.ajax({ - url: homeprozAjax.ajaxUrl, - type: 'POST', - data: requestData, - success: function(response) { + // Capture isMapUpdate state before it might change + var wasMapUpdate = this.isMapUpdate; + this.isMapUpdate = false; + + // Queue the property list request with debounce and cancellation + RequestQueue.queue( + 'properties', + function(requestId) { + return $.ajax({ + url: homeprozAjax.ajaxUrl, + type: 'POST', + data: requestData + }); + }, + function(response, requestId) { if (response.success) { self.$results.html(response.data.html); self.isFirstLoad = false; // Update map with new filter params (but not if this was triggered by map move) - if (response.data.filters && !self.isMapUpdate) { + if (response.data.filters && !wasMapUpdate) { PropertyMap.updateFilters(response.data.filters); } - self.isMapUpdate = false; // Recalculate layout after content update if (typeof LayoutCalculator !== 'undefined') { @@ -730,14 +842,15 @@ } } }, - error: function() { - self.$results.html('

    Error

    Something went wrong. Please try again.

    '); - }, - complete: function() { - self.isLoading = false; + function() { + // Complete callback - remove loading state self.$filters.removeClass('is-loading'); + }, + function() { + // Error callback - show error message + self.$results.html('

    Error

    Something went wrong. Please try again.

    '); } - }); + ); }, /** @@ -1486,13 +1599,16 @@ * Reset infinite scroll (called on filter/map change) */ reset: function() { - // Cancel all pending requests + // Cancel all pending infinite scroll requests for (var page in InfiniteScrollState.pendingRequests) { if (InfiniteScrollState.pendingRequests[page]) { InfiniteScrollState.pendingRequests[page].abort(); } } + // Also cancel pending property and cluster requests via RequestQueue + RequestQueue.cancel(); + // Clear state this.resetState();