PageSourceSearch

https://camperandnicholsons.com/core/view/website_2/src/scripts/components/buy-results-bar.js?v=129

js camperandnicholsons.com collected 2026-09-25 19:18:50 UTC 55,297 bytes, 1,594 lines download raw bytes

1import { createHistogramRangeFilter } from '../utils/histogram-helpers.js?v=3';
2
3  export default (config = {}) => {
4    const params = new URLSearchParams(window.location.search);
5
6    // Currency conversion helpers for URL state handling
7    const currencySymbolToCode = {
8      "€": "EUR",
9      "$": "USD",
10      "£": "GBP",
11    };
12    
13    const currencyCodeToSymbol = {
14      "EUR": "€",
15      "USD": "$",
16      "GBP": "£",
17    };
18
19    // Store initial values for comparison
20    const initialMinPrice = config.priceRange?.minValue || 0;
21    const initialMaxPrice = config.priceRange?.maxValue || 2000000;
22    const initialMinLength = config.lengthRange?.minValue || 0;
23    const initialMaxLength = config.lengthRange?.maxValue || 150;
24
25    // Create reactive state for URL synchronization
26    const urlState = Alpine.reactive({
27      minPrice: initialMinPrice,
28      maxPrice: initialMaxPrice,
29      minLength: initialMinLength,
30      maxLength: initialMaxLength,
31      cabins: config.cabins?.defaultValue || 0,
32      guests: config.guests?.defaultValue || 0,
33      yachtType: config.yachtType?.defaultValue || "any",
34      popularYachtType: null, // Popular yacht type selections
35      selectedYacht: null, // Selected yacht from search (stored as slug)
36      builders: config.builder?.defaultSelected || [],
37      exteriorDesigners: config.exteriorDesigner?.defaultSelected || [],
38      interiorDesigners: config.interiorDesigner?.defaultSelected || [],
39      yearBuilt: config.yearBuilt?.defaultValue || "any",
40      lastRefit: config.lastRefit?.defaultValue || "any",
41      bookingOptions: config.bookingOptions?.defaultSelected || [],
42      features: config.features?.defaultSelected || [],
43      activeFilter: "yachts",
44      gridColumns: 3,
45      currency: "EUR",
46    });
47  
48    const paramMapping = {
49      minPrice: {
50        type: "number",
51        default: () => config.priceRange?.minValue || 0,
52      },
53      maxPrice: {
54        type: "number",
55        default: () => config.priceRange?.maxValue || 2000000,
56      },
57      minLength: {
58        type: "number",
59        default: () => config.lengthRange?.minValue || 30,
60      },
61      maxLength: {
62        type: "number",
63        default: () => config.lengthRange?.maxValue || 150,
64      },
65      cabins: {
66        type: "number",
67        default: () => config.cabins?.defaultValue || 0,
68      },
69      guests: {
70        type: "number",
71        default: () => config.guests?.defaultValue || 0,
72      },
73      yachtType: {
74        type: "string",
75        default: () => config.yachtType?.defaultValue || "any",
76      },
77      popularYachtType: {
78        type: "string",
79        default: () => null,
80      },
81      selectedYacht: {
82        type: "string",
83        default: () => null,
84      },
85      builders: {
86        type: "array",
87        default: () => config.builder?.defaultSelected || [],
88      },
89      exteriorDesigners: {
90        type: "array",
91        default: () => config.exteriorDesigner?.defaultSelected || [],
92      },
93      interiorDesigners: {
94        type: "array",
95        default: () => config.interiorDesigner?.defaultSelected || [],
96      },
97      yearBuilt: {
98        type: "string",
99        default: () => config.yearBuilt?.defaultValue || "any",
100      },
101      lastRefit: {
102        type: "string",
103        default: () => config.lastRefit?.defaultValue || "any",
104      },
105      bookingOptions: {
106        type: "array",
107        default: () => config.bookingOptions?.defaultSelected || [],
108      },
109      features: {
110        type: "array",
111        default: () => config.features?.defaultSelected || [],
112      },
113      activeFilter: {
114        type: "string",
115        default: () => "yachts",
116      },
117      gridColumns: {
118        type: "number",
119        default: () => 3,
120      },
121      currency: {
122        type: "string",
123        default: () => "EUR",
124      },
125    };
126  
127    Object.entries(paramMapping).forEach(([key, config]) => {
128      if (params.has(key)) {
129        const value = params.get(key);
130        if (config.type === "number") {
131          urlState[key] = Number(value) || config.default();
132        } else if (config.type === "array") {
133          urlState[key] = value ? value.split(",") : config.default();
134        } else {
135          // Special handling for currency: convert old symbol values to codes
136          if (key === "currency") {
137            // If it's already a code (EUR, USD, GBP), use it
138            if (["EUR", "USD", "GBP"].includes(value.toUpperCase())) {
139              urlState[key] = value.toUpperCase();
140            } else {
141              // Otherwise, try to convert from symbol to code
142              urlState[key] = currencySymbolToCode[value] || config.default();
143            }
144          } else {
145            urlState[key] = value || config.default();
146          }
147        }
148      }
149    });
150  
151    // Create price and length filter objects
152    const priceFilter = createHistogramRangeFilter(
153      config.priceRange || {},
154      "price"
155    );
156    const lengthFilter = createHistogramRangeFilter(
157      config.lengthRange || {},
158      "length"
159    );
160  
161    const cabinsFilter = {
162      minValue: config.cabins?.minValue || 0,
163      maxValue: config.cabins?.maxValue || 12,
164      value: urlState.cabins,
165  
166      increment() {
167        if (this.value < this.maxValue) {
168          this.value++;
169        }
170      },
171  
172      decrement() {
173        if (this.value > this.minValue) {
174          this.value--;
175        }
176      },
177  
178      updateValue(newValue) {
179        const parsed = parseInt(newValue) || 0;
180        this.value = Math.max(this.minValue, Math.min(this.maxValue, parsed));
181      },
182    };
183  
184    const guestsFilter = {
185      minValue: config.guests?.minValue || 0,
186      maxValue: config.guests?.maxValue || 24,
187      value: urlState.guests,
188  
189      increment() {
190        if (this.value < this.maxValue) {
191          this.value++;
192        }
193      },
194  
195      decrement() {
196        if (this.value > this.minValue) {
197          this.value--;
198        }
199      },
200  
201      updateValue(newValue) {
202        const parsed = parseInt(newValue) || 0;
203        this.value = Math.max(this.minValue, Math.min(this.maxValue, parsed));
204      },
205    };
206  
207    const yachtTypeFilter = {
208      options: config.yachtType?.options || [
209        { value: "any", label: "ANY" },
210        { value: 1, label: "MOTOR" },
211        { value: 2, label: "SAILING" },
212      ],
213      selectedValue: urlState.yachtType,
214  
215      updateValue(newValue) {
216        this.selectedValue = newValue;
217      },
218    };
219  
220    const builderFilter = {
221      builders: config.builder?.builders || [],
222      selectedBuilders: [...(urlState.builders || [])],
223      searchQuery: "",
224      showAll: false,
225  
226      get filteredBuilders() {
227        if (!this.searchQuery) {
228          return this.builders;
229        }
230        return this.builders.filter((builder) =>
231          builder.name.toLowerCase().includes(this.searchQuery.toLowerCase())
232        );
233      },
234  
235      toggleBuilder(builderId) {
236        const index = this.selectedBuilders.indexOf(builderId);
237        if (index > -1) {
238          this.selectedBuilders.splice(index, 1);
239        } else {
240          this.selectedBuilders.push(builderId);
241        }
242        // Force reactivity update
243        this.selectedBuilders = [...this.selectedBuilders];
244      },
245  
246      updateValue(newBuilders) {
247        this.selectedBuilders = [...newBuilders];
248      },
249    };
250  
251    const exteriorDesignerFilter = {
252      exteriorDesigners: config.exteriorDesigner?.exteriorDesigners || [],
253      selectedExteriorDesigners: [...(urlState.exteriorDesigners || [])],
254      searchQuery: "",
255      showAll: false,
256  
257      get filteredExteriorDesigners() {
258        if (!this.searchQuery) {
259          return this.exteriorDesigners;
260        }
261        return this.exteriorDesigners.filter((exteriorDesigner) =>
262          exteriorDesigner.name.toLowerCase().includes(this.searchQuery.toLowerCase())
263        );
264      },
265  
266      toggleExteriorDesigner(exteriorDesignerId) {
267        const index = this.selectedExteriorDesigners.indexOf(exteriorDesignerId);
268        if (index > -1) {
269          this.selectedExteriorDesigners.splice(index, 1);
270        } else {
271          this.selectedExteriorDesigners.push(exteriorDesignerId);
272        }
273        // Force reactivity update
274        this.selectedExteriorDesigners = [...this.selectedExteriorDesigners];
275      },
276  
277      updateValue(newExteriorDesigners) {
278        this.selectedExteriorDesigners = [...newExteriorDesigners];
279      },
280    };
281
282    const interiorDesignerFilter = {
283      interiorDesigners: config.interiorDesigner?.interiorDesigners || [],
284      selectedInteriorDesigners: [...(urlState.interiorDesigners || [])],
285      searchQuery: "",
286      showAll: false,
287  
288      get filteredInteriorDesigners() {
289        if (!this.searchQuery) {
290          return this.interiorDesigners;
291        }
292        return this.interiorDesigners.filter((interiorDesigner) =>
293          interiorDesigner.name.toLowerCase().includes(this.searchQuery.toLowerCase())
294        );
295      },
296  
297      toggleInteriorDesigner(interiorDesignerId) {
298        const index = this.selectedInteriorDesigners.indexOf(interiorDesignerId);
299        if (index > -1) {
300          this.selectedInteriorDesigners.splice(index, 1);
301        } else {
302          this.selectedInteriorDesigners.push(interiorDesignerId);
303        }
304        // Force reactivity update
305        this.selectedInteriorDesigners = [...this.selectedInteriorDesigners];
306      },
307  
308      updateValue(newInteriorDesigners) {
309        this.selectedInteriorDesigners = [...newInteriorDesigners];
310      },
311    };
312  
313    const yearBuiltFilter = {
314      options: config.yearBuilt?.options || [
315        { value: "any", label: "ANY" },
316        { value: "up-to-2-years", label: "UP TO 2 YEARS" },
317        { value: "up-to-5-years", label: "UP TO 5 YEARS" },
318        { value: "up-to-10-years", label: "UP TO 10 YEARS" },
319        { value: "over-10-years", label: "OVER 10 YEARS" },
320      ],
321      selectedValue: urlState.yearBuilt,
322  
323      updateValue(newValue) {
324        this.selectedValue = newValue;
325      },
326    };
327  
328    const lastRefitFilter = {
329      options: config.lastRefit?.options || [
330        { value: "any", label: "ANY" },
331        { value: "up-to-2-years", label: "UP TO 2 YEARS" },
332        { value: "up-to-5-years", label: "UP TO 5 YEARS" },
333        { value: "up-to-10-years", label: "UP TO 10 YEARS" },
334        { value: "over-10-years", label: "OVER 10 YEARS" },
335      ],
336      selectedValue: urlState.lastRefit,
337  
338      updateValue(newValue) {
339        this.selectedValue = newValue;
340      },
341    };
342  
343    const bookingOptionsFilter = {
344      options: config.bookingOptions?.options || [
345        { id: "special-offers", label: "Special offers", enabled: false },
346      ],
347      selectedOptions: [...(urlState.bookingOptions || [])],
348  
349      toggleOption(optionId) {
350        const index = this.selectedOptions.indexOf(optionId);
351        if (index > -1) {
352          this.selectedOptions.splice(index, 1);
353        } else {
354          this.selectedOptions.push(optionId);
355        }
356        // Force reactivity update
357        this.selectedOptions = [...this.selectedOptions];
358      },
359  
360      updateValue(newOptions) {
361        this.selectedOptions = [...newOptions];
362      },
363    };
364  
365    const featuresFilter = {
366      options: config.features?.options || [],
367      selectedFeatures: [...(urlState.features || [])],
368  
369      toggleFeature(featureId) {
370        const index = this.selectedFeatures.indexOf(featureId);
371        if (index > -1) {
372          this.selectedFeatures.splice(index, 1);
373        } else {
374          this.selectedFeatures.push(featureId);
375        }
376        // Force reactivity update
377        this.selectedFeatures = [...this.selectedFeatures];
378      },
379  
380      updateValue(newFeatures) {
381        this.selectedFeatures = [...newFeatures];
382      },
383    };
384  
385    // Set initial values from URL state
386    priceFilter.minValue = urlState.minPrice;
387    priceFilter.maxValue = urlState.maxPrice;
388    priceFilter.displayCurrency = currencyCodeToSymbol[urlState.currency] || "€";
389    // Initialize display after setting currency to ensure proper formatting
390    priceFilter.initDisplay();
391    lengthFilter.minValue = urlState.minLength;
392    lengthFilter.maxValue = urlState.maxLength;
393    lengthFilter.initDisplay();
394  
395    return {
396      gridColumns: urlState.gridColumns,
397      activeFilter: urlState.activeFilter,
398      showFilterModal: false,
399  
400      // Yacht types dropdown state
401      showYachtTypesDropdown: false,
402      yachtSearchQuery: "",
403      activeSearchYacht: 0,
404      selectedYacht: null,
405      selectedYachts: [],
406      selectedYachtFromSearchId: null,
407      popularYachtType: urlState.popularYachtType,
408
409      // Size dropdown state
410      showSizeDropdown: false,
411
412      // Price dropdown state
413      showPriceDropdown: false,
414  
415      // Destination dropdown state
416      showDestinationDropdown: false,
417      destinationSearchQuery: "",
418      activeSearchDestination: 0,
419  
420      // Date dropdown state
421      showDateDropdown: false,
422      dateType: "calendar", // 'calendar' or 'flexible'
423      season: null, // null, 'flexible', 'summer', 'winter'
424      months: [], // array of selected month numbers (1-12)
425      calendarRange: {
426        start: null,
427        end: null,
428      },
429  
430      // Mobile filter states
431      showMobileFilters: false,
432      showMobileYachtDropdown: false,
433      showMobileSizeDropdown: false,
434      showMobilePriceDropdown: false,
435      showMobileSortDropdown: false,
436      mobileActiveAccordion: null, // 'type' | 'size' | 'price' | null
437
438      // Sort state
439      sortBy: 'newest',
440      sortOptions: [
441        { value: 'newest', label: 'Newest Listings' },
442        { value: 'priceasc', label: 'Price: Low to High' },
443        { value: 'pricedesc', label: 'Price: High to Low' },
444        { value: 'lengthasc', label: 'Length: Low to High' },
445        { value: 'lengthdesc', label: 'Length: High to Low' },
446      ],
447  
448      // Mock yacht search data
449      yachtSearchData: config.yachtSearchData,
450  
451      // Mock destination search data
452      destinationSearchData: [
453        {
454          id: 1,
455          name: "Mediterranean",
456          description: "EUROPE • FRANCE • ITALY • SPAIN",
457          image: "/src/images/octopus-1.jpg",
458        },
459        {
460          id: 2,
461          name: "The Caribbean",
462          description: "BAHAMAS • BARBADOS • ST. MARTIN",
463          image: "/src/images/octopus-1.jpg",
464        },
465        {
466          id: 3,
467          name: "North America",
468          description: "USA • CANADA • ALASKA",
469          image: "/src/images/octopus-1.jpg",
470        },
471        {
472          id: 4,
473          name: "South Pacific",
474          description: "FIJI • TAHITI • NEW ZEALAND",
475          image: "/src/images/octopus-1.jpg",
476        },
477        {
478          id: 5,
479          name: "Southeast Asia",
480          description: "THAILAND • INDONESIA • MALAYSIA",
481          image: "/src/images/octopus-1.jpg",
482        },
483        {
484          id: 6,
485          name: "Indian Ocean",
486          description: "MALDIVES • SEYCHELLES • MAURITIUS",
487          image: "/src/images/octopus-1.jpg",
488        },
489        {
490          id: 7,
491          name: "Central America",
492          description: "COSTA RICA • PANAMA • BELIZE",
493          image: "/src/images/octopus-1.jpg",
494        },
495        {
496          id: 8,
497          name: "Middle East",
498          description: "UAE • QATAR • OMAN",
499          image: "/src/images/octopus-1.jpg",
500        },
501        {
502          id: 9,
503          name: "Northern Europe",
504          description: "NORWAY • SWEDEN • FINLAND",
505          image: "/src/images/octopus-1.jpg",
506        },
507      ],
508  
509      // Accordion state management
510      activeAccordion: null,
511  
512      // Yacht count for filtering results
513      yachtCount: '', // This will be updated via AJAX
514  
515      priceFilter,
516      lengthFilter,
517      cabinsFilter,
518      guestsFilter,
519      yachtTypeFilter,
520      builderFilter,
521      exteriorDesignerFilter,
522      interiorDesignerFilter,
523      yearBuiltFilter,
524      lastRefitFilter,
525      bookingOptionsFilter,
526      featuresFilter,
527
528      yachtLabel() {
529        // Check if a yacht is selected from search
530        if (this.selectedYachtFromSearch) {
531          return this.selectedYachtFromSearch.name.toUpperCase();
532        }
533
534        // Check if a popular yacht type is selected
535        if (this.popularYachtType) {
536          const popularTypeLabels = {
537            'new-build': 'NEW BUILD',
538            'under-construction': 'UNDER CONSTRUCTION',
539            'classic': 'CLASSIC YACHTS',
540            'superyachts': 'SUPERYACHTS',
541            'multihull': 'MULTIHULL'
542          };
543          
544          if (popularTypeLabels[this.popularYachtType]) {
545            return popularTypeLabels[this.popularYachtType];
546          }
547        }
548
549        // Check if a specific yacht is selected
550        if (this.selectedYacht) {
551          return this.selectedYacht.name.toUpperCase();
552        }
553        
554        // Check if multiple yachts are selected
555        if (this.selectedYachts && this.selectedYachts.length > 0) {
556          if (this.selectedYachts.length === 1) {
557            return this.selectedYachts[0].toUpperCase();
558          } else if (this.selectedYachts.length <= 2) {
559            return this.selectedYachts.map(y => y.toUpperCase()).join(', ');
560          } else {
561            return `${this.selectedYachts.length} YACHTS`;
562          }
563        }
564        
565        // Check if yacht type is selected
566        if (this.yachtTypeFilter.selectedValue && this.yachtTypeFilter.selectedValue !== 'any') {
567          const option = this.yachtTypeFilter.options.find(o => o.value === this.yachtTypeFilter.selectedValue);
568          return option ? `${option.label} YACHTS` : 'ALL YACHTS';
569        }
570        
571        // Default
572        return 'ALL YACHTS';
573      },
574
575      get sizeLabel() {
576        // Check if size range has been modified from initial defaults
577        if (this.lengthFilter.minValue !== initialMinLength || 
578            this.lengthFilter.maxValue !== initialMaxLength) {
579          const minDisplay = this.lengthFilter.displayUnit === 'ft' 
580            ? Math.round(this.lengthFilter.minValue * 3.28084) 
581            : this.lengthFilter.minValue;
582          const maxDisplay = this.lengthFilter.displayUnit === 'ft' 
583            ? Math.round(this.lengthFilter.maxValue * 3.28084) 
584            : this.lengthFilter.maxValue;
585          const unit = this.lengthFilter.displayUnit === 'ft' ? 'FT' : 'M';
586          return `${minDisplay}-${maxDisplay}${unit}`;
587        }
588        
589        // Default
590        return 'ANY SIZE';
591      },
592
593      get priceLabel() {
594        // Check if price range has been modified from initial defaults
595        if (this.priceFilter.minValue !== initialMinPrice || 
596            this.priceFilter.maxValue !== initialMaxPrice) {
597          const formatPrice = (value) => {
598            let displayValue = value;
599            let symbol = '€';
600            
601            if (this.priceFilter.displayCurrency === '$') {
602              displayValue = Math.round(value * 1.17);
603              symbol = '$';
604            } else if (this.priceFilter.displayCurrency === '£') {
605              displayValue = Math.round(value * 0.86);
606              symbol = '£';
607            }
608            
609            if (displayValue >= 1000000) {
610              return symbol + (displayValue / 1000000).toFixed(1).replace(/\.0$/, '') + 'M';
611            } else if (displayValue >= 1000) {
612              return symbol + (displayValue / 1000).toFixed(0) + 'K';
613            }
614            return symbol + displayValue;
615          };
616          
617          return `${formatPrice(this.priceFilter.minValue)}-${formatPrice(this.priceFilter.maxValue)}`;
618        }
619        
620        // Default
621        return 'ANY PRICE';
622      },
623  
624      get activeFilterLabel() {
625        switch (this.activeFilter) {
626          case "yachts":
627            return "All yachts";
628          case "destination":
629            return "Anywhere";
630          case "date":
631            return "Any week";
632          default:
633            return "All yachts";
634        }
635      },
636
637       selectYacht(yacht) {
638        this.selectedYacht = yacht;
639        this.selectedYachts = [yacht.name];
640
641        // Handle popular yacht types (special Type selections)
642        const popularTypes = ['new-build', 'under-construction', 'classic', 'superyachts', 'multihull'];
643        
644        if (popularTypes.includes(yacht.id)) {
645          // Set the popular yacht type instead of regular yacht type
646          this.popularYachtType = yacht.id;
647          urlState.popularYachtType = yacht.id;
648          
649          // Clear regular yacht type when popular type is selected
650          this.yachtTypeFilter.selectedValue = 'any';
651          
652          //this.yachtResults()
653        } else if (yacht.specs === 'Type' || yacht.specs === 'Seasonal') {
654          // For any popular yacht type selection
655          this.popularYachtType = yacht.id;
656          urlState.popularYachtType = yacht.id;
657          
658          // Clear regular yacht type
659          this.yachtTypeFilter.selectedValue = 'any';
660        } else {
661          // For regular yacht selections, clear popular type
662          this.popularYachtType = null;
663          urlState.popularYachtType = null;
664        }
665
666        // Close the dropdown after selection
667        this.showYachtTypesDropdown = false;
668        this.resetDropdownHeight();
669        this.resetYachtDropdownState();
670      },
671
672      clearYachtSelection() {
673        this.selectedYacht = null;
674        this.selectedYachts = [];
675        this.yachtTypeFilter.selectedValue = 'any';
676        this.popularYachtType = null;
677        urlState.popularYachtType = null;
678      },
679
680      // Computed property to check if any dropdown is open
681      get anyDropdownOpen() {
682        return this.showYachtTypesDropdown || this.showSizeDropdown || this.showPriceDropdown;
683      },
684  
685      get filteredYachtSearch() {
686        if (!this.yachtSearchQuery || this.yachtSearchQuery.length < 2) {
687          return [];
688        }
689        return this.yachtSearchData
690          .filter((yacht) =>
691            yacht.name.toLowerCase().includes(this.yachtSearchQuery.toLowerCase())
692          )
693          .sort((a, b) => a.name.localeCompare(b.name));
694      },
695  
696      get activeSearchYachtData() {
697        const filtered = this.filteredYachtSearch;
698        return filtered.length > 0 ? filtered[this.activeSearchYacht] : null;
699      },
700
701      // Returns the selected yacht data if it's from search
702      get selectedYachtFromSearch() {
703        if (this.selectedYachtFromSearchId === null) {
704          return null;
705        }
706        return this.yachtSearchData.find(y => y.id === this.selectedYachtFromSearchId) || null;
707      },
708  
709      get filteredDestinationSearch() {
710        if (
711          !this.destinationSearchQuery ||
712          this.destinationSearchQuery.length < 2
713        ) {
714          return [];
715        }
716        return this.destinationSearchData.filter((destination) =>
717          destination.name
718            .toLowerCase()
719            .includes(this.destinationSearchQuery.toLowerCase())
720        );
721      },
722  
723      get activeSearchDestinationData() {
724        const filtered = this.filteredDestinationSearch;
725        return filtered.length > 0
726          ? filtered[this.activeSearchDestination]
727          : null;
728      },
729  
730      updateURL() {
731        const newParams = new URLSearchParams();
732  
733        // Only add non-default values to keep URL clean
734        Object.entries(paramMapping).forEach(([key, paramConfig]) => {
735          const currentValue = urlState[key];
736          const defaultValue = paramConfig.default();
737  
738          // Handle array comparison differently
739          if (paramConfig.type === "array") {
740            if (
741              Array.isArray(currentValue) &&
742              currentValue.length > 0 &&
743              JSON.stringify(currentValue) !== JSON.stringify(defaultValue)
744            ) {
745              newParams.set(key, currentValue.join(","));
746            }
747          } else if (currentValue !== defaultValue) {
748            newParams.set(key, currentValue);
749          }
750        });
751  
752        const url = `${window.location.pathname}${
753          newParams.toString() ? "?" + newParams.toString() : ""
754        }`;
755        window.history.replaceState(null, "", url);
756      },
757
758      getURL(){
759        const newParams = new URLSearchParams();
760  
761        // Only add non-default values to keep URL clean
762        Object.entries(paramMapping).forEach(([key, paramConfig]) => {
763          const currentValue = urlState[key];
764          const defaultValue = paramConfig.default();
765  
766          // Handle array comparison differently
767          if (paramConfig.type === "array") {
768            if (
769              Array.isArray(currentValue) &&
770              currentValue.length > 0 &&
771              JSON.stringify(currentValue) !== JSON.stringify(defaultValue)
772            ) {
773              newParams.set(key, currentValue.join(","));
774            }
775          } else if (currentValue !== defaultValue) {
776            newParams.set(key, currentValue);
777          }
778        });
779  
780        const url = newParams.toString() ? "?" + newParams.toString() : "";
781        return url;
782      },
783  
784      init() {
785        const savedColumns = localStorage.getItem("charterGridColumns");
786        if (savedColumns && !params.has("gridColumns")) {
787          this.gridColumns = parseInt(savedColumns);
788          urlState.gridColumns = this.gridColumns;
789        }
790
791        // Sync with global currency store on init
792        const currencyStore = Alpine.store("currency");
793        if (currencyStore && currencyStore.selected) {
794          this.priceFilter.syncWithCurrencyStore(currencyStore.selected);
795        }
796
797        // Watch for global currency store changes
798        this.$watch("$store.currency.selected", (newCurrency) => {
799          this.priceFilter.syncWithCurrencyStore(newCurrency);
800        });
801
802  
803        // Initialize popularYachtType from URL if present
804        if (urlState.popularYachtType) {
805          this.popularYachtType = urlState.popularYachtType;
806          
807          // Also set the selectedYacht to match the popular type
808          const popularTypeNames = {
809            'new-build': 'New Build',
810            'under-construction': 'Under Construction',
811            'classic': 'Classic Yachts',
812            'superyachts': 'Superyachts',
813            'multihull': 'Multihull'
814          };
815          
816          if (popularTypeNames[urlState.popularYachtType]) {
817            this.selectedYacht = {
818              name: popularTypeNames[urlState.popularYachtType],
819              id: urlState.popularYachtType,
820              specs: urlState.popularYachtType.includes('2025') ? 'Seasonal' : 'Type'
821            };
822            this.selectedYachts = [popularTypeNames[urlState.popularYachtType]];
823          }
824        }
825
826        // Initialize selectedYachtFromSearchId from URL if present
827        if (urlState.selectedYacht) {
828          const yachtSlug = urlState.selectedYacht;
829          const matchingYacht = this.yachtSearchData.find(y => {
830            const slug = y.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
831            return slug === yachtSlug;
832          });
833          if (matchingYacht) {
834            this.selectedYachtFromSearchId = matchingYacht.id;
835          }
836        }
837
838        // Initialize displays for both filters
839        this.priceFilter.initDisplay();
840        this.lengthFilter.initDisplay();
841  
842        let yachtDebounceTimeout;
843        // Set up URL synchronization using Alpine.effect
844       Alpine.effect(() => {
845        var first_submit = document.querySelector(".module-input-search .first-submit")?.value;
846        if(first_submit == 1) this.updateURL();
847
848        var url = this.getURL();
849        const link_button = document.getElementById("viewYachtsSaleButton");
850        const link_button_mobile = document.getElementById("viewYachtsSaleButtonMobile");
851        if(link_button && first_submit == 1) link_button.href = '/buy-a-yacht/yachts-for-sale'+url;
852        if(link_button_mobile && first_submit == 1) link_button_mobile.href = '/buy-a-yacht/yachts-for-sale'+url;
853
854        //Update the yacht counter after x time after the input is changed
855        clearTimeout(yachtDebounceTimeout);
856          yachtDebounceTimeout = setTimeout(() => {
857            this.yachtResults(1);
858            this.yachtResults();
859          }, 500);
860        });
861        
862  
863        this.$watch("gridColumns", (value) => {
864          localStorage.setItem("charterGridColumns", value);
865          urlState.gridColumns = value;
866          this.$dispatch("grid-columns-changed", {
867            columns: value,
868          });
869        });
870  
871        this.$watch("activeFilter", (value) => {
872          urlState.activeFilter = value;
873        });
874
875        const bottomBar = document.getElementById("bottom-bar");
876
877        if(bottomBar) {
878            let mm = gsap.matchMedia();
879
880            mm.add("(min-width: 1024px)", () => {
881                this.$watch([
882                    'showYachtTypesDropdown',
883                    'showSizeDropdown',
884                    'showPriceDropdown',
885                ].join(','), value => {
886                    if(this.showYachtTypesDropdown || this.showSizeDropdown || this.showPriceDropdown) {
887                        gsap
888                            .to(
889                                "#bottom-bar",
890                                {
891                                    width: "100%",
892                                    duration: 0.6,
893                                    ease: "power2.inOut",
894                                }
895                        )
896                    }
897                });
898            });
899        }
900  
901        // Watch price filter changes
902        this.$watch("priceFilter.minValue", () => {
903          urlState.minPrice = this.priceFilter.minValue;
904          if (!document.activeElement || document.activeElement.type !== "text") {
905            this.priceFilter.minDisplay = this.priceFilter.formattedMin;
906          }
907          this.$dispatch("price-range-changed", {
908            min: this.priceFilter.minValue,
909            max: this.priceFilter.maxValue,
910          });
911        });
912  
913        this.$watch("priceFilter.maxValue", () => {
914          urlState.maxPrice = this.priceFilter.maxValue;
915          if (!document.activeElement || document.activeElement.type !== "text") {
916            this.priceFilter.maxDisplay = this.priceFilter.formattedMax;
917          }
918          this.$dispatch("price-range-changed", {
919            min: this.priceFilter.minValue,
920            max: this.priceFilter.maxValue,
921          });
922        });
923
924        // Watch length filter changes
925        this.$watch("lengthFilter.minValue", () => {
926          urlState.minLength = this.lengthFilter.minValue;
927          if (!document.activeElement || document.activeElement.type !== "text") {
928            this.lengthFilter.minDisplay = this.lengthFilter.formattedMin;
929          }
930          this.$dispatch("length-range-changed", {
931            min: this.lengthFilter.minValue,
932            max: this.lengthFilter.maxValue,
933          });
934        });
935  
936        this.$watch("lengthFilter.maxValue", () => {
937          urlState.maxLength = this.lengthFilter.maxValue;
938          if (!document.activeElement || document.activeElement.type !== "text") {
939            this.lengthFilter.maxDisplay = this.lengthFilter.formattedMax;
940          }
941          this.$dispatch("length-range-changed", {
942            min: this.lengthFilter.minValue,
943            max: this.lengthFilter.maxValue,
944          });
945        });
946  
947        // Watch cabins filter changes
948        this.$watch("cabinsFilter.value", () => {
949          urlState.cabins = this.cabinsFilter.value;
950          this.$dispatch("cabins-changed", {
951            value: this.cabinsFilter.value,
952          });
953        });
954  
955        // Watch guests filter changes
956        this.$watch("guestsFilter.value", () => {
957          urlState.guests = this.guestsFilter.value;
958          this.$dispatch("guests-changed", {
959            value: this.guestsFilter.value,
960          });
961        });
962
963  
964        // Watch yacht type filter changes
965        this.$watch("yachtTypeFilter.selectedValue", () => {
966          urlState.yachtType = this.yachtTypeFilter.selectedValue;
967          this.$dispatch("yacht-type-changed", {
968            value: this.yachtTypeFilter.selectedValue,
969          });
970          //this.yachtResults()
971        });
972
973        // Watch popular yacht type changes
974        this.$watch("popularYachtType", () => {
975          urlState.popularYachtType = this.popularYachtType;
976          this.$dispatch("popular-yacht-type-changed", {
977            value: this.popularYachtType,
978          });
979        });
980  
981        // Watch builder filter changes
982        this.$watch("builderFilter.selectedBuilders", (newBuilders) => {
983          urlState.builders = [...newBuilders];
984          this.$dispatch("builders-changed", {
985            value: newBuilders,
986          });
987        });
988  
989        // Watch exterior designer filter changes
990        this.$watch("exteriorDesignerFilter.selectedExteriorDesigners", (newExteriorDesigners) => {
991          urlState.exteriorDesigners = [...newExteriorDesigners];
992          this.$dispatch("exterior-designers-changed", {
993            value: newExteriorDesigners,
994          });
995        });
996
997        // Watch interior designer filter changes
998        this.$watch("interiorDesignerFilter.selectedInteriorDesigners", (newInteriorDesigners) => {
999          urlState.interiorDesigners = [...newInteriorDesigners];
1000          this.$dispatch("interior-designers-changed", {
1001            value: newInteriorDesigners,
1002          });
1003        });
1004  
1005        // Watch year built filter changes
1006        this.$watch("yearBuiltFilter.selectedValue", () => {
1007          urlState.yearBuilt = this.yearBuiltFilter.selectedValue;
1008          this.$dispatch("year-built-changed", {
1009            value: this.yearBuiltFilter.selectedValue,
1010          });
1011        });
1012  
1013        // Watch last refit filter changes
1014        this.$watch("lastRefitFilter.selectedValue", () => {
1015          urlState.lastRefit = this.lastRefitFilter.selectedValue;
1016          this.$dispatch("last-refit-changed", {
1017            value: this.lastRefitFilter.selectedValue,
1018          });
1019        });
1020  
1021        // Watch booking options filter changes
1022        this.$watch("bookingOptionsFilter.selectedOptions", (newOptions) => {
1023          urlState.bookingOptions = [...newOptions];
1024          this.$dispatch("booking-options-changed", {
1025            value: newOptions,
1026          });
1027        });
1028  
1029        // Watch features filter changes
1030        this.$watch("featuresFilter.selectedFeatures", (newFeatures) => {
1031          urlState.features = [...newFeatures];
1032          this.$dispatch("features-changed", {
1033            value: newFeatures,
1034          });
1035        });
1036
1037        // Watch dropdown states to control header visibility
1038        const updateHeaderVisibility = () => {
1039          const anyDropdownOpen = this.showYachtTypesDropdown || this.showSizeDropdown || this.showPriceDropdown;
1040          this.$dispatch('hide-nav', anyDropdownOpen);
1041        };
1042        this.$watch("showYachtTypesDropdown", updateHeaderVisibility);
1043        this.$watch("showSizeDropdown", updateHeaderVisibility);
1044        this.$watch("showPriceDropdown", updateHeaderVisibility);
1045  
1046        // Watch yacht search query changes
1047        this.$watch("yachtSearchQuery", () => {
1048          this.resetSearchYacht();
1049        });
1050  
1051        // Watch destination search query changes
1052        this.$watch("destinationSearchQuery", () => {
1053          this.resetSearchDestination();
1054        });
1055
1056        // Watch filter changes to update yacht count
1057        this.$watch("yachtTypeFilter.selectedValue", () => this.fetchYachtCount());
1058        this.$watch("lengthFilter.minValue", () => this.fetchYachtCount());
1059        this.$watch("lengthFilter.maxValue", () => this.fetchYachtCount());
1060        this.$watch("priceFilter.minValue", () => this.fetchYachtCount());
1061        this.$watch("priceFilter.maxValue", () => this.fetchYachtCount());
1062      },
1063  
1064      handleYachtFilter() {
1065        this.activeFilter = "yachts";
1066      },
1067  
1068      setSeason(val) {
1069        if (this.season === val) {
1070          this.season = null;
1071        } else {
1072          this.season = val;
1073        }
1074        this.months = [];
1075      },
1076  
1077      toggleMonth(val) {
1078        if (!this.months.includes(val)) {
1079          this.season = null;
1080        }
1081        if (this.months.includes(val)) {
1082          this.months = this.months.filter((m) => m !== val);
1083        } else {
1084          this.months.push(val);
1085        }
1086      },
1087  
1088      resetDateDropdownState() {
1089        // Don't reset the actual selections, just close the dropdown
1090        this.showDateDropdown = false;
1091      },
1092  
1093      toggleYachtTypesDropdown() {
1094        if (this.showYachtTypesDropdown) {
1095          // If yacht dropdown is open, close it
1096          this.showYachtTypesDropdown = false;
1097          this.resetDropdownHeight();
1098          this.resetYachtDropdownState();
1099        } else {
1100          // If yacht dropdown is closed, open it
1101          if (this.showSizeDropdown || this.showPriceDropdown) {
1102            // If other dropdown is open, close it first, then open this
1103            this.showSizeDropdown = false;
1104            this.resetSizeDropdownHeight();
1105            this.resetSizeDropdownState();
1106
1107            this.showPriceDropdown = false;
1108            this.resetPriceDropdownHeight();
1109            this.resetPriceDropdownState();
1110  
1111            // Wait for other dropdown to close before opening this
1112            setTimeout(() => {
1113              this.showYachtTypesDropdown = true;
1114              this.$nextTick(() => {
1115                this.setDropdownFixedHeight();
1116              });
1117            }, 150); // Match the transition duration
1118          } else {
1119            // No other dropdowns open, open immediately
1120            this.showYachtTypesDropdown = true;
1121            this.$nextTick(() => {
1122              this.setDropdownFixedHeight();
1123            });
1124          }
1125  
1126          // Always close filter modal and date dropdown
1127          this.showFilterModal = false;
1128          this.showDateDropdown = false;
1129        }
1130      },
1131  
1132      handleSizeDropdown() {
1133        if (this.showSizeDropdown) {
1134          // If sizes dropdown is open, close it
1135          this.showSizeDropdown = false;
1136          this.resetSizeDropdownHeight();
1137          this.resetSizeDropdownState();
1138        } else {
1139          // If yacht dropdown is closed, open it
1140          if (this.showYachtTypesDropdown || this.showPriceDropdown) {
1141            // If other dropdown is open, close it first, then open this
1142            this.showYachtTypesDropdown = false;
1143            this.resetDropdownHeight();
1144            this.resetYachtDropdownState();
1145
1146            this.showPriceDropdown = false;
1147            this.resetPriceDropdownHeight();
1148            this.resetPriceDropdownState();
1149  
1150            // Wait for other dropdown to close before opening this
1151            setTimeout(() => {
1152              this.showSizeDropdown = true;
1153              this.$nextTick(() => {
1154                this.setSizeDropdownFixedHeight();
1155              });
1156            }, 150); // Match the transition duration
1157          } else {
1158            // No other dropdowns open, open immediately
1159            this.showSizeDropdown = true;
1160            this.$nextTick(() => {
1161              this.setSizeDropdownFixedHeight();
1162            });
1163          }
1164  
1165          // Always close filter modal and date dropdown
1166          this.showFilterModal = false;
1167          this.showDateDropdown = false;
1168        }
1169      },
1170  
1171      handlePriceDropdown() {
1172        if (this.showPriceDropdown) {
1173          // If prices dropdown is open, close it
1174          this.showPriceDropdown = false;
1175          this.resetPriceDropdownHeight();
1176          this.resetPriceDropdownState();
1177        } else {
1178          // If yacht dropdown is closed, open it
1179          if (this.showYachtTypesDropdown || this.showSizeDropdown) {
1180            // If other dropdown is open, close it first, then open this
1181            this.showYachtTypesDropdown = false;
1182            this.resetDropdownHeight();
1183            this.resetYachtDropdownState();
1184
1185            this.showSizeDropdown = false;
1186            this.resetSizeDropdownHeight();
1187            this.resetSizeDropdownState();
1188  
1189            // Wait for other dropdown to close before opening this
1190            setTimeout(() => {
1191              this.showPriceDropdown = true;
1192              this.$nextTick(() => {
1193                this.setPriceDropdownFixedHeight();
1194              });
1195            }, 150); // Match the transition duration
1196          } else {
1197            // No other dropdowns open, open immediately
1198            this.showPriceDropdown = true;
1199            this.$nextTick(() => {
1200              this.setPriceDropdownFixedHeight();
1201            });
1202          }
1203  
1204          // Always close filter modal and date dropdown
1205          this.showFilterModal = false;
1206          this.showDateDropdown = false;
1207        }
1208      },
1209  
1210      setDropdownFixedHeight() {
1211        const dropdown = document.querySelector("[data-yacht-dropdown]");
1212        if (dropdown) {
1213          // Capture the current height (default state)
1214          const currentHeight = dropdown.offsetHeight;
1215          dropdown.style.height = `${currentHeight}px`;
1216          dropdown.style.minHeight = `${currentHeight}px`;
1217          dropdown.style.maxHeight = `${currentHeight}px`;
1218        }
1219      },
1220  
1221      resetDropdownHeight() {
1222        const dropdown = document.querySelector("[data-yacht-dropdown]");
1223        if (dropdown) {
1224          dropdown.style.height = "auto";
1225          dropdown.style.minHeight = "auto";
1226          dropdown.style.maxHeight = "auto";
1227        }
1228      },
1229  
1230      setSizeDropdownFixedHeight() {
1231        const dropdown = document.querySelector("[data-size-dropdown]");
1232        if (dropdown) {
1233          // Capture the current height (default state)
1234          const currentHeight = dropdown.offsetHeight;
1235          dropdown.style.height = `${currentHeight}px`;
1236          dropdown.style.minHeight = `${currentHeight}px`;
1237          dropdown.style.maxHeight = `${currentHeight}px`;
1238        }
1239      },
1240  
1241      resetSizeDropdownHeight() {
1242        const dropdown = document.querySelector("[data-size-dropdown]");
1243        if (dropdown) {
1244          dropdown.style.height = "auto";
1245          dropdown.style.minHeight = "auto";
1246          dropdown.style.maxHeight = "auto";
1247        }
1248      },
1249  
1250      setPriceDropdownFixedHeight() {
1251        const dropdown = document.querySelector("[data-price-dropdown]");
1252        if (dropdown) {
1253          // Capture the current height (default state)
1254          const currentHeight = dropdown.offsetHeight;
1255          dropdown.style.height = `${currentHeight}px`;
1256          dropdown.style.minHeight = `${currentHeight}px`;
1257          dropdown.style.maxHeight = `${currentHeight}px`;
1258        }
1259      },
1260  
1261      resetPriceDropdownHeight() {
1262        const dropdown = document.querySelector("[data-price-dropdown]");
1263        if (dropdown) {
1264          dropdown.style.height = "auto";
1265          dropdown.style.minHeight = "auto";
1266          dropdown.style.maxHeight = "auto";
1267        }
1268      },
1269  
1270      setActiveSearchYacht(index) {
1271        this.activeSearchYacht = index;
1272      },
1273  
1274      resetSearchYacht() {
1275        this.activeSearchYacht = 0;
1276      },
1277  
1278      setActiveSearchDestination(index) {
1279        this.activeSearchDestination = index;
1280      },
1281  
1282      resetSearchDestination() {
1283        this.activeSearchDestination = 0;
1284      },
1285  
1286      resetDestinationDropdownState() {
1287        this.destinationSearchQuery = "";
1288        this.activeSearchDestination = 0;
1289      },
1290  
1291      resetYachtDropdownState() {
1292        this.yachtSearchQuery = "";
1293        this.activeSearchYacht = 0;
1294      },
1295  
1296      resetSizeDropdownState() {
1297      },
1298  
1299      resetPriceDropdownState() {
1300      },
1301  
1302      openMobileFilters() {
1303        this.showMobileFilters = true;
1304        this.showYachtTypesDropdown = false; // Close desktop yacht dropdown
1305        this.showDestinationDropdown = false; // Close desktop destination dropdown
1306  
1307        // Reset dropdown states when opening mobile filters
1308        this.resetYachtDropdownState();
1309        this.resetDestinationDropdownState();
1310
1311        // Hide the navigation header
1312        window.dispatchEvent(new CustomEvent('hide-nav', { detail: true }));
1313  
1314        window.locomotiveScroll.stop();
1315      },
1316  
1317      closeMobileFilters() {
1318        this.showMobileFilters = false;
1319        this.showMobileYachtDropdown = false;
1320        this.showMobileSizeDropdown = false;
1321        this.showMobilePriceDropdown = false;
1322        this.showMobileSortDropdown = false;
1323        this.mobileActiveAccordion = null;
1324
1325        // Show the navigation header
1326        window.dispatchEvent(new CustomEvent('hide-nav', { detail: false }));
1327
1328        window.locomotiveScroll.start();
1329      },
1330  
1331      toggleMobileYachtDropdown() {
1332        this.showMobileYachtDropdown = !this.showMobileYachtDropdown;
1333      },
1334  
1335      closeMobileYachtDropdown() {
1336        this.showMobileYachtDropdown = false;
1337      },
1338  
1339      toggleMobileSizeDropdown() {
1340        this.showMobileSizeDropdown = !this.showMobileSizeDropdown;
1341      },
1342  
1343      closeMobileSizeDropdown() {
1344        this.showMobileSizeDropdown = false;
1345      },
1346  
1347      toggleMobilePriceDropdown() {
1348        this.showMobilePriceDropdown = !this.showMobilePriceDropdown;
1349      },
1350  
1351      closeMobilePriceDropdown() {
1352        this.showMobilePriceDropdown = false;
1353      },
1354
1355      toggleMobileSortDropdown() {
1356        this.showMobileSortDropdown = !this.showMobileSortDropdown;
1357      },
1358
1359      closeMobileSortDropdown() {
1360        this.showMobileSortDropdown = false;
1361      },
1362
1363      toggleMobileAccordion(section) {
1364        this.mobileActiveAccordion = this.mobileActiveAccordion === section ? null : section;
1365      },
1366
1367      selectSort(value) {
1368        this.sortBy = value;
1369        this.closeMobileSortDropdown();
1370
1371        var select_order = document.querySelector('.order-search');
1372        select_order.value = value;
1373        select_order.dispatchEvent(new Event('change'));
1374      },
1375
1376      getSortLabel() {
1377        const option = this.sortOptions.find(o => o.value === this.sortBy);
1378        return option ? option.label : 'Newest Listings';
1379      },
1380  
1381      openFilterModal() {
1382        this.showFilterModal = true;
1383        this.showYachtTypesDropdown = false; // Close yacht types dropdown
1384        this.showSizeDropdown = false; // Close sizes dropdown
1385        this.showDestinationDropdown = false; // Close destination dropdown
1386        this.showDateDropdown = false; // Close date dropdown
1387  
1388        // Reset dropdown states when opening filter modal
1389        this.resetYachtDropdownState();
1390        this.resetSizeDropdownState();
1391        this.resetDestinationDropdownState();
1392  
1393        window.locomotiveScroll.stop();
1394      },
1395  
1396      closeFilterModal() {
1397        this.showFilterModal = false;
1398        window.locomotiveScroll.start();
1399      },
1400  
1401      applyFilters() {
1402        this.yachtResults();
1403        this.closeFilterModal();
1404      },
1405
1406      // Close all dropdowns and reset their states
1407      closeAllDropdowns() {
1408        this.showYachtTypesDropdown = false;
1409        this.showSizeDropdown = false;
1410        this.showPriceDropdown = false;
1411        this.resetDropdownHeight();
1412        this.resetSizeDropdownHeight();
1413        this.resetPriceDropdownHeight();
1414        this.resetYachtDropdownState();
1415        this.resetSizeDropdownState();
1416        this.resetPriceDropdownState();
1417      },
1418
1419      // Mock fetch function to get yacht count based on current filters
1420      // TODO: Replace with real API endpoint when backend is ready
1421      async fetchYachtCount() {
1422        // Example of real API call (commented out for backend handoff):
1423        // const params = new URLSearchParams({
1424        //   yachtType: this.yachtTypeFilter.selectedValue || 'any',
1425        //   minPrice: this.priceFilter.minValue,
1426        //   maxPrice: this.priceFilter.maxValue,
1427        //   minLength: this.lengthFilter.minValue,
1428        //   maxLength: this.lengthFilter.maxValue,
1429        //   cabins: this.cabinsFilter.value,
1430        //   guests: this.guestsFilter.value,
1431        // });
1432        // const response = await fetch(`/api/buy/yachts/count?${params}`);
1433        // const data = await response.json();
1434        // this.yachtCount = data.count;
1435
1436        // Mock implementation - returns count based on filters to simulate backend response
1437        return new Promise((resolve) => {
1438          setTimeout(() => {
1439            const mockCounts = {
1440              'any': 286,
1441              'motor': 180,
1442              'sailing': 106
1443            };
1444            let count = mockCounts[this.yachtTypeFilter.selectedValue] || 286;
1445
1446            // Simulate size filtering
1447            if (this.lengthFilter.minValue > this.lengthFilter.absoluteMin ||
1448                this.lengthFilter.maxValue < this.lengthFilter.absoluteMax) {
1449              count = Math.floor(count * 0.7); // ~70% when size is filtered
1450            }
1451
1452            // Simulate price filtering
1453            if (this.priceFilter.minValue > this.priceFilter.absoluteMin ||
1454                this.priceFilter.maxValue < this.priceFilter.absoluteMax) {
1455              count = Math.floor(count * 0.6); // ~60% when price is filtered
1456            }
1457
1458            // this.yachtCount = count;
1459            // resolve(count);
1460          }, 100); // Small delay to simulate network
1461        });
1462      },
1463
1464      selectYachtType(type) {
1465        // Clear any popular yacht selection
1466        this.selectedYacht = null;
1467        this.selectedYachts = [];
1468        this.popularYachtType = null;
1469        urlState.popularYachtType = null;
1470
1471        // Clear any yacht from search selection
1472        this.selectedYachtFromSearchId = null;
1473        urlState.selectedYacht = null;
1474        
1475        // Update the yacht type filter
1476        this.yachtTypeFilter.updateValue(type);
1477        
1478        // Keep dropdown open (don't close on selection)
1479      },
1480
1481      selectYachtFromSearch(yacht) {
1482        // Store the selected yacht id
1483        this.selectedYachtFromSearchId = yacht.id;
1484
1485        // Generate slug from yacht name and store in URL
1486        const yachtSlug = yacht.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
1487        urlState.selectedYacht = yachtSlug;
1488
1489        // Clear any popular yacht selection
1490        this.selectedYacht = null;
1491        this.selectedYachts = [];
1492        this.popularYachtType = null;
1493        urlState.popularYachtType = null;
1494
1495        // Reset yacht type filter to "any" since we're selecting a specific yacht
1496        this.yachtTypeFilter.updateValue('any');
1497
1498        // Clear the search query
1499        this.yachtSearchQuery = '';
1500      },
1501
1502      clearSelectedYachtFromSearch() {
1503        this.selectedYachtFromSearchId = null;
1504        urlState.selectedYacht = null;
1505      },
1506  
1507      clearAllFilters() {
1508        // Reset all filters to default values
1509        this.priceFilter.minValue = this.priceFilter.absoluteMin;
1510        this.priceFilter.maxValue = this.priceFilter.absoluteMax;
1511        this.lengthFilter.minValue = this.lengthFilter.absoluteMin;
1512        this.lengthFilter.maxValue = this.lengthFilter.absoluteMax;
1513        this.cabinsFilter.value = this.cabinsFilter.minValue;
1514        this.guestsFilter.value = this.guestsFilter.minValue;
1515        this.yachtTypeFilter.selectedValue = "any";
1516        this.builderFilter.selectedBuilders = [];
1517        this.exteriorDesignerFilter.selectedExteriorDesigners = [];
1518        this.interiorDesignerFilter.selectedInteriorDesigners = [];
1519        this.yearBuiltFilter.selectedValue = "any";
1520        this.lastRefitFilter.selectedValue = "any";
1521        this.bookingOptionsFilter.selectedOptions = [];
1522        this.featuresFilter.selectedFeatures = [];
1523  
1524        // Reset yacht selections
1525        this.selectedYacht = null;
1526        this.selectedYachts = [];
1527
1528        // Reset displays
1529        this.priceFilter.initDisplay();
1530        this.lengthFilter.initDisplay();
1531      },
1532
1533      yachtResults(count){
1534        if(count == 1) var action = '/core/?pages:search_get_data_count';
1535        else var action = '/core/?pages:search_get_data';
1536        
1537        var y_for = document.querySelector(".module-input-search .yacht_for")?.value;
1538        var promobanner = document.querySelector(".module-input-search .promobanner")?.value;
1539        var order = document.querySelector(".module-input-search .order-search")?.value;
1540        var first_submit = document.querySelector(".module-input-search .first-submit")?.value;
1541        var input_bar = document.querySelector(".module-input-search .input-bar")?.value;
1542
1543        const currencyStore = Alpine.store("currency");
1544        const formData = new FormData();
1545        if(this.selectedYachtFromSearch) formData.append('yref', this.selectedYachtFromSearch.id);
1546        formData.append('min_price', this.priceFilter.minValue);
1547        formData.append('max_price', this.priceFilter.maxValue);
1548        formData.append('currency', urlState.currency);
1549        formData.append('min_loa', this.lengthFilter.minValue);
1550        formData.append('max_loa', this.lengthFilter.maxValue);
1551        formData.append('cabins', this.cabinsFilter.value);
1552        formData.append('guest', this.guestsFilter.value);
1553        formData.append('id_type', this.yachtTypeFilter.selectedValue);
1554        formData.append('id_builder', this.builderFilter.selectedBuilders);
1555        formData.append('year_range',  this.yearBuiltFilter.selectedValue);
1556        formData.append('refit_range', this.lastRefitFilter.selectedValue);
1557        formData.append('bookingOptions', this.bookingOptionsFilter.selectedOptions);
1558        formData.append('features_list', this.featuresFilter.selectedFeatures);
1559        formData.append('popular_search', this.popularYachtType);
1560        formData.append('exterior_designer', this.exteriorDesignerFilter.selectedExteriorDesigners);
1561        formData.append('interior_designer', this.interiorDesignerFilter.selectedInteriorDesigners);
1562        formData.append('currency', currencyStore.selected);
1563        formData.append('yachtFor', y_for);
1564        formData.append('promobanner', promobanner);
1565        formData.append('order', order);
1566        formData.append('count', 1);
1567
1568        if(first_submit == 1 || input_bar == 1){
1569          fetch(action, {
1570          method: 'POST',
1571            body: formData
1572          })
1573          .then(response => response.text())
1574          .then(data => {
1575            if(count == 1) this.yachtCount = data;
1576            else{
1577              var grid_container = document.querySelector(".grid-container");
1578              if(grid_container) grid_container.innerHTML = data;
1579            } 
1580
1581            const yacht_number = document.querySelectorAll(".search-yacht-card").length;
1582            var counter_result_container = document.getElementById("countResultYacht");
1583            if(counter_result_container) counter_result_container.textContent = 
1583yacht_number +' results';
1584
1585            if(input_bar == 1) document.querySelector('.first-submit').value = 1;
1586          });
1587        } else if(!count) {
1588          document.querySelector('.first-submit').value = 1
1589        }
1590        
1591      }, 
1592    };
1593  };
1594  

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