1jQuery(document).ready(function ($) { 2 3 /** 4 ** Var collection 5 */ 6 7 const $body = $('body') 8 const $filterElements = $('.filter-bar, .filter-bar li, .filter-bar form, .filter-bar button', '#products') 9 const buttons = '.filter-bar button' 10 const labels = '.filter-bar-container-sub label:not([for="search"])' 11 const filterBtn = '.btn-filter button' 12 const btnOpenFilter = '.open-filter' 13 14 /** 15 ** Anim collection 16 */ 17 const anim = { 18 ripple: (el, event, scale = 40, input = false) => { 19 // Ripple effect on click 20 // Note : $el[0] = this 21 22 const $el = typeof el[0] === 'undefined' ? $(el) : el 23 const xPos = input ? 5 : event.pageX - $el.offset().left 24 const yPos = input ? 5 : event.pageY - $el.offset().top 25 const elWavesRipple = document.createElement('span') 26 27 elWavesRipple.className = 'waves-ripple' 28 elWavesRipple.style.left = xPos + 'px' 29 elWavesRipple.style.top = yPos + 'px' 30 31 $el[0].appendChild(elWavesRipple) 32 33 anime({ 34 targets: $el.find('.waves-ripple')[0], 35 duration: 600, 36 scale: [0, scale], 37 opacity: [0.2, 0], 38 easing: 'easeOutSine', 39 complete: function () { 40 $el.find('.waves-ripple').remove() 41 }, 42 }) 43 }, 44 list: (el) => { 45 if (el.next()[0] === undefined) return 46 47 anime({ 48 targets: el.next()[0].querySelectorAll('label'), 49 duration: 150, 50 translateY: [5, 0], 51 opacity: [0, 1], 52 easing: 'easeOutSine', 53 delay: anime.stagger(50, { start: 150 }), 54 }) 55 }, 56 openMenu: () => { 57 let tl = anime.timeline(filter.options) 58 let selectors = window.innerWidth > 992 ? '.filter-bar-container > ul > li, .filter-bar-container > ul > fieldset > li, form, button' : '.filter-bar-container ul li, form, button' 59 60 tl.add({ 61 targets: filter.selector, 62 translateX: [0, '-100%'], 63 }).add( 64 { 65 targets: filter.selector.querySelectorAll(selectors), 66 translateX: ['10px', 0], 67 opacity: [0, 1], 68 delay: anime.stagger(25), 69 }, 70 '-=500' 71 ) 72 }, 73 closeMenu: () => { 74 let tl = anime.timeline(filter.options) 75 76 tl.add({ 77 targets: filter.selector.querySelectorAll('li, form, button'), 78 translateX: [0, '10px'], 79 opacity: [1, 0], 80 delay: anime.stagger(25), 81 }).add( 82 { 83 targets: filter.selector, 84 translateX: ['-100%', 0], 85 }, 86 '300' 87 ) 88 }, 89 resetSVG: () => { 90 anime({ 91 targets: '.reset-filter svg', 92 rotate: [0, '360deg'], 93 duration: 800, 94 easing: 'easeOutExpo', 95 }) 96 }, 97 } 98 99 /** 100 ** Filter UI + URL 101 */ 102 const filter = { 103 selector: document.querySelector('.filter-bar'), 104 isOpen: false, 105 isClose: !this.isOpen, 106 cssClass: { 107 openMobileMenu: 'js-filter-menu-open', 108 toggleSubmenu: 'js-filter-submenu-open', 109 }, 110 options: { 111 //options globales AnimeJS 112 easing: 'easeOutExpo', 113 duration: 750, 114 }, 115 url: { 116 //filter.url => GET/SET l'url 117 118 searchParams: new URLSearchParams(window.location.search), //vanilla JS 119 objectURL: {}, //jQuery param => va contenir tous les slugs de chaque catégorie sous forme d'objet (j'ai pas fait de tableau à plusieurs dimension pour les sous(-sous)-cat parce que je suis inté :p) 120 string: '', 121 remove: (mainCategory, category) => { 122 123 //Garde tous les paramètres en cours 124 let currentCategories = filter.url.searchParams.getAll(mainCategory) 125 126 //Vide les paramètres pour la catégorie sélectionnée (pas le choix que de faire ça, il n'existe pas de fn du style "deleteOne='ma-categorie'") 127 filter.url.searchParams.delete(mainCategory) 128 129 //Remets toutes les catégories cochées dans le tableau sauf celle qui vient d'être décochée 130 currentCategories.forEach((previousCategory) => (previousCategory !== category ? filter.url.searchParams.append(mainCategory, previousCategory) : null)) 131 },
132 handle: (mainCategory, category, status) => { 133 134 // Supprime la recherche sur les filtres en dessous du current 135 let filters_list = []; 136 $( $('.filter-bar-container-sub .filter-name') ).each(function( index ) { 137 //if($( this ).attr('data-category') != undefined) { 138 if(filters_list.indexOf($( this ).attr('data-category')) == -1) 139 filters_list.push($( this ).attr('data-category')); 140 //} 141 }); 142 let current_index_filter = filters_list.indexOf(mainCategory); 143 for(var i = current_index_filter+1; i<filters_list.length; i++) { 144 145 if(mainCategory!= 'countries') { 146 filter.url.searchParams.delete(filters_list[i]) 147 delete filter.url.objectURL[filters_list[i]] 148 } 149 } 150 151 152 status ? filter.url.searchParams.append(mainCategory, category) : filter.url.remove(mainCategory, category) 153 154 //Gère l'objet objectURL 155 filter.url.objectURL[mainCategory] = filter.url.searchParams.getAll(mainCategory) 156 157 // Supprime les types parents si sous type checké 158 if(mainCategory == "type-de-produits") { 159 var types_datas = filter.url.objectURL["type-de-produits"]; 160 $(types_datas).each(function( index ) { 161 var sous_type = $('.filter-name[data-category="type-de-produits"][data-value="'+types_datas[index]+'"]'); 162 var parent = sous_type.data('parent') 163 if(parent) { 164 var allchilds = $('label[for="cd"] input[type="checkbox"]:checked').filter(function() { 165 return $(this).siblings('.filter-name[data-category="type-de-produits"]').data('parent') === parent; 166 }); 167 if(status || allchilds.length>0) { 168 let indexOf = filter.url.objectURL['type-de-produits'].indexOf(parent.toString()) 169 if(indexOf!=-1) filter.url.objectURL['type-de-produits'].splice(indexOf , 1); 170 } 171 } 172 }); 173 } 174 if (!filter.url.objectURL[mainCategory].length) 175 delete filter.url.objectURL[mainCategory] 176 filter.url.string = $.param(filter.url.objectURL) 177 }, 178 }, 179 toggleStatus: () => { 180 filter.isClose = !filter.isClose 181 filter.isOpen = !filter.isOpen 182 }, 183 openMenu: () => { 184 $body.addClass(filter.cssClass.openMobileMenu) 185 186 anim.openMenu() 187 188 filter.toggleStatus() 189 }, 190 closeMenu: () => { 191 $body.removeClass(filter.cssClass.openMobileMenu) 192 193 anim.closeMenu() 194 195 filter.toggleStatus() 196 }, 197 toggleMenu: () => { 198 filter.isOpen ? filter.closeMenu() : filter.openMenu() 199 }, 200 resetMenu: () => { 201 //reset *VISUEL* du menu, pas de data ici 202 $body.removeClass(filter.cssClass.openMobileMenu) 203 204 $filterElements.removeAttr('style') 205 206 filter.isOpen = false 207 }, 208 toggleSubmenu: (el, event) => { 209 //plie/déplie les "panneaux" de filtres 210 let $button = $(el) 211 212 if (event) anim.ripple($button, event) 213 214 if (!$button.hasClass(filter.cssClass.toggleSubmenu)) anim.list($button) 215 216 //Slide haut/bas le sous-menu 217 $button.toggleClass(filter.cssClass.toggleSubmenu).next().slideToggle(400) 218 }, 219 openAllSubmenus: () => { 220 //ouvre tous les sous-menu (comme son nom l'indique) 221 $(buttons).each(function () { 222 let $button = $(this) 223 224 //Reset CSS style de tous les labels & affiche les via le CSS => opacity :1 225 if (!$button.hasClass(filter.cssClass.toggleSubmenu)) $button.next().find('label').removeAttr('style').css({ opacity: 1 }) 226 227 $button.addClass(filter.cssClass.toggleSubmenu) 228 229 $button.next().show() 230 }) 231 }, 232 closeAllSubmenus: () => { 233 //ouvre tous les sous-menu (comme son nom l'indique) 234 $(buttons).each(function () { 235 let $button = $(this) 236 237 //Reset CSS style de tous les labels & affiche les via le CSS => opacity :1 238 if (!$button.hasClass(filter.cssClass.toggleSubmenu)) $button.next().find('label').removeAttr('style').css({ opacity: 0 }) 239 240 $button.removeClass(filter.cssClass.toggleSubmenu) 241 242 $button.next().hide() 243 }) 244 }, 245 submenuAutoToggle: (el) => { 246 //Ferme et supprime les sous-menu/sous-filtres ainsi que les slugs correspondants 247 let $label = $(el) 248 let input = $label.find('input[type="checkbox"]')[0] ? $label.find('input[type="checkbox"]')[0] : $label.find('input[type="radio"]')[0] 249 let $subMenu = $label.nextAll('ul') 250 251 filter.toggleSubmenu(el) 252 253 //Uncheck les input des sous-menus 254 if (!input.checked) { 255 $subMenu.find('label:not(.label-all)').each(function () { 256 let $subMenuInput = $(this).find('input')[0] 257 if ($subMenuInput.checked) filter.handle(this) //Gère (supprime dans ce cas-ci) les slugs 258 }) 259 } 260 }, 261 handle: (el, event, ripple = true) => { 262 //Toggle les inputs & choppe les slug de la catégorie cliquées et de la catégorie parente pour en faire une URL via filter.url.handle() 263 let $label = $(el) 264 let $parentUl = $label.parents('ul') 265 let $subMenu = $label.nextAll('ul') 266 //let mainCategory = $parentUl.prev('button').attr('data-value') 267 let mainCategory = $label.find('.filter-name').attr('data-category') 268 let category = $label.find('.filter-name').attr('data-value') 269 let input = $label.find('input[type="checkbox"]')[0] ? $label.find('input[type="checkbox"]')[0] : $label.find('input[type="radio"]')[0] 270 let is_radio = $(input).attr('type') == 'radio'?true:false; 271 let is_sstype = $(input).hasClass('sstype'); 272 // if(is_sstype) mainCategory = 'sous-types'; 273 274 //Anim le click si l'interface event est passée et qu'on est bien dans une checkbox 275 if (event && ripple && input) anim.ripple($label, event, 7, true) 276 277 //toggle le check de l'input en JS... obligé car sinon l'event "click" est trigger 2 fois car mon <input> est dans mon <label> 278 if (input) input.checked = input.checked === true && input.disabled === false ? false : true 279
280 //Ferme les sous-menu si il existe 281 if ($subMenu.length) filter.submenuAutoToggle(el) 282 if(is_radio) filter.url.searchParams.delete(mainCategory) 283 284 //Modifie l'url en conséquence 285 if (input) filter.url.handle(mainCategory, category, input.checked) 286 287 288 filter.handleCatInputsToggle(el, mainCategory) 289 }, 290 handleCatInputsToggle: (el, mainCategory) => { 291 292 let $label = $(el) 293 294 if ($label.find('input')[0].type === 'radio') return 295 if ($label.parents('.radio-container').length) return 296 297 let $parentUl = $label.parents('ul') 298 if($parentUl.find('> li > .label-all input').length){ 299 var $inputAll = $parentUl.find('> li > .label-all input') 300 var $catInputs = $parentUl.find('> li > label:not(.label-all) input') 301 var countOfCheckedInputs = $parentUl.find('> li > label:not(.label-all) input:checked').length 302 }else{ 303 var $inputAll = $parentUl.find('.label-all input') 304 var $catInputs = $parentUl.find('label:not(.label-all) input') 305 var countOfCheckedInputs = $parentUl.find('label:not(.label-all) input:checked').length 306 } 307 308 let isInputAll = $(el).hasClass('label-all') 309 let input = $label.find('input[type="checkbox"]')[0] 310 311 //Si c'est l'input "TOUT" && qu'il n'est PAS checké, check l'input "TOUT" 312 if (isInputAll && input.checked === false){ 313 $inputAll.prop('checked', true) 314 filter.url.searchParams.delete(mainCategory) 315 filter.url.objectURL[mainCategory] = {} 316 } 317 318 //Si c'est l'input "TOUT" && qu'il est checké, décheck tous les inputs 319 if (isInputAll && input.checked === true) { 320 console.log('all') 321 if(mainCategory == "type-de-produits"){ 322 filter.url.searchParams.delete(mainCategory) 323 filter.url.objectURL[mainCategory] = {} 324 $catInputs.prop('checked', false) 325 filter.handleSubCatInputsToggle(el) 326 filter.url.handle(mainCategory, null, false) 327 } 328 else if($catInputs.length >1 && $inputAll.length >1) { 329 $catInputs.each((catInput)=>{ 330 if(catInput>0) $($catInputs.get(catInput)).prop('checked', false) 331 }) 332 filter.url.searchParams.delete(mainCategory) 333 filter.url.objectURL[mainCategory] = {} 334 filter.url.handle(mainCategory, null, false) 335 //} 336 } 337 else { 338 $catInputs.prop('checked', false) 339 filter.handleSubCatInputsToggle(el) 340 filter.url.searchParams.delete(mainCategory) 341 filter.url.objectURL[mainCategory] = {} 342 filter.url.handle(mainCategory, null, false) 343 } 344 } 345 346 //Si c'est PAS l'input "TOUT" && qu'il est checké, décheck l'input "TOUT" 347 if (!isInputAll && (input && input.checked === true)){ 348 if(mainCategory=="type-de-produits"){ 349 if($label.find('.filter-name').data('parent')){ 350 let indexOf = filter.url.objectURL[mainCategory].indexOf($label.find('.filter-name').data('parent').toString()) 351 if(indexOf!=-1) filter.url.objectURL[mainCategory].splice(indexOf , 1); 352 } 353 } 354 if($inputAll.length>1){ 355 if($($inputAll.get(1)).prop('checked') == true){ 356 $($inputAll.get(1)).prop('checked', false) 357 } 358 }else{ 359 if($inputAll.prop('checked') == true){ 360 $inputAll.prop('checked', false) 361 } 362 } 363 } 364 365 //Si c'est PAS l'input "TOUT" && que les autres inputs ne sont PAS checké, check l'input "TOUT" 366 if (!isInputAll && !countOfCheckedInputs) { 367 if(mainCategory=="type-de-produits"){ 368 $inputAll.prop('checked', true); 369 filter.url.objectURL[mainCategory] = [] 370 filter.url.searchParams.delete(mainCategory) 371 filter.url.objectURL[mainCategory].push($inputAll.next().next().attr('data-value')); 372 }else if($inputAll.length>1){ 373 $($inputAll[1]).prop('checked', true); 374 }else{ 375 $inputAll.prop('checked', true); 376 } 377 } 378 } 379 , 380 handleSubCatInputsToggle: (el) => { 381 let $openedSubSubMenus = $(el).parents('ul').find(`.${filter.cssClass.toggleSubmenu}`) 382 383 $openedSubSubMenus.each(function () { 384 filter.toggleSubmenu(this) 385 }) 386 } 387 , 388 resetFilters: () => { 389 location.reload(); 390 anim.resetSVG() 391 392 $(labels).each(function () { 393 let input = $(this).find('input')[0] 394 let isInputAll = $(this).hasClass('label-all') 395 396 input.checked = isInputAll ? true : false 397 398 filter.url.objectURL = {} 399 filter.url.searchParams = new URLSearchParams() 400 }) 401 }, 402 } 403 404// window.innerWidth > 992 ? filter.resetMenu() : filter.openAllSubmenus() 405 406 /*window.addEventListener( 407 'resize', 408 _.debounce(() => (window.innerWidth > 992 ? filter.resetMenu() : filter.openAllSubmenus()), 600) 409 )*/ 410 411 /** 412 ** Open/close filter menu 413 */ 414 $(document).on('click', buttons, function (event) { 415 $(this).hasClass('close') ? filter.closeMenu() : ($(this).hasClass('reset-filter') ? filter.resetFilters() : ($(this).hasClass('reset-filter-map') ? window.location.href.split("?")[0] :filter.toggleSubmenu(this, event))) 416 }) 417 418 /** 419 ** Handle label UI behaviour + pass to "handleParamsURL" 420 */ 421 $(document).on('click', labels, function (event) { 422 event.stopPropagation() 423 event.preventDefault() 424 425 filter.handle(this, event) 426 }) 427 428 /** 429 ** Ripple anim on button click 430 */ 431 $(document).on('click', filterBtn, function (event) { 432 anim.ripple(this, event, 8) 433 }) 434 435 /** 436 ** open filter menu on button click 437 */ 438 $(document).on( 439 'click', 440 btnOpenFilter, 441 _.throttle(() => filter.openMenu(), 500, { leading: true }) 442 ) 443 444 /*if (dev)*/ return (window.coccifilter = { filter }) 445})
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.