PageSourceSearch

https://www.designeroutletalgarve.com/_assets/6a101b282a6a03d13b1a9c6b8fc74836/JavaScript/filter.js?1790758556

js designeroutletalgarve.com collected 2026-10-04 12:23:59 UTC 17,163 bytes, 426 lines download raw bytes

1$(document).ready(function (e) {
2  $(".loading-spinner").hide();
3
4  // Submit the filter form via AJAX and replace the correct fragment
5  $(document).on("submit", "#filterForm", function (e) {
6    e.preventDefault();
7    // Ensure hidden allcategory flag reflects current selection
8    setAllCategoryHiddenFromSelection();
9    // If offcanvas is open, close it before updating the DOM to avoid body scroll lock
10    closeFiltersOffcanvas();
11    changeFilter(this);
12  });
13
14  $(document).on("click", ".filters__character-link", function (e) {
15    e.preventDefault();
16    var $form = $(this).closest('form');
17    $form.find(".js_brand-search-input").val("").trigger("input");
18    $form.find("#formCharacter").val($(this).data("character"));
19    changeFilter($form[0]);
20  });
21
22  $(document).on("click", ".filters__option__list__link", function (e) {
23    e.preventDefault();
24    var form = e.currentTarget.closest('form') || document.getElementById('filterForm');
25    var $form = $(form);
26    if ($form.find("#formSorting").val() === 'DESC') {
27      $form.find("#formSorting").val('ASC');
28    } else {
29      $form.find("#formSorting").val('DESC');
30    }
31    changeFilter(form);
32  });
33
34  $(document).on("click", ".filters__selection-link", function (e) {
35    e.preventDefault();
36    // Ignore the global reset link here; handled separately
37    if ($(this).attr("id") === 'resetall') {
38      return;
39    }
40
41    // Resolve form even if link is outside of it, then submit via AJAX
42    var form = e.currentTarget.closest('form') || document.getElementById('filterForm');
43    var $form = $(form);
44
45    // Unselect respective brand/category/search if present
46    var brandId = $(this).data("brand");
47    var categoryId = $(this).data("category");
48    var isSearch = $(this).data("search");
49    if (brandId) {
50      $form.find("#" + brandId).prop("checked", false);
51    }
52    if (categoryId) {
53      $form.find("#" + categoryId).not(":disabled").prop("checked", false);
54    }
55    if (isSearch) {
56      $form.find(".js_brand-search-input").val("").trigger("input");
57    }
58
59    // If no brands remain, re-check the global brand/all-offers toggles
60    var hasAnyBrand = $form.find("input.filters__brand-input:checked").filter(function(){
61      return parseInt($(this).val(), 10) > -1;
62    }).length > 0;
63
64    if (hasAnyBrand) {
65      $form.find("#all-brands").prop("checked", false);
66      $form.find("#all-offers").prop("checked", false);
67      $form.find("#formAllBrand").val('0');
68    } else {
69      $form.find("#all-brands").prop("checked", true);
70      $form.find("#all-offers").prop("checked", true);
71      $form.find("#formAllBrand").val('-1');
72    }
73
74    // Maintain deterministic all-categories state and hidden field
75    var allCategoriesChecked = $form.find("#all-categories").prop("checked");
76    var hasAnyCategory = $form.find("input.filters__category-input:checked").filter(function(){
77      return parseInt($(this).val(), 10) > 0;
78    }).length > 0;
79
80    if (hasAnyCategory) {
81      $form.find("#all-categories").prop("checked", false);
82      $form.find("#formAllCategory").val('0');
83    } else {
84      $form.find("#all-categories").prop("checked", true);
85      $form.find("#formAllCategory").val('-1');
86      // Do not remove categories that are disabled (fixed)
87      // NOTE: We no longer remove hidden inputs here as it can lead to inconsistent state
88      // when multiple filters are being unchecked.
89    }
90
91    // Keep clear button state consistent
92    updateClearButtonState($form);
93
94    // Resolve form even if link is outside of it, then submit via AJAX
95    if (form) {
96      changeFilter(form);
97    }
98  });
99
100  $(document).on("click", "#resetall, #resetall-noresults", function (e) {
101    e.preventDefault();
102    // Resolve form even if link is outside of it
103    var form = e.currentTarget.closest('form') || document.getElementById('filterForm');
104    performFullResetAndSubmit(form);
105  });
106
107  $(document).on("click", "[data-viewmode]", function (e) {
108    e.preventDefault();
109    var viewMode = $(this).data("viewmode");
110    var form = e.currentTarget.closest('form') || document.getElementById('filterForm');
111    var $form = $(form);
112    $form.find("#formViewMode").val(viewMode);
113
114    var list = form.closest('.rosFilterList');
115    if (list && viewMode === 'list') {
116      list.querySelectorAll('.pdlist--grid').forEach(e => {
117        e.querySelectorAll('.col-6').forEach(li => {
118          li.classList.add('col-12');
119          li.classList.remove('col-6');
120        });
121        e.classList.add('pdlist--list');
122        e.classList.remove('pdlist--grid');
123      });
124      list.querySelectorAll('[data-viewmode*="list"]').forEach(e => { e.classList.add('active');});
125      list.querySelectorAll('[data-viewmode*="grid"]').forEach(e => { e.classList.remove('active');});
126    } else {
127      list.querySelectorAll('.pdlist--list').forEach(e => {
128        e.querySelectorAll('.col-12').forEach(li => {
129          li.classList.add('col-6');
130          li.classList.remove('col-12');
131        });
132        e.classList.add('pdlist--grid');
133        e.classList.remove('pdlist--list');
134      });
135      list.querySelectorAll('[data-viewmode*="list"]').forEach(e => { e.classList.remove('active');});
136      list.querySelectorAll('[data-viewmode*="grid"]').forEach(e => { e.classList.add('active');});
137    }
138  });
139
140  // Clear-and-submit button next to submit inside offcanvas
141  $(document).on("click", "#clearAndSubmit", function (e) {
142    e.preventDefault();
143    // Resolve form even if link is outside of it
144    var form = e.currentTarget.closest('form') || document.getElementById('filterForm');
145    performFullResetAndSubmit(form);
146  });
147
148  $(document).on("input", ".brandFilter", function () {
149    var value = $(this).val().toLowerCase();
150    var $list = $(this).closest(".js_brand-names-list");
151    $list.find("li.filters__option__list-item").each(function () {
152      var text = $(this).find("label").text().toLowerCase();
153      $(this).toggle(text.indexOf(value) > -1);
154    });
155  });
156
157  $(document).on("input", ".js_brand-search-input", function () {
158    var value = $(this).val().toLowerCase();
159    var $form = $(this).closest("form");
160    $form.find(".js_brand-search-clear").toggle(value.length > 0);
161    $form.closest(".rosFilterList").find(".js_brand-item").each(function () {
162      var name = $(this).data("name") || "";
163      $(this).toggle(name.toString().startsWith(value));
164    });
165    updateClearButtonState($form);
166  });
167
168  $(document).on("keydown", ".js_brand-search-input", function (e) {
169    if (e.key === "Enter") {
170      e.preventDefault();
171    }
172  });
173
174  $(document).on("click", ".js_brand-search-clear", function () {
175    var $input = $(this).siblings(".js_brand-search-input");
176    $input.val("").trigger("input");
177    // If we are on a filtered page (e.g. submitted), we might want to submit after clear
178    // but the user said "shall only be cleared when I use the 'x' in the input"
179    // and "nothing shall happen" when pressing enter.
180    // However, if the page was reloaded with a search value, clearing it should probably refresh the list.
181    // Since it's a live filter, trigger("input") already updates the list visibility.
182    // If it was a server-side filter, we would need to submit the form.
183    // The brands are rendered in the template, and js_brand-search-input filters them.
184    $input.focus();
185  });
186
187  // Ensure brand checkbox state toggles naturally; do not prevent default
188  $(document).on("change", ".filters__brand-input", function (e) {
189    var $form = $(this).closest("form");
190    if ($(this).val() > -1) {
191      $form.find("#all-brands").prop("checked", false);
192      $form.find("#all-offers").prop("checked", false);
193      $form.find("#formReset").val(1);
194    } else {
195      $form.find(".filters__brand-input").prop("checked", false);
196      $form.find("#all-brands").prop("checked", true);
197      $form.find("#all-offers").prop("checked", true);
198      $form.find("#formReset").val(0);
199    }
200    // Do not auto-submit on checkbox change; submission happens via the explicit submit button
201    updateClearButtonState($form);
202    setAllBrandHiddenFromSelection($form);
203  });
204
205  // Ensure category checkbox state toggles naturally; do not prevent default
206  $(document).on("change", ".filters__category-input", function (e) {
207    var $form = $(this).closest("form");
208    if ($(this).is(":disabled")) {
209        return;
210    }
211    if ($(this).val() > 0) {
212      $form.find("#all-categories").prop("checked", false);
213      $form.find("#formReset").val(1);
214    } else {
215      $form.find(".filters__category-input").not(":disabled").prop("checked", false);
216      $form.find("#all-categories").prop("checked", true);
217      $form.find("#formReset").val(0);
218    }
219    // Do not auto-submit on checkbox change; submission happens via the explicit submit button
220    updateClearButtonState($form);
221    setAllCategoryHiddenFromSelection($form);
222  });
223
224  // Make entire list item clickable to toggle its checkbox
225  $(document).on("click", ".filters__option__list-item", function (e) {
226    // Avoid double-handling when the actual input is clicked
227    if (e.target && (e.target.tagName === 'INPUT' || e.target.tagName === 'LABEL')) {
228      return;
229    }
230    var $checkbox = $(this).find('input[type="checkbox"]').first();
231    if ($checkbox.length && !$checkbox.is(":disabled")) {
232      $checkbox.prop('checked', !$checkbox.prop('checked')).trigger('change');
233    }
234  });
235
236  // Enable/disable the clear button depending on whether any real filter is selected
237  function updateClearButtonState($form) {
238    if (!$form) {
239      $form = $("#filterForm");
240    }
241    var hasBrand = $form.find("input.filters__brand-input:checked").filter(function(){ return parseInt($(this).val(), 10) > -1; }).length > 0;
242    // For categories, only count those that are NOT disabled as "removable" filters for the clear button state
243    var hasCategory = $form.find("input.filters__category-input:checked").not(":disabled").filter(function(){ return parseInt($(this).val(), 10) > 0; }).length > 0;
244    var hasCharacter = (parseInt($form.find("#formCharacter").val() || '0', 10) !== 0);
245    var hasSearch = $form.find(".js_brand-search-input").val() ? $form.find(".js_brand-search-input").val().length > 0 : false;
246    // Also check if 'All' override is active, as this is a non-initial state if Flexform defaults were present
247    var hasAllOverride = ($form.find("#formAllCategory").val() === '-1' || $form.find("#formAllBrand").val() === '-1');
248    var hasAny = hasBrand || hasCategory || hasCharacter || hasAllOverride || hasSearch;
249    var $btn = $form.find("#clearAndSubmit");
250    if ($btn.length) {
251      $btn.prop('disabled', !hasAny).attr('aria-disabled', !hasAny ? 'true' : 'false');
252    }
253  }
254
255  // Initial state on load
256  $(".js_filters").each(function() {
257    var $form = $(this).is("form") ? $(this) : $(this).find("form");
258    updateClearButtonState($form);
259    $form.find(".js_brand-search-input").trigger("input");
260  });
261
262  // Shared routine: clear all filters and submit via AJAX
263  function performFullResetAndSubmit(form) {
264    // Fallback: resolve the main filter form by id when not provided/found
265    if (!form) {
266      form = document.getElementById('filterForm');
267      if (!form) {
268        return; // nothing we can do without the form
269      }
270    }
271    var $form = $(form);
272    $form.find(".filters__brand-input").prop("checked", false);
273    $form.find("#all-brands").prop("checked", false);
274    $form.find("#all-offers").prop("checked", false);
275
276    $form.find(".filters__category-input").not(":disabled").prop("checked", false);
277    $form.find("#all-categories").prop("checked", false);
278
279    $form.find(".js_brand-search-input").val("").trigger("input");
280
281    // clear any explicit 'all' override flags and individual selection fields
282    // this allows the controller to return to its initial/flexform state
283    $form.find("#formAllCategory").val('');
284    $form.find("#formAllBrand").val('');
285
286    // Reset pluginCategoryUids to its initial value if it exists in the DOM
287    var $pluginCatUids = $form.find("#pluginCategoryUids");
288    if ($pluginCatUids.length && $pluginCatUids.attr('data-initial')) {
289        $pluginCatUids.val($pluginCatUids.attr('data-initial'));
290    }
291
292    // Reset originalPluginCategoryUids hidden field if it exists
293    $form.find('input[name="originalPluginCategoryUids"]').val($pluginCatUids.attr('data-initial') || '');
294
295    $form.find("#formCharacter").val(0);
296    $form.find("#formReset").val(0);
297
298    // keep clear button state consistent before/after submit
299    updateClearButtonState($form);
300
301    changeFilter(form);
302  }
303
304  // Ensure the server always receives an explicit allcategory flag
305  function setAllCategoryHiddenFromSelection($form) {
306    try {
307      if (!$form) $form = $("#filterForm");
308      var anySelectedCategory = $form.find("input.filters__category-input:checked").filter(function(){
309        return parseInt($(this).val(), 10) > 0;
310      }).length > 0;
311
312      var val = '-1'; // Default to -1 (All)
313      if (anySelectedCategory) {
314        val = '0';
315      }
316      $form.find("#formAllCategory").val(val);
317    } catch (e) {
318      // no-op
319    }
320  }
321
322  function setAllBrandHiddenFromSelection($form) {
323    try {
324      if (!$form) $form = $("#filterForm");
325      var anySelectedBrand = $form.find("input.filters__brand-input:checked").filter(function(){
326        return parseInt($(this).val(), 10) > -1;
327      }).length > 0;
328
329      var val = '-1'; // Default to -1 (All)
330      if (anySelectedBrand) {
331        val = '0';
332      }
333      $form.find("#formAllBrand").val(val);
334    } catch (e) {
335      // no-op
336    }
337  }
338
339  function closeFiltersOffcanvas() {
340    try {
341      var offcanvasIds = ['brandFiltersOffcanvas', 'offferFiltersOffcanvas'];
342      offcanvasIds.forEach(function(id) {
343        var offcanvasEl = document.getElementById(id);
344        if (!offcanvasEl) return;
345        var isShown = offcanvasEl.classList.contains('show');
346        if (!isShown) return;
347        // Bootstrap 5 API
348        if (window.bootstrap && bootstrap.Offcanvas) {
349          var instance = bootstrap.Offcanvas.getInstance(offcanvasEl) || new bootstrap.Offcanvas(offcanvasEl);
350          instance.hide();
351        } else {
352          // Fallback: simulate closing by removing classes/attributes
353          offcanvasEl.classList.remove('show');
354          offcanvasEl.setAttribute('aria-hidden', 'true');
355        }
356      });
357    } catch (err) {
358      // no-op
359    }
360  }
361
362  function changeFilter(form) {
363    var $form = $(form);
364    var url = form.getAttribute("action");
365    // Ensure AJAX uses type=999 endpoint while normal form submit uses default page type
366    var pageType = form.getAttribute("data-ajax-pagetype") || "999";
367    url += (url.indexOf("?") === -1 ? "?" : "&") + "type=" + pageType;
368    // Finalize hidden flags before serializing
369    setAllCategoryHiddenFromSelection($form);
370    setAllBrandHiddenFromSelection($form);
371    $.ajax({
372      url: url,
373      type: form.getAttribute("method"),
374      data: new URLSearchParams(new FormData(form)).toString(),
375      dataType: "html",
376      beforeSend: function (xhr) {
377        $(".loading-spinner").show();
378      }
379    }).done(function (data) {
380      // Replace only the inner list/content so the offcanvas element persists
381      var $response = $('<div>').html(data);
382      var $newInner = $response.find('.ajaxResult').first();
383      var $currentWrapper = $form.closest('.rosFilterList');
384      var $currentInner = $currentWrapper.find('.ajaxResult').first();
385
386      if ($newInner.length && $currentInner.length) {
387        $currentInner.replaceWith($newInner);
388
389        var offcanvasIds = ['brandFiltersOffcanvas', 'offferFiltersOffcanvas'];
390        offcanvasIds.forEach(function(id) {
391          var $newOffcanvasBody = $response.find('#' + id + ' .offcanvas-body').first();
392          var $currentOffcanvasBody = $currentWrapper.find('#' + id + ' .offcanvas-body').first();
393          if ($newOffcanvasBody.length && $currentOffcanvasBody.length) {
394            $currentOffcanvasBody.replaceWith($newOffcanvasBody);
395          }
396        });
397      } else {
398        // Fallback: replace entire section if we couldn't target inner container
399        var $newSection = $response.find('.rosFilterList').first();
400        if ($newSection.length) {
401          var $current = $('.rosFilterList').first();
402          if ($current.length) {
403            $current.replaceWith($newSection);
404          }
405        } else {
406          var container = $form.closest('.rosFilterList')[0] || document.querySelector('.rosFilterList');
407          if (container) {
408            container.innerHTML = data;
409          }
410        }
411      }
412      // After content replacement, update clear button state based on new DOM
413      updateClearButtonState($form);
414    }).always(function () {
415      $(".loading-spinner").hide();
416      // Safety: if no offcanvas is open anymore, clear potential inline body lock styles
417      var anyOpenOffcanvas = document.querySelector('.offcanvas.show');
418      if (!anyOpenOffcanvas) {
419        try {
420          document.body.style.overflow = '';
421          document.body.style.paddingRight = '';
422        } catch (e) { /* no-op */ }
423      }
424    });
425  }
426});

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.