1/** 2 * 3 * Librairie de gestion des moteurs bridge 4 * 5 * 6 */ 7 8window.bridgeMoteur = function(moteurId) { 9 this.eventsHandlers = { 10 beforeItemChange: [], 11 afterItemChange: [], 12 beforeCalcMoteur: [], 13 afterCalcMoteur: [], 14 beforeSubmit: [], 15 afterSubmit: [] 16 }; 17 18 this.options = { 19 submitOnChange: true, 20 lang: 'fr', 21 bridgeAjaxUrl: '', 22 bridgeAjaxListUrl: '', 23 // Libellés traduisibles, alimentés par init-moteur.php côté CMS. 24 labels: { 25 removeFilter: 'Retirer le filtre' 26 } 27 }; 28 29 this.onSubmitFunction = null; 30 this.id = moteurId; 31 this.currentURL = ''; 32 this.activeFilters = []; 33 this.activeFiltersLabels = []; 34 this.activeItems = []; 35 this.urlParams = []; 36 this.filters = [] ; 37 38 // élément HTML <datalist> qui contiendra les options du filtre citycombodyn 39 this.datalist = null; 40 // liste des options du filtre 41 this.cityComboDynList = null; 42 43 // paramètres des tris 44 this.activeSorts = []; 45 this.sortDirections = []; 46 47 // pour différencier une modification des filtres et un simple tri 48 this.filterHasChanged = false; 49 50 51 this.addEventHandler = function(handlerName, func) 52 { 53 if (Object.prototype.hasOwnProperty.call(this.eventsHandlers, handlerName) ) { 54 this.eventsHandlers[handlerName].push(func); 55 } 56 }; 57 58 59 this.addFilter = function(obj) { 60 this.filters.push(obj); 61 }; 62 63 64 this.findFilterProp = function(key, prop) { 65 var tabIds = key.split('-'); 66 67 if (tabIds.length < 2) { 68 return ''; 69 } 70 71 var temp = this.getFilter(tabIds[1]); 72 73 if (temp != false && temp[prop] != undefined) { 74 return temp[prop]; 75 } else { 76 return ''; 77 } 78 }; 79 80 81 this.getFilter = function(filterId) { 82 var trouve = false; 83 84 for (var i = 0 ; i < this.filters.length && !trouve ; i++) { 85 if (this.filters[i].id == filterId) { 86 return this.filters[i] ; 87 trouve = true; 88 } 89 } 90 return trouve; 91 } 92 93 94 this.deactivateWholeFilter = function(filterId) { 95 var trouve = false; 96 97 this.filterHasChanged = true; 98 99 for (var i = 0 ; i < this.filters.length && !trouve ; i++) { 100 if (this.filters[i].id == filterId) { 101 this.filters[i].active = false; 102 this.filters[i].value = ''; 103 this.filters[i].dateFrom = ''; 104 this.filters[i].dateTo = ''; 105 this.filters[i].hourFrom = ''; 106 this.filters[i].hourTo = ''; 107 this.filters[i].lat = ''; 108 this.filters[i].lon = ''; 109 // FIX TG 26 sept 2023 : On n'enlève pas la distance pour pas casser les filtres, on lui remet juste sa valeur par défaut 110 // this.filters[i].dist = ''; 111 this.filters[i].dist = '5000'; 112 if(this.filters[i].city) { 113 this.filters[i].city = ''; 114 } 115 116 for (var j = 0 ; j < this.filters[i].items.length && !trouve ; j++) { 117 this.filters[i].items[j].active = false; 118 this.filters[i].items[j].min = this.filters[i].items[j].baseMin; 119 this.filters[i].items[j].max = this.filters[i].items[j].baseMax; 120 } 121 // ND 28/06/21 : effacer également les inputs text visibles 122 jQuery("input[data-bridge-filter-id='" + this.filters[i].id + "'][data-bridge-filter-field='" + this.filters[i].field + "']").val('').change(); 123 trouve = true; 124 } 125 } 126 return trouve; 127 } 128 129 130 this.setFilterProp = function (filterId, prop, value, triggerChange) { 131 var trouve = false; 132 133 for (var i = 0 ; i < this.filters.length && !trouve ; i++) { 134 if (this.filters[i].id == filterId) { 135 this.filters[i][prop] = value; 136 for (var j = 0 ; j < this.filters[i].items.length && !trouve ; j++) { 137 this.filters[i].items[j].active = false;
138 } 139 trouve = true; 140 } 141 } 142 143 if (trouve) { 144 this.calcMoteur(triggerChange); 145 } 146 return trouve; 147 } 148 149 150 this.addItem = function(filterId, obj) { 151 var trouve = false; 152 for (var i = 0 ; i < this.filters.length && !trouve ; i++) { 153 if (this.filters[i].id == filterId) { 154 this.filters[i].items.push(obj); 155 trouve = true; 156 } 157 } 158 }; 159 160 161 this.setItem = function(filterId, itemId, obj) { 162 var trouve = false; 163 var x = 0; 164 var self = this; 165 166 for (var i = 0 ; i < this.filters.length && !trouve ; i++) { 167 if (this.filters[i].id == filterId) { 168 for (var j = 0 ; j < this.filters[i].items.length && !trouve ; j++) { 169 if (this.filters[i].items[j].id == itemId) { 170 for (x=0; x<self.eventsHandlers.beforeFilterChange.length; x++) 171 self.eventsHandlers.beforeFilterChange[x](this.filters[i].id, this.filters[i]); 172 for (x=0; x<self.eventsHandlers.beforeItemChange.length; x++) 173 self.eventsHandlers.beforeItemChange[x](this.filters[i].id, this.filters[i].items[j].id, obj, this.filters[i].items[j]); 174 175 this.filters[i].items[j] = obj; 176 for (x=0; x<self.eventsHandlers.afterItemChange.length; x++) 177 self.eventsHandlers.afterItemChange[x](this.filters[i].id, this.filters[i].items[j].id, obj); 178 179 trouve = true; 180 } 181 } 182 } 183 } 184 185 if (trouve) 186 this.calcMoteur(true); 187 return trouve; 188 } 189 190 191 this.findItemProp = function(key, prop) { 192 var tabIds = key.split('-'); 193 if (tabIds.length < 3) 194 return ''; 195 var temp = this.getItem(tabIds[1], tabIds[2]); 196 return temp[prop]; 197 }; 198 199 200 this.getItem = function(filterId, itemId) { 201 var trouve = false; 202 for (var i = 0 ; i < this.filters.length && !trouve ; i++) { 203 if (this.filters[i].id == filterId) { 204 for (var j = 0 ; j < this.filters[i].items.length && !trouve ; j++) { 205 if (this.filters[i].items[j].id == itemId) { 206 return this.filters[i].items[j] ; 207 trouve = true; 208 } 209 } 210 } 211 } 212 return trouve; 213 } 214 215 216 this.setItemProp = function(filterId, itemId, prop, value) { 217 var trouve = false; 218 var x = 0; 219 var self = this; 220 221 for (var i = 0 ; i < this.filters.length && !trouve ; i++) { 222 if (this.filters[i].id == filterId) { 223 for (var j = 0 ; j < this.filters[i].items.length && !trouve ; j++) { 224 if (this.filters[i].items[j].id == itemId) { 225 for (x=0; x<self.eventsHandlers.beforeItemChange.length; x++) 226 self.eventsHandlers.beforeItemChange[x](this.filters[i].id, this.filters[i].items[j].id, obj, this.filters[i].items[j]); 227 228 this.filters[i].items[j][prop] = value; 229 trouve = true; 230 231 for (x=0; x<self.eventsHandlers.afterItemChange.length; x++) 232 self.eventsHandlers.afterItemChange[x](this.filters[i].id, this.filters[i].items[j].id, obj); 233 } 234 } 235 } 236 } 237 // Patch 20/11/2019 : on ne fait pas de calc pour éviter les appels en cascade, c'est aux inputs de lancer leur calc 238 /* 239 if(trouve) { 240 this.calcMoteur(true); 241 } 242 */ 243 244 return trouve; 245 } 246 247 /** 248 * Appelée par js-tris.php à l'activation, la modification ou la désactivation d'un tri dans l'interface 249 * Transmet les informations sur les tris pour l'appel AJAX (this.searchAjax) de mise à jour de la liste 250 * @param {Array} activeSorts : tris activés 251 * @param {Array} sortDirections : directions choisies pour les tris activés 252 */ 253 this.setSortsAndSortDirections = function(activeSorts, sortDirections) { 254 this.activeSorts = activeSorts; 255 this.sortDirections = sortDirections; 256 } 257 258 /** 259 * Appelée par js-select-dropdown.php au chargement ou au rechargement d'une webliste contenant un filtre de type cityComboDyn 260 * (chargement initial de la page ou actualisation après application d'un filtre) 261 * Affiche la liste des communes des fiches de la webliste dans un combo box 262 * @param {String} cityComboDynList : liste des communes à afficher comme options pour le filtre
263 * @param {Object} datalist : élément HTML <datalist> qui contiendra les options à afficher 264 */ 265 this.displayCityComboDynList = function(cityComboDynList, datalist) { 266 267 if (cityComboDynList == '') { 268 cityComboDynList = 'Aucune ville à afficher'; 269 } 270 271 this.datalist = datalist; 272 273 // on efface la liste qui existait déjà 274 while (datalist.children.length > 0) { 275 datalist.removeChild(datalist.lastChild); 276 } 277 278 let cityList = []; 279 280 this.cityComboDynList = cityComboDynList; 281 282 cityList = cityComboDynList.split(','); 283 284 for (let city of cityList) { 285 let option = document.createElement("option"); 286 option.text = city; 287 option.value = city; 288 option.id = "opt_<?= $moteurEnDouble ?><?= $moteur->id ?>-<?= $filter->id ?>-" + city; 289 290 datalist.appendChild(option); 291 } 292 } 293 294 /** 295 * Remplace certains caractères spéciaux par les entités HTML correspondantes pour éviter les problèmes avec les saisies utilisateur 296 * @param {String} text : string à modifier 297 * @returns String : string modifiée 298 */ 299 this.escapeHtml = function(text) { 300 var map = { 301 '&': '&', 302 '<': '<', 303 '>': '>', 304 '"': '"', 305 "'": ''', 306 "`": '`', 307 "+": '+' 308 }; 309 310 return text.replace(/[&<>"'`+]/g, function(m) {return map[m];}); 311 } 312 313 /** 314 * Texte lisible d'une étiquette de filtre actif : intitulé du filtre et valeurs 315 * retenues, débarrassés des balises (un item peut porter une <img>). Sert de nom 316 * accessible au bouton qui retire ce filtre. 317 * @param {Object} activeFilterLabel : une entrée de this.activeFiltersLabels 318 * @returns String 319 */ 320 this.activeFilterText = function(activeFilterLabel) { 321 var parts = []; 322 if (activeFilterLabel.label != undefined && activeFilterLabel.label !== '') { 323 parts.push(activeFilterLabel.label); 324 } 325 if (activeFilterLabel.items && activeFilterLabel.items.length) { 326 parts.push(activeFilterLabel.items.join(', ')); 327 } 328 return parts.join(' : ').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim(); 329 } 330 331 this.calcMoteur = function(isChanged) { 332 333 var self = this; 334 var tmpActive; 335 var activeFilters = []; 336 var activeFiltersLabels = []; 337 var activeItems = []; 338 var urlParams = {}; 339 var tmpFilter; 340 var i; 341 var j; 342 var x; 343 var cmpt; 344 var label; 345 var image; 346 var tmpObj; 347 var tmpItems = []; 348 var tempStr = ''; 349 var section = ''; 350 var activeItemIdx = 0; 351 352 // on garde juste les filter_lat, filter_lon et filter_rayon s'ils existent déjà 353 if (this.urlParams.filter_lat) { 354 urlParams.filter_lat = this.urlParams.filter_lat; 355 } 356 357 if (this.urlParams.filter_lon) { 358 urlParams.filter_lon = this.urlParams.filter_lon; 359 } 360 361 if (this.urlParams.filter_rayon) { 362 urlParams.filter_rayon = this.urlParams.filter_rayon; 363 } 364 365 var tmpActiveFilters = this.activeFilters; 366 var tmpActiveItems = this.activeItems; 367 368 for (x=0; x<self.eventsHandlers.beforeCalcMoteur.length; x++) { 369 self.eventsHandlers.beforeCalcMoteur[x](this); 370 } 371 372 for (i = 0 ; i < this.filters.length ; i++) { 373 tmpActive = false; 374 375 switch (this.filters[i].displayType) { 376 case 'slidedoubleminmax': 377 if(this.filters[i].items.length == 0) { 378 console.error('ERREUR DE PARAMETRAGE BRIDGE : un element slide double min/max n\'a pas d\'item renseigné : filtre n° ' + this.filters[i].id); 379 } else { 380 if (this.filters[i].items[0].min != this.filters[i].items[0].baseMin || this.filters[i].items[0].max != this.filters[i].items[0].baseMax) { 381 this.filters[i].active = true; 382 activeFilters.push(this.id + '-' + this.filters[i].id ); 383 activeItems.push(this.id + '-' + this.filters[i].id + '-' + this.filters[i].items[0].id );
384 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_min'] = this.filters[i].items[0].min; 385 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_max'] = this.filters[i].items[0].max; 386 } else { 387 // Important : effacer le statut actif 388 this.filters[i].active = false; 389 } 390 } 391 break; 392 case 'checkbox': 393 case 'boutonbox': 394 case 'boutonboxdyn': 395 case 'checkboxdyn': 396 case 'citycombo': 397 case 'combo': 398 case 'combodyn': 399 case 'listemulti': 400 case 'onoff': 401 case 'option': 402 case 'optionbutton': 403 case 'optiondyb': 404 case 'slidedouble': 405 case 'slidesimple': 406 case 'slidesimpledyn': 407 isActive = false; 408 activeItemIdx = 0; 409 for (j = 0; j < this.filters[i].items.length ; j++) { 410 if (this.filters[i].items[j].active) { 411 isActive = true; 412 activeItemIdx = j; 413 activeItems.push(this.id + '-' + this.filters[i].id + '-' + this.filters[i].items[j].id); 414 // Patch 18/04/23 : tenir compte des min max enregistrés dans les filtres 415 // Patch 17/02/2024 : on essaie de déplacer pour pouvoir cumuler un critère avec valeur prédéfinie avec une autre mais ça pourra avoir des effets de bords 416 // Mais de toute façon il y en avait déjà lorsqu'on mélange les chèvres et les choux dans un même filtre 417 if (this.filters[i].items[activeItemIdx].baseMin !== undefined && this.filters[i].items[activeItemIdx].baseMin !== '' && this.filters[i].items[activeItemIdx].baseMin !== null) { 418 if ( 419 !urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_min'] || 420 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_min'] > this.filters[i].items[activeItemIdx].baseMin 421 ) { 422 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_min'] = this.filters[i].items[activeItemIdx].baseMin; 423 } 424 } 425 if (this.filters[i].items[activeItemIdx].baseMax !== undefined && this.filters[i].items[activeItemIdx].baseMax !== '' && this.filters[i].items[activeItemIdx].baseMax !== null) { 426 if ( 427 !urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_max'] || 428 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_max'] < this.filters[i].items[activeItemIdx].baseMax 429 ) { 430 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_max'] = this.filters[i].items[activeItemIdx].baseMax; 431 } 432 } 433 } 434 } 435 this.filters[i].active = isActive 436 if (isActive) { 437 activeFilters.push(this.id + '-' + this.filters[i].id); 438 } 439 break; 440 441 442 case 'calendar': 443 case 'dateRangePicker': 444 case 'calendarhours': 445 case 'prochainesperiodes': 446 case 'disponibilites': 447 isActive = false; 448 if ((this.filters[i].dateFrom && this.filters[i].dateFrom !='') || (this.filters[i].dateTo && this.filters[i].dateTo !='')) { 449 isActive = true; 450 if (this.filters[i].dateFrom)
451 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_dateFrom'] = this.filters[i].dateFrom; 452 if (this.filters[i].dateTo) 453 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_dateTo'] = this.filters[i].dateTo; 454 if (this.filters[i].hourFrom) 455 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_hourFrom'] = this.filters[i].hourFrom; 456 if (this.filters[i].hourTo) 457 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_hourTo'] = this.filters[i].hourTo; 458 459 } 460 461 this.filters[i].active = isActive 462 if (isActive) 463 activeFilters.push(this.id + '-' + this.filters[i].id); 464 465 break; 466 case 'cityprox': 467 case 'cityproxdyn': 468 case 'gps': 469 isActive = false; 470 if ((this.filters[i].lat && this.filters[i].lat !='') && (this.filters[i].lon && this.filters[i].lon !='') && (this.filters[i].dist && this.filters[i].dist !='')) { 471 isActive = true; 472 if(this.filters[i].lat) 473 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_lat'] = this.filters[i].lat; 474 if(this.filters[i].lon) 475 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_lon'] = this.filters[i].lon; 476 if(this.filters[i].dist) 477 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_dist'] = this.filters[i].dist; 478 if(this.filters[i].city) 479 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_city'] = encodeURI(this.filters[i].city); 480 } 481 482 this.filters[i].active = isActive 483 if (isActive) 484 activeFilters.push(this.id + '-' + this.filters[i].id); 485 486 break; 487 case 'citycombodyn': 488 case 'cityinput': 489 case 'input': 490 case 'hidden': 491 isActive = false; 492 493 if ((this.filters[i].value && this.filters[i].value !== '')) { 494 isActive = true; 495 if (this.filters[i].value) { 496 // Patch ND 13/01/22 : les accents et espaces ne sont pas encodés 497 urlParams['brflt_' + this.id + '-' + this.filters[i].id + '_value'] = encodeURI(this.filters[i].value); 498 } 499 } 500 501 this.filters[i].active = isActive 502 if (isActive) 503 activeFilters.push(this.id + '-' + this.filters[i].id); 504 break; 505 } 506 } 507 508 this.activeFilters = activeFilters; 509 this.activeItems = activeItems; 510 511 var activeFiltersComp = this.activeFilters; 512 var activeItemsComp = this.activeItems; 513 514 if (JSON.stringify(tmpActiveFilters) !== JSON.stringify(activeFiltersComp) || JSON.stringify(tmpActiveItems) !== JSON.stringify(activeItemsComp)) { 515 this.filterHasChanged = true; 516 } 517 518 this.urlParams = urlParams; 519 520 for (i = 0 ; i < activeFilters.length ; i++) { 521 tmpFilter = activeFilters[i]; 522 label = this.findFilterProp(activeFilters[i], 'label'); 523 section = this.findFilterProp(activeFilters[i], 'sectionId'); 524 tmpObj = { 525 sectionId: section, 526 key: tmpFilter, 527 label: label, 528 items:[] 529 }; 530 531 // Pour les items prédéfinis, on va chercher leur label 532 for (cmpt = 0; cmpt < activeItems.length; cmpt++) { 533 if (activeItems[cmpt].indexOf(tmpFilter) == 0) { 534 label = this.findItemProp(activeItems[cmpt], 'label'); 535 image = this.findItemProp(activeItems[cmpt], 'image'); 536 if (image != undefined && image !== '') { 537 label = '<img src="' + image + '" class="bridge-engine-image"/>' + label; 538 } 539 tmpObj.items.push(label); 540 541 // Cas particulier du slidedoubleminmax qui a des items mais sans valeurs prédéfinies : on veut afficher les valeurs et le label de l'item et non ceux du filtre lui-même 542 let typeCtrl = this.findFilterProp(activeFilters[i],'displayType');
543 let slideDoubleMin = this.findItemProp(activeItems[cmpt], 'min'); 544 let slideDoubleMax = this.findItemProp(activeItems[cmpt], 'max'); 545 546 if (typeCtrl === 'slidedoubleminmax' && slideDoubleMin != null && slideDoubleMax != null) { 547 tmpObj.label = tmpObj.items[0]; 548 let slideDoubleMinMax = slideDoubleMin + '-' + slideDoubleMax; 549 tmpObj.items.splice(0, 1, slideDoubleMinMax); 550 } 551 } 552 } 553 554 // Pour les valeurs, on les met dans un item 555 var tmpValue = this.findFilterProp(activeFilters[i], 'value'); 556 if (tmpValue != '') { 557 tmpObj.items.push(tmpValue); 558 } 559 560 var tmpDateFrom = this.findFilterProp(activeFilters[i], 'dateFrom'); 561 var tmpDateTo = this.findFilterProp(activeFilters[i], 'dateTo'); 562 if (tmpDateFrom != '' && tmpDateTo != '') { 563 tmpObj.items.push(tmpDateFrom + '-' + tmpDateTo); 564 } else { 565 if (tmpDateFrom != '') { 566 tmpObj.items.push(tmpDateFrom); 567 } 568 if (tmpDateTo != '') { 569 tmpObj.items.push(tmpDateTo); 570 } 571 } 572 573 // Pour la distance, on la ville et la distance en km 574 var tmpCity = this.findFilterProp(activeFilters[i], 'city'); 575 var tmpDist = this.findFilterProp(activeFilters[i], 'dist'); 576 if (tmpCity != null && tmpCity != '' && tmpDist != null && tmpDist != '') { 577 tmpDist = (parseInt(tmpDist) / 1000) + ' km'; 578 tmpObj.items.push(tmpCity + ' : ' + tmpDist); 579 } 580 581 activeFiltersLabels.push(tmpObj); 582 } 583 584 this.activeFiltersLabels = activeFiltersLabels; 585 586 var tabAllActiveFiltersString = []; 587 var tabSectionsActiveFiltersString = {}; 588 589 for (i=0 ; i < activeFiltersLabels.length ; i++) { 590 tempStr = ''; 591 tempStr += '<div class="bridgeActiveFilter uk-label">'; 592 tempStr += '<span class="bridgeActiveFilterLabel">' 593 594 if ((activeFiltersLabels[i].label != undefined && activeFiltersLabels[i].label != '') || (activeFiltersLabels[i].image != undefined && activeFiltersLabels[i].image != '')) { 595 596 if (activeFiltersLabels[i].label != undefined && activeFiltersLabels[i].label != '') { 597 tempStr += activeFiltersLabels[i].label; 598 } 599 tempStr += ': '; 600 } 601 602 tempStr += activeFiltersLabels[i].items.join(', '); 603 tempStr += '</span>'; 604 // La croix était un <span> : ni focusable, ni actionnable au clavier, et sans 605 // intitulé (RGAA 7.3 / WCAG 2.1.1, 4.1.2). C'est désormais un <button> nommé 606 // « Retirer le filtre » suivi du libellé du filtre, pour distinguer les étiquettes 607 // entre elles. Le libellé peut contenir du HTML (image d'item) : on le nettoie. 608 tempStr += '<button type="button" class="bridgeActiveFilterRemove far fa fa-times"'; 609 tempStr += ' aria-label="' + this.escapeHtml(((this.options.labels && this.options.labels.removeFilter) || 'Retirer le filtre') + ' ' + this.activeFilterText(activeFiltersLabels[i])) + '"'; 610 tempStr += ' data-filter-key="' + activeFiltersLabels[i].key + '" data-moteur-id="' + this.id + '">'; 611 tempStr += '</button>'; 612 tempStr += '</div>'; 613 614 if (i + 1 === activeFiltersLabels.length) { 615 // tempStr += '<a class="bridgeActiveFilterRemoveAll">Tout effacer</a>'; 616 } 617 tabAllActiveFiltersString.push(tempStr); 618 if(tabSectionsActiveFiltersString[activeFiltersLabels[i].sectionId] == undefined) { 619 tabSectionsActiveFiltersString[activeFiltersLabels[i].sectionId] = [] 620 } 621 622 tabSectionsActiveFiltersString[activeFiltersLabels[i].sectionId].push(tempStr) 623 } 624 625 if(activeFiltersLabels.length > 0) { 626 jQuery('.bridgeActiveFilterRemoveAll').css('display', 'inline'); 627 jQuery('.bridgeActiveFilterRemoveAll').show(); 628 } else { 629 jQuery('.bridgeActiveFilterRemoveAll').css('display', 'none'); 630 jQuery('.bridgeActiveFilterRemoveAll').hide(); 631 } 632 jQuery(".bridgeActiveFiltersDiv").html(tabAllActiveFiltersString.join(' ')); 633 634 635 for (var prop in tabSectionsActiveFiltersString) { 636 if (Object.prototype.hasOwnProperty.call(tabSectionsActiveFiltersString, prop) && tabSectionsActiveFiltersString[prop].length > 0) { 637 jQuery(".bridgeActiveFiltersDivSection" + prop).html(tabSectionsActiveFiltersString[prop].join(' ')); 638 } 639 } 640 jQuery("*[class^='bridgeActiveFiltersDivSection']").each(function(e) { 641 642 var sectionId = jQuery(this).attr('data-section-id'); 643 if (Object.prototype.hasOwnProperty.call(tabSectionsActiveFiltersString, sectionId) && tabSectionsActiveFiltersString[sectionId].length > 0) { 644 jQuery(this).show(500); 645 } else { 646 jQuery(this).hide(500); 647 } 648 }); 649 650 jQuery(".bridgeActiveFilterRemove").click(function(e) { 651 var key = jQuery(this).attr('data-filter-key'); 652 var tabTemp = key.split('-'); 653 654 if (tabTemp.length == 2 && window['bridgeMoteur' + tabTemp[0]] != undefined) { 655 window['bridgeMoteur' + tabTemp[0]].deactivateWholeFilter(tabTemp[1]); 656 window['bridgeMoteur' + tabTemp[0]].calcMoteur(true); 657 } 658 }); 659 660 661 // lien qui supprime tout les filtres 662 jQuery(".bridgeActiveFilterRemoveAll").click(function (e) { 663 var nummoteur = ''; 664 jQuery(".bridgeActiveFilterRemove").each(function() { 665 // Trop minimaliste 666 // jQuery(this).click(); 667 var key = jQuery(this).attr('data-filter-key'); 668 var tabTemp = key.split('-'); 669 nummoteur = tabTemp[0]; 670 671 if (tabTemp.length == 2 && window['bridgeMoteur' + tabTemp[0]] != undefined) { 672 window['bridgeMoteur' + tabTemp[0]].deactivateWholeFilter(tabTemp[1]); 673 } 674 }); 675 window['bridgeMoteur' + nummoteur].calcMoteur(true); 676 }) ; 677 /* WTF 678 e => { 679 for (const key of Object.keys(window)) { 680 if (!key.startsWith("bridgeMoteur")) { 681 continue; 682 } 683 684 if (typeof window[key] === "function") { 685 continue; 686 } 687 688 const filterIds = window[key].activeFilters.map(id => id.replace("6-", "")); 689 690 filterIds.forEach(id => window[key].deactivateWholeFilter(id)); 691 692 window[key].calcMoteur(true); 693 } 694 }); 695 */ 696 for (x=0; x<self.eventsHandlers.afterCalcMoteur.length; x++) 697 self.eventsHandlers.afterCalcMoteur[x](this); 698 699 if (isChanged && this.options.submitOnChange) { 700 var stateObj = JSON.parse(JSON.stringify(this.getUrlState())); 701 var url = "?" + this.getUrlParams(); 702 // 07/06/2025 : conserver également les paramètres d'URL supplémentaires 703 var attributesUrlParams = this.getAttributesUrlParams(true); 704 if(attributesUrlParams !== '') { 705 url += attributesUrlParams; 706 } 707 history.pushState(stateObj, jQuery(document).find("title").text(), url); 708 this.submit(); 709 } 710 else if (isChanged) { 711 // ND 27/08/2024 : j'ajoute cette instruction pour que lorsuq'on met un moteur seul 712 // En shortcode avec nb de résultats visibles mais pas de submit auto 713 // le nb de résultats bouge quand on coche une case
714 this.searchAjax(); 715 } 716 717 } 718 719 this.getUrlParams = function() { 720 var self = this ; 721 var urlParams = ''; 722 if(this.activeFilters !== null) { 723 urlParams += 'braf=' + this.activeFilters.join(','); 724 } 725 if(this.activeItems !== null) { 726 urlParams += '&brai=' + this.activeItems.join(','); 727 } 728 729 if (this.activeSorts.length > 0) { 730 urlParams += '&bras=' + this.activeSorts.join(','); 731 urlParams += '&brsd=' + this.sortDirections.join(','); 732 } 733 734 Object.keys(this.urlParams).forEach(function(key,index) { 735 urlParams += '&' + key + '=' + self.urlParams[key] ; 736 }); 737 738 return urlParams; 739 // location.hash = "parameter1=987¶meter2=zyx"; 740 } 741 742 // Construit une partie de chaine de paramètres d'URL poru le service Bridge getForDisplay 743 // A partir des attributs d'origine de la liste (filtres supplémentaires ou en override) 744 this.getAttributesUrlParams = function(onlyForWordpress) { 745 var self = this ; 746 var urlParams = ''; 747 var urlParamsForWordpress = ''; 748 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('item_template') && this.options.item_template !== '') { 749 urlParams += '&item_template=' + this.options.item_template; 750 } 751 752 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('productCodes') && this.options.productCodes !== '') { 753 urlParams += '&product_codes=' + this.options.productCodes; 754 } 755 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('filter_city') && this.options.filter_city !== '') { 756 urlParams += '&filter_city=' + this.options.filter_city; 757 } 758 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('filter_lat') && this.options.filter_lat !== '') { 759 urlParams += '&filter_lat=' + this.options.filter_lat; 760 } 761 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('filter_lon') && this.options.filter_lon !== '') { 762 urlParams += '&filter_lon=' + this.options.filter_lon; 763 } 764 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('filter_rayon') && this.options.filter_rayon !== '') { 765 urlParams += '&filter_rayon=' + this.options.filter_rayon; 766 } 767 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('selection_id') && this.options.selection_id !== '') { 768 urlParams += '&selection_id=' + this.options.selection_id; 769 urlParamsForWordpress += '&selection_id=' + this.options.selection_id; 770 } 771 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('filter_criterions') && this.options.filter_criterions !== '') { 772 urlParams += '&filter_criterions=' + escape(this.options.filter_criterions); 773 } 774 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('scoring') && this.options.scoring !== '') { 775 urlParams += '&scoring=' + this.options.scoring; 776 urlParamsForWordpress += '&scoring=' + this.options.scoring; 777 } 778 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('minscore') && this.options.minscore !== '') { 779 urlParams += '&minscore=' + this.options.minscore; 780 urlParamsForWordpress += '&minscore=' + this.options.minscore; 781 } 782 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('excluded_codes') && this.options.excluded_codes !== '') { 783 urlParams += '&excluded_codes=' + this.options.excluded_codes; 784 } 785 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('sort_one') && this.options.sort_one !== '') {
786 urlParams += '&sort_one=' + this.options.sort_one; 787 } 788 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('sort_two') && this.options.sort_two !== '') { 789 urlParams += '&sort_two=' + this.options.sort_two; 790 } 791 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('sort_three') && this.options.sort_three !== '') { 792 urlParams += '&sort_three=' + this.options.sort_three; 793 } 794 if(this.options !== null && typeof this.options === 'object' && this.options.hasOwnProperty('duplicate_by') && this.options.duplicate_by !== '') { 795 urlParams += '&duplicate_by=' + this.options.duplicate_by; 796 } 797 798 if(onlyForWordpress) { 799 return urlParamsForWordpress; 800 } else { 801 return urlParams; 802 } 803 804 // location.hash = "parameter1=987¶meter2=zyx"; 805 } 806 807 this.getUrlState = function() { 808 809 if(this.activeFilters === null) { 810 this.activeFilters = []; 811 } 812 var urlParams = { braf: this.activeFilters.join(','), brai: this.activeItems, bras: this.activeSorts, brsd: this.sortDirections }; 813 var self = this ; 814 var toto; 815 816 Object.keys(this.urlParams).forEach(function(key,index) { 817 urlParams[key] = self.urlParams[key]; 818 // toto = self.urlParams[key] ; 819 }); 820 821 822 return urlParams; 823 // location.hash = "parameter1=987¶meter2=zyx"; 824 } 825 826 this.submit = function() { 827 828 var self = this; 829 for (x=0; x<self.eventsHandlers.beforeSubmit.length; x++) 830 self.eventsHandlers.beforeSubmit[x](this); 831 832 var url = this.currentURL ; // + '?braf=' + this.activeFilters.join(','); 833 var urlParams = this.getUrlParams(); 834 835 if (this.onSubmitFunction != null) { 836 this.onSubmitFunction(url, urlParams); 837 } else { 838 this.searchAjax(); 839 } 840 //window.location = url + '?' + urlParams; 841 //this.searchAjax(); 842 // window.location = url + '?' + urlParams; 843 } 844 845 this.searchAjax = function() { 846 var context = this; 847 var urlParams = this.getUrlParams(); 848 var attributesUrlParams = this.getAttributesUrlParams(); 849 if (urlParams != '') 850 urlParams = '&' + urlParams; 851 var self = this; 852 var change = 1; 853 // si on change un filtre plutôt qu'un tri, on a besoin de recharger le filtre cityComboDyn 854 if (this.filterHasChanged) { 855 change = 1; 856 } 857 jQuery('.bridge-loader').fadeIn(100); 858 jQuery.ajax ({ 859 type: "GET", 860 dataType: "json", 861 beforeSend: function(xhr) { 862 xhr.setRequestHeader('X-GT-Translate-Keys', 'html->html'); 863 }, 864 headers: { 865 'X-GT-Translate-Keys': 'html->html' 866 }, 867 url: this.options.bridgeAjaxListUrl, 868 // data: 'action=brListe&id=' + this.options.webListId + '&product_codes=' + this.options.productCodes + '&first=1&max=' + this.options.limitPerPage + urlParams + '&change=' + change, 869 data: 'action=brListe&id=' + this.options.webListId + attributesUrlParams + '&first=1' + urlParams + '&change=' + change + '&lang=' + this.options.lang, 870 success: function (response) { 871 jQuery('.bridge-loader').fadeOut({ duration: 500 }); 872 if (true || response.html) { // response.total && response.total !== 0 873 var uniqueID = jQuery('.bridge-liste[data-moteur-id=' + self.id + ']').attr('data-bridge-identifier'); 874 875 // On désactive l'infinite scroll car on le remet de toute manière plus loin 876 if(window['bridgeInfiniteScroll_' + uniqueID] !== undefined) { 877 window['bridgeInfiniteScroll_' + uniqueID].unbind(); 878 } 879 880 if (response.moteur) { 881 const moteurId = response.moteur.id; 882 // pour chaque moteur.section 883 for (const section of response.moteur.sections) { 884 if (!section.subSections || section.subSections.length === 0) continue; 885 886 for (const subsection of section.subSections) { 887 if (!subsection.filters || subsection.filters.length === 0) continue; 888 889 for (const filter of subsection.filters) { 890 if (!filter.parameters || !filter.parameters.display_results) continue; 891 892 if (!filter.items || filter.items.length === 0) continue; 893 894 for (const item of filter.items) { 895 const countComposants = jQuery(document).find(`.bridge-count[data-item-id=${item.id}]`); 896 897 countComposants.each(function () { 898 const countComposant = jQuery(this); 899 countComposant.text(`(${item.count})`); 900 901 if (item.count === 0) { 902 if (filter.parameters.hasOwnProperty('disable_if_empty') && filter.parameters.disable_if_empty === true) { 903 jQuery(document).find(`input[data-item-id=${item.id}]`).each(function () { 904 // on disable pas si la case est coché, pour pouvoir la décocher 905 if (!jQuery(this).prop("checked")) { 906 jQuery(this).prop("disabled", true) 907 jQuery(this).addClass('bridge-disabled') 908 jQuery(this).closest('label').addClass('bridge-disabled') 909 } 910 }); 911 } 912 if (filter.parameters.hasOwnProperty('hide_if_empty') && filter.parameters.hide_if_empty === true) { 913 jQuery(document).find(`input[data-item-id=${item.id}]`).each(function () { 914 // on masque pas si la case est coché, pour pouvoir la décocher 915 if (!jQuery(this).prop("checked")) { 916 jQuery(this).addClass('bridge-hidden') 917 jQuery(this).closest('label').addClass('bridge-hidden') 918 } 919 }); 920 } 921 } else { 922 jQuery(document).find(`input[data-item-id=${item.id}]`).each(function () { 923 jQuery(this).prop("disabled", false).removeClass('bridge-disabled').removeClass('bridge-hidden'); 924 jQuery(this).closest('label').removeClass('bridge-disabled').removeClass('bridge-hidden'); 925 }); 926 } 927 }); 928 } 929 } 930 } 931 } 932 } 933 934 jQuery('*[data-moteur-id=' + self.id + ']').find(".listeSITCount").html(response.total); 935 if(response.total !== 0) { 936 var $items = jQuery(response.html); 937 jQuery('*[data-moteur-id=' + self.id + ']').find(".listeSIT").html($items); 938 939 $items.find('.zoom-on-hover').hover( function() { 940 var idfiche = jQuery(this).attr('data-dnc-produit'); 941 if(idfiche !== '') { 942 window.mapService.activatemarker(idfiche, true, true); // window.mapService.defaultIconOptions 943 window.mapService.centeronmarker(idfiche, "15"); 944 // tricherie obligatoire pour forcer le point à s'afficher la premiere fois 945 setTimeout(function() { 946 window.mapService.setZoom(15); 947 },500); 948 window.mapService.reDraw(); 949 950 } 951 }); 952 953 // setTimeout(function () { 954 $items.find('.zoom-on-click').click(function() { 955 var idfiche = jQuery(this).attr('data-dnc-produit'); 956 if(idfiche !== '') { 957 window.mapService.activatemarker(idfiche, true, true); // window.mapService.defaultIconOptions 958 window.mapService.centeronmarker(idfiche, "15"); 959 // tricherie obligatoire pour forcer le point à s'afficher la premiere fois 960 setTimeout(function() { 961 window.mapService.setZoom(15); 962 },500); 963 window.mapService.reDraw(); 964 } 965 }); 966 967 968 969 } else { 970 // aucun résultat 971 jQuery('.bridge-liste[data-moteur-id=' + self.id + ']').find(".listeSIT").html('<div>' + response.message + '</div>'); 972 } 973 window.bridgeTotalResults = response.total; 974 975 for (x=0; x<self.eventsHandlers.afterSubmit.length; x++) 976 self.eventsHandlers.afterSubmit[x](this); 977 978 var currentURL = window.location.href.split('?')[0] ; 979 var nbLinks = 1; 980 var start = 1; 981 var end = parseInt(response.lastPage); 982 var attributesUrlParams = self.getAttributesUrlParams(); 983 984 /* PATCH CLEMENT : 27/07/2023 - Bizarre qu'on ajoute ça... 985 if ( ( 1 + nbLinks ) < parseInt(response.lastPage) ) { 986 end = 1 + nbLinks 987 }*/ 988 989 // Ici on met à jour la pagination en repartant la page 1 990 var pagination = '<ul class="bridge-pagination uk-pagination uk-flex-center" uk-margin>'; 991 pagination +=' <li class="uk-disabled""><a class="bridge-pagination-link" data-page="1" href="#nogo"> <span uk-pagination-previous></span></a></li>' ; 992 var tmpdisable = ''; 993 for (var i = start ; i <= end ; i++) { 994 if(i === 1) { 995 pagination += ' <li class="uk-active"><a class="bridge-pagination-link" data-page="' + i + '" href="#nogo">' + i + '</a></li>'; 996 } else { 997 pagination += ' <li class=""><a class="bridge-pagination-link" data-page="' + i + '" href="' + currentURL + '?brpa=' + i + urlParam
997s + '">' + i + '</a></li>'; 998 } 999 } 1000 if ( end < parseInt(response.lastPage) ) { 1001 pagination += '<li class="uk-disabled nolink"><span>...</span></li>'; 1002 pagination += ' <li class=""><a class="bridge-pagination-link" data-page="' + response.lastPage + '" href="' + currentURL + '?brpa=' + response.lastPage + urlParams + '">' + response.lastPage + '</a></li>'; 1003 } 1004 1005 if ( parseInt(response.lastPage) > 1 ) { 1006 pagination += '<li class="">'; 1007 pagination += '<a class="bridge-pagination-link bridge-next" id="bridge_next_' + uniqueID + '" data-page="2" href="' + currentURL + ((currentURL.indexOf('?') > -1)?'&':'?') + 'brpa=2' + urlParams + '"><span uk-pagination-next></span> </a>'; 1008 pagination += '<a class="bridge-pagination-link bridge-next-ajax" id="bridge_next_ajax_' + uniqueID + '" data-page="2" href="' + self.options.bridgeAjaxListUrl + ((self.options.bridgeAjaxListUrl.indexOf('?') > -1)?'&':'?') + 'tt=1&action=brListeInfinite&id=' + self.options.webListId + attributesUrlParams + '&brpa=2' + urlParams + '" style="display:none;"><span uk-pagination-next></span> </a>'; 1009 pagination += '</li>'; 1010 } else { 1011 pagination += '<li class="uk-disabled">'; 1012 pagination += '<a class="bridge-pagination-link bridge-next" id="bridge_next_' + uniqueID + '" data-page="2" href="#nogo"><span uk-pagination-next></span> </a>'; 1013 // pagination += '<a class="bridge-pagination-link bridge-next-ajax" id="bridge_next_ajax_' + uniqueID + '" data-page="2" href="#nogo" style="display:none;"><span uk-pagination-next></span> </a>'; 1014 pagination += '</li>'; 1015 } 1016 1017 jQuery('#bridge-pagination-container-' + uniqueID).html(''); 1018 jQuery('#bridge-pagination-container-' + uniqueID).append(jQuery(pagination)); 1019 1020 // Patch 03/06/22 : On active l'infinite scroll uniquement s'il y a plus d'une page de résultats et qu'on a pas fini 1021 if(end > 1) { 1022 if (window['bridgeActivateInfiniteScroll_' + uniqueID] != undefined) { 1023 window['bridgeActivateInfiniteScroll_' + uniqueID](response.total); 1024 } 1025 } else { 1026 // En mode infinite scroll, comme il est désactivé quand il n'y a pas plus d'une page on doit masquer la pagination 1027 if (window['bridgeActivateInfiniteScroll_' + uniqueID] != undefined) { 1028 jQuery('.bridge-liste[data-moteur-id=' + self.id + ']').find('.bridge-pagination-container').hide(); 1029 } 1030 } 1031 /* 1032 // affichage des options du filtre citycombodyn, s'il est présent sur la liste 1033 if (context.datalist && response.cityComboDynList && response.cityComboDynList !== '0') { 1034 context.displayCityComboDynList(response.cityComboDynList, context.datalist); 1035 } else if (context.datalist && response.cityComboDynList && response.cityComboDynList === '0') { 1036 // s'il y en a un dans la liste mais qu'on ne reçoit rien du contrôleur, on réutilise le précédent (cas d'un tri sans modif du moteur) 1037 context.displayCityComboDynList(context.cityComboDynList, context.datalist); 1038 } 1039 */ 1040 } else if ((!response.total && (response.message === 'Une erreur est survenue')) ) { // || response.total === 0 1041 1042 jQuery('*[data-moteur-id=' + self.id + ']').find(".listeSITCount").html(response.total); 1043 jQuery('.bridge-liste[data-moteur-id=' + self.id + ']').find(".listeSIT").html('<div>' + response.message + '</div>'); 1044 1045 /*if (context.datalist) { 1046 context.displayCityComboDynList('', context.datalist); 1047 }*/ 1048 1049 } else { 1050 jQuery('.bridge-liste[data-moteur-id=' + self.id + ']').find(".listeSIT").html('<div>Une erreur est survenue.</div>'); 1051 1052 /*if (context.datalist) { 1053 context.displayCityComboDynList('', context.datalist); 1054 }*/ 1055 console.error('Erreur : ', response); 1056 } 1057 1058 // on réinitialise le booléen 1059 context.filterHasChanged = false;
1060 }, 1061 error: function(response) { 1062 jQuery('.bridge-loader').fadeOut({ duration: 500 }); 1063 jQuery('.bridge-liste[data-moteur-id=' + self.id + ']').find(".listeSIT").html('<div>Une erreur est survenue.</div>'); 1064 console.error('data error', response); 1065 } 1066 }).done(function( msg ) { 1067 jQuery('.bridge-loader').fadeOut({ duration: 500 }); 1068 // alert( "Data Saved: " + msg ); 1069 }); 1070 1071 } 1072 1073}
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.