From ce78fbd6852c8bedb5155018248608bec0a4aa2c Mon Sep 17 00:00:00 2001 From: metacreo Date: Fri, 11 Jul 2025 18:02:54 +0300 Subject: [PATCH 1/2] Drop jQuery Drop jQuery. Use native JS and bootstrap 5 --- assets/index.php | 29 + assets/vendor/index.php | 29 + assets/vendor/nouislider/index.php | 29 + assets/vendor/nouislider/nouislider.min.css | 1 + assets/vendor/nouislider/nouislider.min.js | 1 + blocklayered-no-products.tpl | 2 +- blocklayered.css | 278 ++--- blocklayered.js | 1163 ++++++++++--------- blocklayered.php | 8 +- blocklayered.tpl | 557 +++++---- 10 files changed, 1132 insertions(+), 965 deletions(-) create mode 100644 assets/index.php create mode 100644 assets/vendor/index.php create mode 100644 assets/vendor/nouislider/index.php create mode 100644 assets/vendor/nouislider/nouislider.min.css create mode 100644 assets/vendor/nouislider/nouislider.min.js diff --git a/assets/index.php b/assets/index.php new file mode 100644 index 0000000..826220d --- /dev/null +++ b/assets/index.php @@ -0,0 +1,29 @@ + + * @copyright 2017-2024 thirty bees + * @license Academic Free License (AFL 3.0) + */ + +header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); + +exit; diff --git a/assets/vendor/index.php b/assets/vendor/index.php new file mode 100644 index 0000000..826220d --- /dev/null +++ b/assets/vendor/index.php @@ -0,0 +1,29 @@ + + * @copyright 2017-2024 thirty bees + * @license Academic Free License (AFL 3.0) + */ + +header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); + +exit; diff --git a/assets/vendor/nouislider/index.php b/assets/vendor/nouislider/index.php new file mode 100644 index 0000000..826220d --- /dev/null +++ b/assets/vendor/nouislider/index.php @@ -0,0 +1,29 @@ + + * @copyright 2017-2024 thirty bees + * @license Academic Free License (AFL 3.0) + */ + +header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); + +exit; diff --git a/assets/vendor/nouislider/nouislider.min.css b/assets/vendor/nouislider/nouislider.min.css new file mode 100644 index 0000000..60f217c --- /dev/null +++ b/assets/vendor/nouislider/nouislider.min.css @@ -0,0 +1 @@ +.noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative}.noUi-base,.noUi-connects{width:100%;height:100%;position:relative;z-index:1}.noUi-connects{overflow:hidden;z-index:0}.noUi-connect,.noUi-origin{will-change:transform;position:absolute;z-index:1;top:0;right:0;height:100%;width:100%;-ms-transform-origin:0 0;-webkit-transform-origin:0 0;-webkit-transform-style:preserve-3d;transform-origin:0 0;transform-style:flat}.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin{left:0;right:auto}.noUi-vertical .noUi-origin{top:-100%;width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{-webkit-backface-visibility:hidden;backface-visibility:hidden;position:absolute}.noUi-touch-area{height:100%;width:100%}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{-webkit-transition:transform .3s;transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;right:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;right:-6px;bottom:-17px}.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle{left:-17px;right:auto}.noUi-target{background:#FAFAFA;border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-connects{border-radius:3px}.noUi-connect{background:#3FB8AF}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;white-space:nowrap;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-sub{background:#AAA}.noUi-marker-large{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate(-50%,50%);transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{-webkit-transform:translate(50%,50%);transform:translate(50%,50%)}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);padding-left:25px}.noUi-rtl .noUi-value-vertical{-webkit-transform:translate(0,50%);transform:translate(0,50%)}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;color:#000;padding:5px;text-align:center;white-space:nowrap}.noUi-horizontal .noUi-tooltip{-webkit-transform:translate(-50%,0);transform:translate(-50%,0);left:50%;bottom:120%}.noUi-vertical .noUi-tooltip{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);top:50%;right:120%}.noUi-horizontal .noUi-origin>.noUi-tooltip{-webkit-transform:translate(50%,0);transform:translate(50%,0);left:auto;bottom:10px}.noUi-vertical .noUi-origin>.noUi-tooltip{-webkit-transform:translate(0,-18px);transform:translate(0,-18px);top:auto;right:28px} \ No newline at end of file diff --git a/assets/vendor/nouislider/nouislider.min.js b/assets/vendor/nouislider/nouislider.min.js new file mode 100644 index 0000000..978a4a3 --- /dev/null +++ b/assets/vendor/nouislider/nouislider.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).noUiSlider={})}(this,function(ut){"use strict";function n(t){return"object"==typeof t&&"function"==typeof t.to}function ct(t){t.parentElement.removeChild(t)}function pt(t){return null!=t}function ft(t){t.preventDefault()}function i(t){return"number"==typeof t&&!isNaN(t)&&isFinite(t)}function dt(t,e,r){0=e[r];)r+=1;return r}function r(t,e,r){if(r>=t.slice(-1)[0])return 100;var n=l(r,t),i=t[n-1],o=t[n],t=e[n-1],n=e[n];return t+(r=r,a(o=[i,o],o[0]<0?r+Math.abs(o[0]):r-o[0],0)/s(t,n))}function o(t,e,r,n){if(100===n)return n;var i=l(n,t),o=t[i-1],s=t[i];return r?(s-o)/2this.xPct[n+1];)n++;else t===this.xPct[this.xPct.length-1]&&(n=this.xPct.length-2);r||t!==this.xPct[n+1]||n++;for(var i,o=1,s=(e=null===e?[]:e)[n],a=0,l=0,u=0,c=r?(t-this.xPct[n])/(this.xPct[n+1]-this.xPct[n]):(this.xPct[n+1]-t)/(this.xPct[n+1]-this.xPct[n]);0= 2) required for mode 'count'.");for(var e=t.values-1,r=100/e,n=[];e--;)n[e]=e*r;return n.push(100),U(n,t.stepped)}(d),m={},t=S.xVal[0],e=S.xVal[S.xVal.length-1],g=!1,v=!1,b=0;return(h=h.slice().sort(function(t,e){return t-e}).filter(function(t){return!this[t]&&(this[t]=!0)},{}))[0]!==t&&(h.unshift(t),g=!0),h[h.length-1]!==e&&(h.push(e),v=!0),h.forEach(function(t,e){var r,n,i,o,s,a,l,u,t=t,c=h[e+1],p=d.mode===ut.PipsMode.Steps,f=(f=p?S.xNumSteps[e]:f)||c-t;for(void 0===c&&(c=t),f=Math.max(f,1e-7),r=t;r<=c;r=Number((r+f).toFixed(7))){for(a=(o=(i=S.toStepping(r))-b)/(d.density||1),u=o/(l=Math.round(a)),n=1;n<=l;n+=1)m[(s=b+n*u).toFixed(5)]=[S.fromStepping(s),0];a=-1ut.PipsType.NoValue&&((t=P(a,!1)).className=p(n,f.cssClasses.value),t.setAttribute("data-value",String(r)),t.style[f.style]=e+"%",t.innerHTML=String(s.to(r))))}),a}function L(){s&&(ct(s),s=null)}function T(t){L();var e=D(t),r=t.filter,t=t.format||{to:function(t){return String(Math.round(t))}};return s=d.appendChild(O(e,r,t))}function j(){var t=i.getBoundingClientRect(),e="offset"+["Width","Height"][f.ort];return 0===f.ort?t.width||i[e]:t.height||i[e]}function z(n,i,o,s){function e(t){var e,r=function(e,t,r){var n=0===e.type.indexOf("touch"),i=0===e.type.indexOf("mouse"),o=0===e.type.indexOf("pointer"),s=0,a=0;0===e.type.indexOf("MSPointer")&&(o=!0);if("mousedown"===e.type&&!e.buttons&&!e.touches)return!1;if(n){var l=function(t){t=t.target;return t===r||r.contains(t)||e.composed&&e.composedPath().shift()===r};if("touchstart"===e.type){n=Array.prototype.filter.call(e.touches,l);if(1=r[e-1]});if(x!==!e)return x=!x,xt(f,f.connect.map(function(t){return!t})),void at()}rt(t),rt(t+1),x&&(rt(t-1),rt(t+2))}function tt(){g.forEach(function(t){var e=50r.stepAfter.startValue&&(i=r.stepAfter.startValue-n),t=n>r.thisStep.startValue?r.thisStep.step:!1!==r.stepBefore.step&&n-r.stepBefore.highestStep,100===e?i=null:0===e&&(t=null);e=S.countStepDecimals();return null!==i&&!1!==i&&(i=Number(i.toFixed(e))),[t=null!==t&&!1!==t?Number(t.toFixed(e)):t,i]}function at(){for(;n.firstChild;)n.removeChild(n.firstChild);for(var t=0;t<=f.handles;t++)u[t]=N(n,f.connect[t]),rt(t);Y({drag:f.events.drag,fixed:!0})}gt(t=d,f.cssClasses.target),0===f.dir?gt(t,f.cssClasses.ltr):gt(t,f.cssClasses.rtl),0===f.ort?gt(t,f.cssClasses.horizontal):gt(t,f.cssClasses.vertical),gt(t,"rtl"===getComputedStyle(t).direction?f.cssClasses.textDirectionRtl:f.cssClasses.textDirectionLtr),i=P(t,f.cssClasses.base),function(t,e){n=P(e,f.cssClasses.connects),l=[],(u=[]).push(N(n,t[0]));for(var r=0;r +

{l s='There are no products.' mod='blocklayered'}

diff --git a/blocklayered.css b/blocklayered.css index 7355e7f..0dec42a 100644 --- a/blocklayered.css +++ b/blocklayered.css @@ -1,183 +1,97 @@ -/** - * Copyright (C) 2017-2024 thirty bees - * Copyright (C) 2007-2016 PrestaShop SA - * - * thirty bees is an extension to the PrestaShop software by PrestaShop SA. - * - * NOTICE OF LICENSE - * - * This source file is subject to the Academic Free License (AFL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/afl-3.0.php - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@thirtybees.com so we can send you a copy immediately. - * - * @author thirty bees - * @author PrestaShop SA - * @copyright 2017-2024 thirty bees - * @copyright 2007-2016 PrestaShop SA - * @license Academic Free License (AFL 3.0) - * PrestaShop is an internationally registered trademark of PrestaShop SA. - */ - -#layered_block_left div.block_content { - padding: 6px; -} - -#layered_block_left ul { - padding-left: 0; - max-height: 120px; - overflow-y: auto; - overflow-x: hidden; - padding-left: 1px; - padding-right: 1px; -} - -#layered_form > div > div { - border-bottom: 1px dotted #CCCCCC; - padding-bottom: 5px; - margin-bottom: 5px; -} - -#layered_block_left .hide-action { - display: block; - text-align: right; - padding-right: 5px; -} -#layered_block_left .hide-action:hover { - text-decoration: underline; -} - -#layered_block_left ul li { - margin-left: 15px; - padding-left: 2px; - list-style-type: none; -} - -#layered_block_left #enabled_filters , #layered_url_filter_block { - font-size: 10px; - padding-bottom: 5px; -} - -#layered_block_left #enabled_filters ul li { - margin-left: 0; - padding-left: 0; -} - - -/* Style moved form the template to the css */ -#layered_block_left #enabled_filters ul li:hover { - background: inherit; - color: inherit; -} - -#layered_block_left #enabled_filters a { - text-decoration: none; - color: red; -} - -#layered_block_left ul li:hover { - background: #71BF47; - color: #FFF; -} - -#layered_block_left ul li label { - cursor: pointer; -} - -#layered_block_left ul li.nomargin { - margin-left: 0; -} - -#layered_block_left span.layered_subtitle { - float: left; - font-weight: bold; - font-size: 12px; - display: block; - margin-bottom: 4px; -} - -#layered_block_left span.layered_close a { text-decoration: none; } - -#layered_block_left span.layered_close { - float: right; - font-weight: bold; - font-size: 12px; - display: block; - margin-bottom: 4px; -} - -#layered_block_left ul li input.checkbox { - vertical-align: middle; - margin-left: 2px; -} - -#layered_block_left ul li .disabled { - color: #666; -} -#product_list .warning { - margin-top: 13px; -} - -#layered_form .select { - width: 165px; -} -#layered_form .color-option { - margin-left: 0; - width: 16px; - height: 16px; - padding:0; - border: 1px solid #666; -} -#layered_block_left .color-option.disable { - opacity: 0.5; - filter:alpha(opacity=50); - -moz-opacity:0.5; -} -#layered_form .color-option.on { - border: 1px solid red; -} -#layered_form input[type=radio] { - margin-left: 2px; - vertical-align: middle; -} -.layered_price_range { - margin-left: 7px; -} -.layered_price_range_unit { - margin-right: 7px; -} -.layered_slider_container { - margin: 6px 0 6px 6px; width: 93%; -} -.layered_slider { - margin-top:5px; -} -.layered_input_range_min { - width:30px; -} -.layered_input_range_max { - width:40px; -} -.layered_list_selected { - font-weight: bold; -} -.layered_ajax_loader p { - margin: 20px 0; - text-align: center; -} -.hide-action { - text-align: center; - text-align: right; - display: block; - cursor: pointer; -} -.hide-action:hover { - font-weight: bold; -} -#layered_form div div ul { - max-height: 200px; - overflow-x: auto; +@charset "UTF-8"; + +/* used bootstrap 5 CSS */ + +.range-slider { + --cz-range-slider-height: 0.125rem; + --cz-range-slider-bg: var(--cz-border-color); + --cz-range-slider-connect-bg: var(--cz-emphasis-color); + --cz-range-slider-handle-size: 0.8125rem; + --cz-range-slider-handle-bg: var(--cz-body-bg); + --cz-range-slider-handle-active-bg: var(--cz-emphasis-color); + --cz-range-slider-handle-border-width: 2px; + --cz-range-slider-handle-border-color: var(--cz-emphasis-color); + --cz-range-slider-handle-border-radius: 50%; + --cz-range-slider-pips-color: var(--cz-body-color); + --cz-range-slider-pips-font-size: 0.75rem; + --cz-range-slider-pips-border-width: var(--cz-border-width); + --cz-range-slider-pips-border-color: rgb(205.4, 213.4, 223); + --cz-range-slider-tooltip-padding-y: 0.375rem; + --cz-range-slider-tooltip-padding-x: 0.625rem; + --cz-range-slider-tooltip-bg: transparent; + --cz-range-slider-tooltip-color: var(--cz-emphasis-color); + --cz-range-slider-tooltip-font-size: 0.75rem; + --cz-range-slider-tooltip-border-radius: var(--cz-border-radius-sm); +} + +.range-slider-ui { + height: var(--cz-range-slider-height); + margin: 0; + margin-top: 2.25rem; + margin-right: 0; + margin-bottom: 1.75rem; + margin-left: 0; + background-color: var(--cz-range-slider-bg); + border: 0; + box-shadow: none; +} +.range-slider-ui .noUi-connect { + background-color: var(--cz-range-slider-connect-bg); +} +.range-slider-ui .noUi-handle { + top: 50%; + width: var(--cz-range-slider-handle-size); + height: var(--cz-range-slider-handle-size); + margin-top: calc(var(--cz-range-slider-handle-size) * -0.5); + background-color: var(--cz-range-slider-handle-bg); + border: var(--cz-range-slider-handle-border-width) solid var(--cz-range-slider-handle-border-color); + border-radius: var(--cz-range-slider-handle-border-radius); + box-shadow: none; +} +.range-slider-ui .noUi-handle::before, .range-slider-ui .noUi-handle::after { + display: none; +} +.range-slider-ui .noUi-handle:active, .range-slider-ui .noUi-handle:focus-visible { + background-color: var(--cz-range-slider-handle-active-bg); +} +.range-slider-ui .noUi-handle:focus { + outline: none; +} +.range-slider-ui .noUi-handle:focus-visible { + box-shadow: 0 0 0 0.25rem var(--cz-focus-ring-color); +} +.range-slider-ui .noUi-marker-normal { + display: none; +} +.range-slider-ui .noUi-marker-horizontal.noUi-marker { + width: var(--cz-range-slider-pips-border-width); + background-color: var(--cz-range-slider-pips-border-color); +} +.range-slider-ui .noUi-marker-horizontal.noUi-marker-large { + height: 0.75rem; +} +.range-slider-ui .noUi-value { + padding-top: 0.125rem; + font-size: var(--cz-range-slider-pips-font-size); + color: var(--cz-range-slider-pips-color); +} +.range-slider-ui .noUi-tooltip { + padding: var(--cz-range-slider-tooltip-padding-y) var(--cz-range-slider-tooltip-padding-x); + line-height: 1.2; + font-size: var(--cz-range-slider-tooltip-font-size); + font-weight: 500; + color: var(--cz-range-slider-tooltip-color); + background-color: var(--cz-range-slider-tooltip-bg); + border: 0; + border-radius: var(--cz-range-slider-tooltip-border-radius); +} + +html:not([dir=rtl]) .range-slider-ui.noUi-horizontal .noUi-handle { + right: calc(var(--cz-range-slider-handle-size) * -0.5); +} + +.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle { + /* rtl:begin:ignore */ + left: -0.5rem; + /* rtl:end:ignore */ } diff --git a/blocklayered.js b/blocklayered.js index 80f8939..6b12654 100644 --- a/blocklayered.js +++ b/blocklayered.js @@ -1,574 +1,671 @@ -/** - * Copyright (C) 2017-2024 thirty bees - * Copyright (C) 2007-2016 PrestaShop SA - * - * thirty bees is an extension to the PrestaShop software by PrestaShop SA. - * - * NOTICE OF LICENSE - * - * This source file is subject to the Academic Free License (AFL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/afl-3.0.php - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@thirtybees.com so we can send you a copy immediately. - * - * @author thirty bees - * @author PrestaShop SA - * @copyright 2017-2024 thirty bees - * @copyright 2007-2016 PrestaShop SA - * @license Academic Free License (AFL 3.0) - * PrestaShop is an internationally registered trademark of PrestaShop SA. - */ - -var ajaxQueries = new Array(); -var ajaxLoaderOn = 0; -var sliderList = new Array(); -var slidersInit = false; - -$(document).ready(function() { - cancelFilter(); - openCloseFilter(); - - // Click on color - $('#layered_form input[type=button], #layered_form label.layered_color').live('click', function() { - if (!$('input[name='+$(this).attr('name')+'][type=hidden]').length) { - $('').attr('type', 'hidden').attr('name', $(this).attr('name')).val($(this).data('rel')).appendTo('#layered_form'); - } else { - $('input[name='+$(this).attr('name')+'][type=hidden]').remove(); - } - reloadContent(); - }); - - // Click on checkbox - $('#layered_form input[type=checkbox], #layered_form input[type=radio]').live('click', function() { - reloadContent(); - }); - - $(document).on('change', '#layered_form .select', function(e) { - reloadContent(); - }); - - // Changing content of an input text - $('#layered_form input.layered_input_range').live('keyup', function() { - if ($(this).attr('timeout_id')) { - window.clearTimeout($(this).attr('timeout_id')); - } - - // IE Hack, setTimeout do not acept the third parameter - var reference = this; - - $(this).attr('timeout_id', window.setTimeout(function(it) { - if (!$(it).attr('id')) { - it = reference; - } - - var filter = $(it).attr('id').replace(/^layered_(.+)_range_.*$/, '$1'); - - var value_min = parseInt($('#layered_'+filter+'_range_min').val()); - if (isNaN(value_min)) { - value_min = 0; - } - $('#layered_'+filter+'_range_min').val(value_min); - - var value_max = parseInt($('#layered_'+filter+'_range_max').val()); - if (isNaN(value_max)) { - value_max = 0; - } - $('#layered_'+filter+'_range_max').val(value_max); - - if (value_max < value_min) { - $('#layered_'+filter+'_range_max').val($(it).val()); - $('#layered_'+filter+'_range_min').val($(it).val()); - } - reloadContent(); - }, 500, this)); - }); - - $('#layered_block_left .radio').live('click', function() { - var name = $(this).attr('name'); - $.each($(this).parent().parent().find('input[type=button]'), function (it, item) { - if ($(item).hasClass('on') && $(item).attr('name') != name) { - $(item).click(); - } - }); - return true; - }); - - // Click on label - $('#layered_block_left label a').live({ - click: function() { - var disable = $(this).parent().parent().find('input').attr('disabled'); - if (disable == '' - || typeof(disable) == 'undefined' - || disable == false) { - $(this).parent().parent().find('input').click(); - reloadContent(); - } - return false; - } - }); - - layered_hidden_list = {}; - $('.hide-action').live('click', function() { - if (typeof(layered_hidden_list[$(this).parent().find('ul').attr('id')]) == 'undefined' || layered_hidden_list[$(this).parent().find('ul').attr('id')] == false) { - layered_hidden_list[$(this).parent().find('ul').attr('id')] = true; - } else { - layered_hidden_list[$(this).parent().find('ul').attr('id')] = false; - } - hideFilterValueAction(this); - }); - $('.hide-action').each(function() { - hideFilterValueAction(this); - }); - - // To be sure there is no other events attached to the selectProductSort, change the ID - var id = 1; - while ($('#selectPrductSort').length) { // Because ids are duplicated - // Unbind event change on #selectPrductSort - $('#selectPrductSort').unbind('change'); - $('#selectPrductSort').attr('onchange', ''); - $('#selectPrductSort').addClass('selectProductSort'); - $('#selectPrductSort').attr('id', 'selectPrductSort'+id); - $('label[for=selectPrductSort]').attr('for', 'selectPrductSort'+id); - id++; - } - - while ($('#selectProductSort').length) { // Because ids are duplicated - // Unbind event change on #selectProductSort - $('#selectProductSort').unbind('change'); - $('#selectProductSort').attr('onchange', ''); - $('#selectProductSort').addClass('selectProductSort'); - $('#selectProductSort').attr('id', 'selectProductSort'+id); - $('label[for=selectProductSort]').attr('for', 'selectProductSort'+id); - id++; - } - - // Since 1.5, event is add to .selectProductSort and not to #selectProductSort - setTimeout(function() { - $('.selectProductSort').unbind('change'); - }, 100); - - $('.selectProductSort').live('change', function(event) { - $('.selectProductSort').val($(this).val()); - reloadContent(); - }); - - $(document).off('change', '.selectProductSort').on('change', '.selectProductSort', function () { - $('.selectProductSort').val($(this).val()); - if ($('#layered_form').length > 0) { - reloadContent(); - } - }); - - $(document).off('change', 'select[name="n"]').on('change', 'select[name="n"]', function () { - $('select[name=n]').val($(this).val()); - reloadContent(); - }); - - // To be sure there is no other events attached to the nb_item, change the ID - var id = 1; - while ($('#nb_item').length) { // Because ids are duplicated - // Unbind event change on #nb_item - $('#nb_item').unbind('change'); - $('#nb_item').attr('onchange', ''); - $('#nb_item').addClass('nb_item'); - $('#nb_item').attr('id', 'nb_item'+id); - $('label[for=nb_item]').attr('for', 'nb_item'+id); - id++; - } - $('.nb_item').live('change', function(event) { - $('.nb_item').val($(this).val()); - reloadContent(); - }); - - paginationButton(); - initLayered(); -}); - -function hideFilterValueAction(it) { - if (typeof(layered_hidden_list[$(it).parent().find('ul').attr('id')]) == 'undefined' || layered_hidden_list[$(it).parent().find('ul').attr('id')] == false) { - $(it).parent().find('.hiddable').hide(); - $(it).parent().find('.hide-action.less').hide(); - $(it).parent().find('.hide-action.more').show(); - } else { - $(it).parent().find('.hiddable').show(); - $(it).parent().find('.hide-action.less').show(); - $(it).parent().find('.hide-action.more').hide(); - } -} - -function addSlider(type, data, unit, format) { - sliderList = sliderList.filter(function(slider) { - return slider.type !== type; - }); - sliderList.push({ - type: type, - data: data, - unit: unit, - format: format - }); -} - -function initSliders() { - $(sliderList).each(function(i, slider) { - $('#layered_'+slider['type']+'_slider').slider(slider['data']); - - var from = ''; - var to = ''; - switch (slider['format']) { - case 1: - case 2: - case 3: - case 4: - from = formatCurrency($('#layered_'+slider['type']+'_slider').slider('values', 0), slider['format'], slider['unit']); - to = formatCurrency($('#layered_'+slider['type']+'_slider').slider('values', 1), slider['format'], slider['unit']); - break; - case 5: - from = $('#layered_'+slider['type']+'_slider').slider('values', 0)+slider['unit'] - to = $('#layered_'+slider['type']+'_slider').slider('values', 1)+slider['unit']; - break; - } - if (isRtl) { - $('#layered_'+slider['type']+'_range').html(to+' - '+from); - } else { - $('#layered_'+slider['type']+'_range').html(from+' - '+to); +/* global param_product_url */ + +let fetchControllers = []; + +const update_product_url_selector = 'div.product_list div.col div.product-card div.position-relative a, div.product_list div.col div.product-card div.w-100 a'; +const loader_class = 'opacity-50'; +const product_list_selector = '#product_list'; +const product_count_selector = '.category-product-count'; +const page_heading_selector = '.page-heading .cat-name'; + +function hideFilterValueAction(toggleElement) { + const list = toggleElement.closest('ul'); + if (list) { + const listId = list.id; + const expand = !!(window.layered_hidden_list && window.layered_hidden_list[listId]); + function toggleDisplay(elements, show) { + elements.forEach(el => { + el.style.display = show ? '' : 'none'; + }); + } + const hiddableElements = list.querySelectorAll('.hiddable'); + toggleDisplay(hiddableElements, expand); + const hideActionLessElements = list.querySelectorAll('.hide-action.less'); + toggleDisplay(hideActionLessElements, expand); + const hideActionMoreElements = list.querySelectorAll('.hide-action.more'); + toggleDisplay(hideActionMoreElements, !expand); } - }); } -function initLayered() { - initSliders(); - initLocationChange(); - updateProductUrl(); - if (window.location.href.split('#').length == 2 && window.location.href.split('#')[1] != '') { - var params = window.location.href.split('#')[1]; - reloadContent('&selected_filters='+params); - } +function openCloseFilter() { + document.addEventListener('click', function(e) { + const clickedLink = e.target.closest('#layered_form span.layered_close a'); + if (clickedLink) { + e.preventDefault(); + const iconElement = clickedLink.querySelector('i'); + const targetElement = document.getElementById(clickedLink.dataset.rel); + if (iconElement && iconElement.classList.contains('ci-chevron-left')) { + if (targetElement) { + targetElement.style.display = ''; + } + iconElement.classList.remove('ci-chevron-left'); + iconElement.classList.add('ci-chevron-down'); + clickedLink.parentNode.classList.remove('closed'); + } else if (iconElement && iconElement.classList.contains('ci-chevron-down')) { + if (targetElement) { + targetElement.style.display = 'none'; + } + iconElement.classList.remove('ci-chevron-down'); + iconElement.classList.add('ci-chevron-left'); + clickedLink.parentNode.classList.add('closed'); + } + } + }); } function paginationButton() { - $('#pagination a').not(':hidden').each(function () { - if ($(this).attr('href').search(/[&|\?]p=/) == -1) { - var page = 1; - } else { - var page = $(this).attr('href').replace(/^.*[&|\?]p=(\d+).*$/, '$1'); + if (typeof window.current_friendly_url === 'undefined') { + window.current_friendly_url = '#'; } - - var location = window.location.href.replace(/#.*$/, ''); - $(this).attr('href', location+current_friendly_url.replace(/\/page-(\d+)/, '')+'/page-'+page); - }); - $('#pagination li, .pagination li').not('.current, .disabled').each(function () { - var nbPage = 0; - if ($(this).attr('id') == 'pagination_next') { - nbPage = parseInt($('#pagination li.current').children().html().replace(/<(.|\n)*?>/g, ''))+ 1; - } else if ($(this).attr('id') == 'pagination_previous') { - nbPage = parseInt($('#pagination li.current').children().html().replace(/<(.|\n)*?>/g, ''))- 1; - } - - $(this).children().click(function () { - if (nbPage == 0) { - p = parseInt($(this).html().replace(/<(.|\n)*?>/g, '')) + parseInt(nbPage) ; - } else { - p = nbPage; - } - p = '&p='+p; - reloadContent(p); - nbPage = 0; - return false; + const paginationLinks = document.querySelectorAll('ul.pagination a'); + paginationLinks.forEach(function(link) { + const computedStyle = window.getComputedStyle(link); + if (computedStyle.display === 'none' || computedStyle.visibility === 'hidden' || computedStyle.opacity === '0') { + return; + } + let page; + const href = link.getAttribute('href'); + if (href.search(/[&|\?]p=/) === -1) { + page = 1; + } else { + const match = href.match(/[&|\?]p=(\d+)/); + if (match && match[1]) { + page = match[1]; + } else { + page = 1; + } + } + const location = window.location.href.replace(/#.*$/, ''); + const newHref = location + current_friendly_url.replace(/\/page-(\d+)/, '') + '/page-' + page; + link.setAttribute('href', newHref); + }); + document.querySelectorAll('ul.pagination li:not(.current):not(.disabled)').forEach(function(liElement) { + let nbPage = 0; + const currentPageLi = document.querySelector('ul.pagination li.current'); + let currentPageNumber = 0; + if (currentPageLi) { + const pageNumberContentElement = currentPageLi.querySelector('a') || currentPageLi.firstElementChild; + if (pageNumberContentElement) { + currentPageNumber = parseInt(pageNumberContentElement.innerHTML.replace(/<(.|\n)*?>/g, '')); + } + } + if (liElement.id === 'pagination_next' || liElement.id === 'pagination_next_bottom') { + nbPage = currentPageNumber + 1; + } else if (liElement.id === 'pagination_previous' || liElement.id === 'pagination_previous_bottom') { + nbPage = currentPageNumber - 1; + } + liElement.querySelectorAll('a').forEach(function(childLink) { + childLink.addEventListener('click', function(event) { + event.preventDefault(); + let p; + if (nbPage === 0) { + p = parseInt(childLink.innerHTML.replace(/<(.|\n)*?>/g, '')); + } else { + p = nbPage; + } + p = '&p=' + p; + reloadContent(p); + nbPage = 0; + return false; + }); + }); }); - }); -} - -function cancelFilter() { - $('#enabled_filters a').live('click', function(e) { - if ($(this).data('rel').search(/_slider$/) > 0) { - if ($('#'+$(this).data('rel')).length) { - $('#'+$(this).data('rel')).slider('values', 0, $('#'+$(this).data('rel')).slider('option', 'min')); - $('#'+$(this).data('rel')).slider('values', 1, $('#'+$(this).data('rel')).slider('option', 'max')); - $('#'+$(this).data('rel')).slider('option', 'slide')(0, {values:[$('#'+$(this).data('rel')).slider('option', 'min'), $('#'+$(this).data('rel')).slider('option', 'max')]}); - } else if ($('#'+$(this).data('rel').replace(/_slider$/, '_range_min')).length) { - $('#'+$(this).data('rel').replace(/_slider$/, '_range_min')).val($('#'+$(this).data('rel').replace(/_slider$/, '_range_min')).attr('limitValue')); - $('#'+$(this).data('rel').replace(/_slider$/, '_range_max')).val($('#'+$(this).data('rel').replace(/_slider$/, '_range_max')).attr('limitValue')); - } - } else { - if ($('option#'+$(this).data('rel')).length) { - $('#'+$(this).data('rel')).parent().val(''); - } else { - $('#'+$(this).data('rel')).attr('checked', false); - $('.'+$(this).data('rel')).attr('checked', false); - $('#layered_form input[type=hidden][name='+$(this).data('rel')+']').remove(); - } - } - reloadContent(); - e.preventDefault(); - }); -} - -function openCloseFilter() { - $('#layered_form span.layered_close a').live('click', function(e) { - if ($(this).html() == '<') { - $('#'+$(this).data('rel')).show(); - $(this).html('v'); - $(this).parent().removeClass('closed'); - } else { - $('#'+$(this).data('rel')).hide(); - $(this).html('<'); - $(this).parent().addClass('closed'); - } - - e.preventDefault(); - }); -} - -function stopAjaxQuery() { - if (typeof(ajaxQueries) == 'undefined') { - ajaxQueries = new Array(); - } - for(i = 0; i < ajaxQueries.length; i++) { - if (typeof ajaxQueries[i] != 'undefined') - ajaxQueries[i].abort(); - } - ajaxQueries = new Array(); } -function reloadContent(params_plus) { - stopAjaxQuery(); - - if (!ajaxLoaderOn) { - $('#layered_form').addClass( 'loading-overlay'); - $('#category-products').addClass('loading-overlay'); - ajaxLoaderOn = 1; - } - - data = $('#layered_form').serialize(); - $('.layered_slider').each(function () { - var sliderStart = $(this).slider('values', 0); - var sliderStop = $(this).slider('values', 1); - if (typeof(sliderStart) == 'number' && typeof(sliderStop) == 'number') { - data += '&'+$(this).attr('id')+'='+sliderStart+'_'+sliderStop; +function reloadContent(paramsPlus) { + const $form = document.getElementById('layered_form'); + const $categoryProducts = document.querySelector(product_list_selector); + stopFetchQuery(); + $form.classList.add('opacity-50'); + $categoryProducts.classList.add('opacity-50'); + let data = new FormData($form); + ['price', 'weight'].forEach(function(sliderType) { + const range_min_element = $form.querySelector('#layered_' + sliderType + '_range_min'); + const range_max_element = $form.querySelector('#layered_' + sliderType + '_range_max'); + if (range_min_element && range_max_element) { + data.append('layered_' + sliderType + '_slider', range_min_element.value + '_' + range_max_element.value); + } + }); + const selectOptions = $form.querySelectorAll('select option'); + if (selectOptions) { + selectOptions.forEach(function(optionElement) { + const parentSelect = optionElement.parentElement; + if (optionElement.id && parentSelect.value == optionElement.value) { + data.append(optionElement.id, optionElement.value); + } + }); } - }); - - $(['price', 'weight']).each(function(it, sliderType) { - if ($('#layered_'+sliderType+'_range_min').length) { - data += '&layered_'+sliderType+'_slider='+$('#layered_'+sliderType+'_range_min').val()+'_'+$('#layered_'+sliderType+'_range_max').val(); + let productSort = document.querySelector('select.selectProductSort'); + if (productSort) { + let selectedOption = productSort.options[productSort.selectedIndex]; + data.append('orderby', selectedOption.value.split(':')[0]); + data.append('orderway', selectedOption.value.split(':')[1]); } - }); - - $('#layered_form .select option').each(function () { - if ($(this).attr('id') && $(this).parent().val() == $(this).val()) { - data += '&'+$(this).attr('id')+'='+$(this).val(); + let productCountShow = document.querySelector('select.nb_item[name="n"]'); + if (productCountShow) { + data.append('n', productCountShow.value); } - }); - - if ($('.selectProductSort').length && $('.selectProductSort').val()) { - if ($('.selectProductSort').val().search(/orderby=/) > 0) { - // Old ordering working - var splitData = [ - $('.selectProductSort').val().match(/orderby=(\w*)/)[1], - $('.selectProductSort').val().match(/orderway=(\w*)/)[1] - ]; - } else { - // New working for default theme 1.4 and theme 1.5 - var splitData = $('.selectProductSort').val().split(':'); - } - data += '&orderby='+splitData[0]+'&orderway='+splitData[1]; - } - if ($('.nb_item').length) { - data += '&n='+$('.nb_item').val(); - } - - var slideUp = true; - if (params_plus == undefined || !(typeof params_plus == 'string')) { - params_plus = ''; - slideUp = false; - } - - // Get nb items per page - var n = ''; - $('#pagination #nb_item').children().each(function(it, option) { - if (option.selected) { - n = '&n='+option.value; + let slideUp = true; + if (typeof paramsPlus === 'undefined' || !(typeof paramsPlus === 'string')) { + paramsPlus = ''; + slideUp = false; } - }); - - ajaxQuery = $.ajax({ - type: 'GET', - url: baseDir+'modules/blocklayered/blocklayered-ajax.php', - data: data+params_plus+n, - dataType: 'json', - cache: false, // @todo see a way to use cache and to add a timestamps parameter to refresh cache each 10 minutes for example - success: function(result) { - $('#layered_block_left').replaceWith(utf8_decode(result.filtersBlock)); - - $('.category-product-count').html(result.categoryCount); - - if (result.productList) { - $('.product_list').replaceWith(utf8_decode(result.productList)); - } else { - $('.product_list').html(''); - } - - $('.product_list').css('opacity', '1'); - if ($.browser.msie) { // Fix bug with IE8 and aliasing - $('.product_list').css('filter', ''); - } - - if (result.pagination.search(/[^\s]/) >= 0) { - var pagination = $('
').html(result.pagination); - var pagination_bottom = $('
').html(result.pagination_bottom); - - if ($('
').html(pagination).find('#pagination').length) { - $('#pagination').show().replaceWith(pagination.find('#pagination')); + const controller = new AbortController(); + const signal = controller.signal; + let url = window.baseDir + 'modules/blocklayered/blocklayered-ajax.php' + '?' + new URLSearchParams(data).toString() + paramsPlus; + fetch(url, { + method: 'GET', + signal: signal + }).then(response => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }).then(result => { + if (typeof result === 'undefined') { + console.warn('reloadContent: AJAX response is undefined.'); + return; + } + if (result.meta_description !== '') { + const metaDescriptionTag = document.querySelector('meta[name="description"]'); + if (metaDescriptionTag) { + metaDescriptionTag.setAttribute('content', result.meta_description); + } + } + if (result.meta_keywords !== '') { + const metaKeywordsTag = document.querySelector('meta[name="keywords"]'); + if (metaKeywordsTag) { + metaKeywordsTag.setAttribute('content', result.meta_keywords); + } + } + if (result.meta_title !== '') { + document.title = result.meta_title; + } + if (result.heading !== '') { + document.querySelector(page_heading_selector).innerHTML = result.heading; + } + document.getElementById('layered_block_left').outerHTML = utf8_decode(result.filtersBlock); + document.querySelector(product_count_selector).outerHTML = result.categoryCount; + if (result.productList) { + document.querySelector(product_list_selector).outerHTML = utf8_decode(result.productList); } else { - $('#pagination').hide(); + document.querySelector(product_list_selector).innerHTML = ''; } - - if ($('
').html(pagination_bottom).find('#pagination_bottom').length) { - $('#pagination_bottom').show().replaceWith(pagination_bottom.find('#pagination_bottom')); + $form.classList.remove('opacity-50'); + $categoryProducts.classList.remove('opacity-50'); + if (result.pagination) { + const pagination = document.getElementById('pagination'); + if (pagination) { + pagination.outerHTML = result.pagination; + } + } + if (result.pagination_bottom) { + const pagination_bottom = document.getElementById('pagination_bottom'); + if (pagination_bottom) { + pagination_bottom.outerHTML = result.pagination_bottom; + } + } + window.filters = result.filters; + initFilters(); + initNoUiSlders(); + window.current_friendly_url = result.current_friendly_url; + if (typeof window.current_friendly_url === 'undefined') { + window.current_friendly_url = '#'; + } + ['price', 'weight'].forEach(function (sliderType) { + const $sliderRangerMin = document.getElementById('layered_' + sliderType + '_range_min'); + const $sliderRangeMax = document.getElementById('layered_' + sliderType + '_range_max'); + if ($sliderRangerMin && $sliderRangeMax && typeof blocklayeredSliderName !== 'undefined') { + const minLimit = $sliderRangerMin.getAttribute('limitValue'); + const maxLimit = $sliderRangeMax.getAttribute('limitValue'); + if (parseFloat($sliderRangerMin.value) !== parseFloat(minLimit) || parseFloat($sliderRangeMax.value) !== parseFloat(maxLimit)) { + window.current_friendly_url += '/' + blocklayeredSliderName[sliderType] + '-' + $sliderRangerMin.value + '-' + $sliderRangeMax.value; + } + } + }); + paginationButton(); + if (history.pushState) { + history.pushState(null, '', window.current_friendly_url); } else { - $('#pagination_bottom').hide(); + window.location.hash = window.current_friendly_url; } - } else { - $('#pagination, #pagination_bottom').hide(); - } - - paginationButton(); - ajaxLoaderOn = 0; - $('#layered_form').removeClass('loading-overlay'); - $('#category-products').removeClass('loading-overlay'); - - // On submiting nb items form, relaod with the good nb of items - $('#pagination form').submit(function() { - val = $('#pagination #nb_item').val(); - $('#pagination #nb_item').children().each(function(it, option) { - if (option.value == val) { - $(option).attr('selected', true); - } else { - $(option).removeAttr('selected'); - } + window.lockLocationChecking = true; + if (slideUp) { + const scrollTarget = document.querySelector(product_list_selector); + scrollTarget.scrollIntoView({behavior: 'smooth', block: 'start', inline: 'center'}); + } + updateProductUrl(); + document.querySelectorAll('.hide-action').forEach(function(element) { + hideFilterValueAction(element); }); - // Reload products and pagination - reloadContent(); - return false; - }); - if (typeof(ajaxCart) != "undefined") { - ajaxCart.overrideButtonsInThePage(); - } - - if (typeof(reloadProductComparison) == 'function') { - reloadProductComparison(); - } - initSliders(); - - // Currente page url - if (typeof(current_friendly_url) == 'undefined') { - current_friendly_url = '#'; - } + }).catch(error => { + if (error.name === 'AbortError') { + console.warn('Fetch aborted:', error.message); + } else { + console.error('Fetch error caught:', error); + } + }); + fetchControllers.push(controller); +} - // Get all sliders value - $(['price', 'weight']).each(function(it, sliderType) { - if ($('#layered_'+sliderType+'_slider').length) { - // Check if slider is enable & if slider is used - if (typeof($('#layered_'+sliderType+'_slider').slider('values', 0)) != 'object') { - if ($('#layered_'+sliderType+'_slider').slider('values', 0) != $('#layered_'+sliderType+'_slider').slider('option', 'min') - || $('#layered_'+sliderType+'_slider').slider('values', 1) != $('#layered_'+sliderType+'_slider').slider('option', 'max')) { - current_friendly_url += '/'+sliderType+'-'+$('#layered_'+sliderType+'_slider').slider('values', 0)+'-'+$('#layered_'+sliderType+'_slider').slider('values', 1) +function cancelFilter() { + document.addEventListener('click', function(e) { + const clickedLink = e.target.closest('#enabled_filters a'); + if (clickedLink) { + const dataRel = clickedLink.dataset.rel; + if (dataRel.search(/_slider$/) > 0) { + const sliderElement = document.getElementById(dataRel); + const minInput = document.getElementById(dataRel.replace(/_slider$/, '_range_min')); + const maxInput = document.getElementById(dataRel.replace(/_slider$/, '_range_max')); + if (minInput && maxInput) { + minInput.value = minInput.getAttribute('limitValue'); + maxInput.value = maxInput.getAttribute('limitValue'); + if (sliderElement && sliderElement.noUiSlider) { + sliderElement.noUiSlider.set([minInput.value, maxInput.value]); + } + } + } else { + const targetElement = document.getElementById(dataRel); + if (targetElement && targetElement.tagName === 'OPTION') { + const parentSelect = targetElement.parentNode; + if (parentSelect && parentSelect.tagName === 'SELECT') { + parentSelect.value = null; + } + } else { + const elementById = document.getElementById(dataRel); + if (elementById) { + elementById.removeAttribute('checked'); + } + const elementsByClass = document.querySelectorAll('.' + dataRel); + if (elementsByClass) { + elementsByClass.forEach(function(el) { + el.removeAttribute('checked'); + }); + } + const hiddenInput = document.querySelectorAll('#layered_form input[type="hidden"][name="' + dataRel + '"]'); + if (hiddenInput) { + hiddenInput.forEach(function(inp) { + inp.remove(); + }); + } + } } - } - } else if ($('#layered_'+sliderType+'_range_min').length) { - if ($('#layered_'+sliderType+'_range_min').attr('limitValue') != $('#layered_'+sliderType+'_range_min').val() - || $('#layered_'+sliderType+'_range_max').attr('limitValue') != $('#layered_'+sliderType+'_range_max').val()) { - current_friendly_url += '/'+sliderType+'-'+$('#layered_'+sliderType+'_range_min').val()+'-'+$('#layered_'+sliderType+'_range_max').val(); - } + reloadContent(); } - }); - if (history.pushState) { - history.pushState(null, '', window.current_friendly_url); - } else { - window.location.hash = window.current_friendly_url; - } - lockLocationChecking = true; - - if (slideUp) { - $.scrollTo('.product_list', 400); - } - updateProductUrl(); + }); +} - $('.hide-action').each(function() { - hideFilterValueAction(this); - }); - } - }); - ajaxQueries.push(ajaxQuery); +function stopFetchQuery() { + fetchControllers.forEach(controller => { + controller.abort(); + }); + fetchControllers = []; } function initLocationChange(func, time) { - if (!time) { - time = 500; - } - - var current_friendly_url = getUrlParams(); - setInterval(function() { - if (getUrlParams() != current_friendly_url && !lockLocationChecking) { - // Don't reload page if current_friendly_url and real url match - if (current_friendly_url.replace(/^#(\/)?/, '') == getUrlParams().replace(/^#(\/)?/, '')) { - return; - } - - lockLocationChecking = true; - reloadContent('&selected_filters='+getUrlParams().replace(/^#/, '')); - } else { - lockLocationChecking = false; - current_friendly_url = getUrlParams(); + if (!time) { + time = 500; } - }, time); + let currentFriendlyUrl = getUrlParams(); + setInterval(function () { + if (getUrlParams() != currentFriendlyUrl && !window.lockLocationChecking) { + if (currentFriendlyUrl.replace(/^#(\/)?/, '') === getUrlParams().replace(/^#(\/)?/, '')) { + return; + } + window.lockLocationChecking = true; + reloadContent('&selected_filters=' + getUrlParams().replace(/^#/, '')); + } else { + window.lockLocationChecking = false; + currentFriendlyUrl = getUrlParams(); + } + }, time); } function getUrlParams() { - var params = current_friendly_url; - if (window.location.href.split('#').length == 2 && window.location.href.split('#')[1] != '') { - params = '#'+window.location.href.split('#')[1]; - } - - return params; + if (typeof window.current_friendly_url === 'undefined') { + window.current_friendly_url = '#'; + } + let params = current_friendly_url; + if (window.location.href.split('#').length == 2 && window.location.href.split('#')[1] != '') { + params = '#' + window.location.href.split('#')[1]; + } + return params; } function updateProductUrl() { - // Adding the filters to URL product - if (param_product_url && param_product_url !== '#') { - $.each($('ul.product_list li.ajax_block_product .product_img_link,'+ - 'ul.product_list li.ajax_block_product h3 a,'+ - 'ul.product_list li.ajax_block_product .product_desc a,'+ - 'ul.product_list li.ajax_block_product .lnk_view'), function() { - $(this).attr('href', $(this).attr('href')+param_product_url); - }); - } + if (typeof param_product_url !== 'undefined' && param_product_url !== '' && param_product_url !== '#') { + document.querySelectorAll(update_product_url_selector).forEach(function(link) { + link.setAttribute('href', link.getAttribute('href') + param_product_url); + }); + } } /** * Copy of the php function utf8_decode() */ -function utf8_decode (utfstr) { - var res = ''; - for (var i = 0; i < utfstr.length;) { - var c = utfstr.charCodeAt(i); +function utf8_decode(utfstr) { + let res = ''; + for (let i = 0; i < utfstr.length;) { + let c = utfstr.charCodeAt(i); + let c1; + let c2; + if (c < 128) { + res += String.fromCharCode(c); + i++; + } else if ((c > 191) && (c < 224)) { + c1 = utfstr.charCodeAt(i + 1); + res += String.fromCharCode(((c & 31) << 6) | (c1 & 63)); + i += 2; + } else { + c1 = utfstr.charCodeAt(i + 1); + c2 = utfstr.charCodeAt(i + 2); + res += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63)); + i += 3; + } + } + return res; +} + +function initFilters() { + if (typeof filters !== 'undefined') { + for (let key in filters) { + let filter; + if (filters.hasOwnProperty(key)) { + filter = filters[key]; + } + if (typeof filter.slider !== 'undefined') { + const minElement = document.getElementById('layered_' + filter.type + '_range_min'); + if (minElement) { + minElement.setAttribute('limitValue', filter.min); + } + const maxElement = document.getElementById('layered_' + filter.type + '_range_max'); + if (maxElement) { + maxElement.setAttribute('limitValue', filter.max); + } + } + document.querySelectorAll('.layered_' + filter.type).forEach(element => { + element.style.display = ''; + }); + } + } +} - if (c < 128) { - res += String.fromCharCode(c); - i++; - } else if ((c > 191) && (c < 224)) { - var c1 = utfstr.charCodeAt(i+1); - res += String.fromCharCode(((c & 31) << 6) | (c1 & 63)); - i += 2; - } else { - var c1 = utfstr.charCodeAt(i+1); - var c2 = utfstr.charCodeAt(i+2); - res += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63)); - i += 3; +function initLayered() { + initFilters(); + initNoUiSlders(); + initLocationChange(); + updateProductUrl(); + if (window.location.href.split('#').length === 2 && window.location.href.split('#')[1] !== '') { + const params = window.location.href.split('#')[1]; + reloadContent('&selected_filters=' + params); } - } - return res; +} + +(function () { + function initBlockLayered() { + if (typeof $ === 'undefined') { + setTimeout(initBlockLayered, 100); + return; + } + $(function () { + cancelFilter(); + openCloseFilter(); + document.addEventListener('click', function(e) { + const target = e.target.closest('#layered_form input.color-option[type="button"], #layered_form label.layered_color'); + if (target) { + const nameAttr = target.getAttribute('name'); + const dataRelValue = target.dataset.rel; + const hiddenInputs = document.querySelectorAll('input[type="hidden"][name="' + nameAttr + '"]'); + if (!hiddenInputs.length) { + const newHiddenInput = document.createElement('input'); + newHiddenInput.type = 'hidden'; + newHiddenInput.name = nameAttr; + newHiddenInput.value = dataRelValue; + document.getElementById('layered_form').appendChild(newHiddenInput); + } else { + hiddenInputs.forEach(function(hInp){ + hInp.remove(); + }); + } + reloadContent(); + } + }); + document.addEventListener('click', function(e) { + const target = e.target.closest('#layered_form input[type="checkbox"]'); + if (target) { + reloadContent(); + } + }); + document.addEventListener('change', function(e) { + const target = e.target.closest('#layered_form select, #layered_form input[type=radio]'); + if (target) { + reloadContent(); + } + }); + const handleRangeInputKeyup = function(e) { + const target = e.target; + if (target.matches('#layered_form input.layered_input_range')) { + if (target.dataset.timeoutId) { + window.clearTimeout(parseInt(target.dataset.timeoutId, 10)); + } + const timeoutId = window.setTimeout(() => { + const filter = target.id.replace(/^layered_(.+)_range_.*$/, '$1'); + const filterRangeMin = document.getElementById('layered_' + filter + '_range_min'); + const filterRangeMax = document.getElementById('layered_' + filter + '_range_max'); + if (filterRangeMin && filterRangeMax) { + let valueMin = parseFloat(filterRangeMin.value) || 0; + filterRangeMin.value = valueMin; + let valueMax = parseFloat(filterRangeMax.value) || 0; + filterRangeMax.value = valueMax; + if (valueMax < valueMin) { + filterRangeMin.value = valueMax; + filterRangeMax.value = valueMin; + } + reloadContent(); + } + }, 500); + target.dataset.timeoutId = timeoutId.toString(); + } + }; + document.removeEventListener('keyup', handleRangeInputKeyup); + document.addEventListener('keyup', handleRangeInputKeyup); + document.addEventListener('click', function(e) { + const clickedRadio = e.target.closest('#layered_block_left .radio'); + if (clickedRadio) { + const colorButtons = clickedRadio.parentElement.querySelectorAll('input.color-option[type="button"]'); + if (colorButtons) { + const radioName = clickedRadio.getAttribute('name'); + colorButtons.forEach(function(item) { + if (item.classList.contains('on') && item.getAttribute('name') !== radioName) { + item.click(); + } + }); + return true; + } + } + }); + document.addEventListener('click', function(e) { + const clickedLink = e.target.closest('#layered_block_left label:not(.layered_color) a'); + if (clickedLink) { + const parentOfLabel = clickedLink.parentElement.parentElement; + const inputElement = parentOfLabel ? parentOfLabel.querySelector('input') : null; + if (inputElement) { + const isDisabled = inputElement.hasAttribute('disabled'); + if (!isDisabled) { + inputElement.click(); + } + } + } + }); + window.layered_hidden_list = {}; + document.addEventListener('click', function (e) { + const clickedElement = e.target.closest('.hide-action'); + if (clickedElement) { + const parentUl = clickedElement.parentNode; + if (parentUl && parentUl.tagName === 'UL' && parentUl.id) { + const ulId = parentUl.id; + if (typeof (layered_hidden_list[ulId]) === 'undefined' || layered_hidden_list[ulId] === false) { + layered_hidden_list[ulId] = true; + } else { + layered_hidden_list[ulId] = false; + } + hideFilterValueAction(clickedElement); + } + } + }); + document.querySelectorAll('.hide-action').forEach(function (element) { + hideFilterValueAction(element); + }); + const handleProductSortChange = function(e) { + if (e.target.matches('.selectProductSort')) { + const newValue = e.target.value; + const allSelectSortElements = document.querySelectorAll('.selectProductSort'); + allSelectSortElements.forEach(selectElement => { + selectElement.value = newValue; + }); + if (document.getElementById('layered_form')) { + reloadContent('forceSlide'); + } + } + }; + document.removeEventListener('change', handleProductSortChange); + document.addEventListener('change', handleProductSortChange); + const handleProductNumberChange = function(e) { + if (e.target.matches('select[name="n"]')) { + const newValue = e.target.value; + const allSelectSortElements = document.querySelectorAll('select[name="n"]'); + allSelectSortElements.forEach(selectElement => { + selectElement.value = newValue; + }); + if (document.getElementById('layered_form')) { + reloadContent('forceSlide'); + } + } + }; + document.removeEventListener('change', handleProductNumberChange); + document.addEventListener('change', handleProductNumberChange); + paginationButton(); + initLayered(); + }); + } + initBlockLayered(); +}()); + +function initNoUiSlders() { + const rangeSliderWidgets = document.querySelectorAll('[data-range-slider]'); + if (rangeSliderWidgets.length === 0) return; + const htmlElement = document.documentElement; + const direction = htmlElement.getAttribute('dir') === 'ltr' || htmlElement.getAttribute('dir') === 'rtl' ? htmlElement.getAttribute('dir') : 'ltr'; + rangeSliderWidgets.forEach(rangeSliderWidget => { + const rangeSlider = rangeSliderWidget.querySelector('.range-slider-ui'); + const valueMinInput = rangeSliderWidget.querySelector('[data-range-slider-min]'); + const valueMaxInput = rangeSliderWidget.querySelector('[data-range-slider-max]'); + const currentSliderId = rangeSliderWidget.id; + const filterMatch = currentSliderId.match(/^layered_(.+)_noUiSlider$/); + let filterType = null; + if (filterMatch && filterMatch[1]) { + filterType = filterMatch[1]; + } + let dataOptions = {}; + if (rangeSliderWidget.dataset.rangeSlider !== undefined) { + try { + dataOptions = JSON.parse(rangeSliderWidget.dataset.rangeSlider); + } catch (e) { + console.error('Error parsing data-range-slider JSON:', e); + return; + } + } + const options = { + startMin: parseFloat(dataOptions.startMin), + startMax: parseFloat(dataOptions.startMax), + min: parseFloat(dataOptions.min), + max: parseFloat(dataOptions.max), + step: parseFloat(dataOptions.step), + pips: dataOptions.pips, + tooltipPrefix: dataOptions.tooltipPrefix || '', + tooltipSuffix: dataOptions.tooltipSuffix || '', + precision: parseInt(dataOptions.precision) || 0 + }; + const start = options.startMax ? [options.startMin, options.startMax] : [options.startMin]; + const connect = options.startMax ? true : 'lower'; + if (rangeSlider && rangeSlider.noUiSlider) { + rangeSlider.noUiSlider.destroy(); + } + noUiSlider.create(rangeSlider, { + direction: direction, + start: start, + connect: connect, + step: options.step, + pips: options.pips ? { + mode: 'count', + values: 5 + } : false, + tooltips: true, + range: { + min: options.min, + max: options.max + }, + format: { + to: function (value) { + return options.tooltipPrefix + parseFloat(value).toFixed(options.precision) + options.tooltipSuffix; + }, + from: function (value) { + return Number(parseFloat(value).toFixed(options.precision)); + } + } + }); + rangeSlider.noUiSlider.on('update', (values, handle) => { + stopFetchQuery(); + let value = values[handle]; + if (typeof value === 'string') { + value = parseFloat(value.replace(/[^\d.-]/g, '')); + } + const formattedValue = value.toFixed(options.precision); + if (handle === 1) { + if (valueMaxInput) { + valueMaxInput.value = formattedValue; + } + } else { + if (valueMinInput) { + valueMinInput.value = formattedValue; + } + } + const rangeDisplayElement = document.getElementById(`layered_${filterType}_noUiRange`); + if (rangeDisplayElement) { + const currentFromValue = parseFloat(values[0].replace(/[^\d.-]/g, '')).toFixed(options.precision); + const currentToValue = parseFloat(values[1].replace(/[^\d.-]/g, '')).toFixed(options.precision); + const displayFormat = parseInt(rangeSliderWidget.dataset.format, 10); + const displayUnit = rangeSliderWidget.dataset.unit; + let fromDisplay, toDisplay, spanDisplay; + if (displayFormat < 5) { + fromDisplay = window.formatCurrency(parseFloat(currentFromValue), displayFormat, displayUnit); + toDisplay = window.formatCurrency(parseFloat(currentToValue), displayFormat, displayUnit); + } else { + fromDisplay = currentFromValue + displayUnit; + toDisplay = currentToValue + displayUnit; + } + if (window.isRtl) { + spanDisplay = toDisplay + ' - ' + fromDisplay; + } else { + spanDisplay = fromDisplay + ' - ' + toDisplay; + } + rangeDisplayElement.innerHTML = spanDisplay; + } + }); + rangeSlider.noUiSlider.on('change', () => { + reloadContent(); + }); + if (valueMinInput) { + valueMinInput.addEventListener('change', function () { + rangeSlider.noUiSlider.set([this.value, null]); + reloadContent(); + }); + } + if (valueMaxInput) { + valueMaxInput.addEventListener('change', function () { + rangeSlider.noUiSlider.set([null, this.value]); + reloadContent(); + }); + } + }); } diff --git a/blocklayered.php b/blocklayered.php index df320e1..39ce0d3 100644 --- a/blocklayered.php +++ b/blocklayered.php @@ -3630,12 +3630,10 @@ public function hookHeader($params) $smarty->assign('meta_keywords', rtrim($category_title.', '.$keywords.', '.$category_metas['meta_keywords'], ', ')); } + $this->context->controller->addJS($this->_path.'assets/vendor/nouislider/nouislider.min.js'); + $this->context->controller->addCSS($this->_path.'assets/vendor/nouislider/nouislider.min.css', 'all'); $this->context->controller->addJS(($this->_path).'blocklayered.js'); - $this->context->controller->addJS(_PS_JS_DIR_.'jquery/jquery-ui-1.8.10.custom.min.js'); - $this->context->controller->addJQueryUI('ui.slider'); - $this->context->controller->addCSS(_PS_CSS_DIR_.'jquery-ui-1.8.10.custom.css'); $this->context->controller->addCSS(($this->_path).'blocklayered.css', 'all'); - $this->context->controller->addJQueryPlugin('scrollTo'); $filters = $this->getSelectedFilters(); @@ -4133,7 +4131,7 @@ public function ajaxCall() $category = new Category(Tools::getValue('id_category_layered', Configuration::get('PS_HOME_CATEGORY')), (int) $cookie->id_lang); // Generate meta title and meta description - $category_title = $category->name; + $category_title = (empty($category->meta_title) ? $category->name : $category->meta_title); $category_metas = Meta::getMetaTags((int) $cookie->id_lang, 'category'); $title = ''; $keywords = ''; diff --git a/blocklayered.tpl b/blocklayered.tpl index 585dc98..f98e039 100644 --- a/blocklayered.tpl +++ b/blocklayered.tpl @@ -1,251 +1,320 @@ -{** - * Copyright (C) 2017-2024 thirty bees - * Copyright (C) 2007-2016 PrestaShop SA - * - * thirty bees is an extension to the PrestaShop software by PrestaShop SA. - * - * NOTICE OF LICENSE - * - * This source file is subject to the Academic Free License (AFL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/afl-3.0.php - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@thirtybees.com so we can send you a copy immediately. - * - * @author thirty bees - * @author PrestaShop SA - * @copyright 2017-2024 thirty bees - * @copyright 2007-2016 PrestaShop SA - * @license Academic Free License (AFL 3.0) - * PrestaShop is an internationally registered trademark of PrestaShop SA. - *} - {if $nbr_filterBlocks != 0} -
-

{l s='Product filter' mod='blocklayered'}

-
-
-
- {if isset($selected_filters) && $n_filters > 0} -
- {l s='Enabled filters:' mod='blocklayered'} -
    - {foreach from=$selected_filters key=filter_type item=filter_values} - {foreach from=$filter_values key=filter_key item=filter_value name=f_values} - {foreach from=$filters item=filter} - {if $filter.type == $filter_type && isset($filter.values)} - {if isset($filter.slider)} - {if $smarty.foreach.f_values.first} -
  • - x - {if $filter.format == 1} - {l s='%1$s: %2$s - %3$s'|sprintf:$filter.name:{displayPrice price=$filter.values[0]}:{displayPrice price=$filter.values[1]}|escape:html:'UTF-8' mod='blocklayered'} - {else} - {l s='%1$s: %2$s %4$s - %3$s %4$s'|sprintf:$filter.name:$filter.values[0]:$filter.values[1]:$filter.unit|escape:html:'UTF-8' mod='blocklayered'} - {/if} -
  • - {/if} - {else} - {foreach from=$filter.values key=id_value item=value} - {if $id_value == $filter_key && !is_numeric($filter_value) && ($filter.type eq 'id_attribute_group' || $filter.type eq 'id_feature') || $id_value == $filter_value && $filter.type neq 'id_attribute_group' && $filter.type neq 'id_feature'} -
  • - x - {l s='%1$s: %2$s' mod='blocklayered' sprintf=[$filter.name, $value.name]} -
  • - {/if} - {/foreach} - {/if} - {/if} - {/foreach} - {/foreach} - {/foreach} -
-
- {/if} - {foreach from=$filters item=filter} - {if isset($filter.values)} - {if isset($filter.slider)} - - - {foreach from=$filters item=filter} - {if $filter.type_lite == 'id_attribute_group' && isset($filter.is_color_group) && $filter.is_color_group && $filter.filter_type != 2} - {foreach from=$filter.values key=id_value item=value} - {if isset($value.checked)} - - {/if} - {/foreach} - {/if} - {/foreach} - -
- +

{l s='Catalog' mod='blocklayered'}

+
+
+ {if isset($selected_filters) && $n_filters > 0} +
+

{l s='Enabled filters:' mod='blocklayered'}

+
    + {foreach from=$selected_filters key=filter_type item=filter_values} + {foreach from=$filter_values key=filter_key item=filter_value name=f_values} + {foreach from=$filters item=filter} + {if $filter.type == $filter_type && isset($filter.values)} + {if isset($filter.slider)} + {if $smarty.foreach.f_values.first} +
  • + {if $filter.format == 1} + {l s='%1$s: %2$s - %3$s'|sprintf:$filter.name:{displayPrice price=$filter.values[0]}:{displayPrice price=$filter.values[1]}|escape:'html':'UTF-8' mod='blocklayered'} + {else} + {l s='%1$s: %2$s %4$s - %3$s %4$s'|sprintf:$filter.name:$filter.values[0]:$filter.values[1]:$filter.unit|escape:'html':'UTF-8' mod='blocklayered'} + {/if} + +
  • + {/if} + {else} + {foreach from=$filter.values key=id_value item=value} + {if $id_value == $filter_key && !is_numeric($filter_value) && ($filter.type eq 'id_attribute_group' || $filter.type eq 'id_feature') || $id_value == $filter_value && $filter.type neq 'id_attribute_group' && $filter.type neq 'id_feature'} +
  • + {l s='%1$s: %2$s' mod='blocklayered' sprintf=[$filter.name, $value.name]} + +
  • + {/if} + {/foreach} + {/if} + {/if} + {/foreach} + {/foreach} + {/foreach} +
+
+ {/if} + {foreach from=$filters item=filter} + {if isset($filter.values)} + + {/if} + {/foreach} + + {foreach from=$filters item=filter} + {if $filter.type_lite == 'id_attribute_group' && isset($filter.is_color_group) && $filter.is_color_group && $filter.filter_type != 2} + {foreach from=$filter.values key=id_value item=value} + {if isset($value.checked)} + + {/if} + {/foreach} + {/if} + {/foreach} +
+
{else}
-
-
- -
-
-
-


{l s='Loading...' mod='blocklayered'}

-
+
+
+ +
+
{/if} +{if $nbr_filterBlocks != 0} + {strip} + {if version_compare($smarty.const._PS_VERSION_,'1.5','>')} + {addJsDef param_product_url='#'|cat:$param_product_url} + {else} + {addJsDef param_product_url=''} + {/if} + {addJsDef blocklayeredSliderName=$blocklayeredSliderName} + + {if isset($filters) && $filters|@count} + {addJsDef filters=$filters} + {/if} + {/strip} +{/if} From 24f085cef06528b60decb4ad0f4499fe13e93e9e Mon Sep 17 00:00:00 2001 From: metacreo Date: Wed, 27 Aug 2025 13:19:04 +0300 Subject: [PATCH 2/2] Update blocklayered-footer.js --- blocklayered-footer.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/blocklayered-footer.js b/blocklayered-footer.js index e6b735b..4ce8fd2 100644 --- a/blocklayered-footer.js +++ b/blocklayered-footer.js @@ -22,10 +22,11 @@ * PrestaShop is an internationally registered trademark of PrestaShop SA. */ -$(document).ready(function() -{ - $('#selectProductSort').unbind('change').bind('change', function() - { - reloadContent(); - }) +document.addEventListener('DOMContentLoaded', () => { + const selectElement = document.getElementById('selectProductSort'); + if (selectElement) { + selectElement.addEventListener('change', () => { + reloadContent(); + }); + } });