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.