1const ajaxUrl = params.ajaxUrl; 2let tri = 'new'; 3let search = ''; 4let codePostal = ''; 5let page; 6let firstLoad = true; //logique inversée pour éviter les listeners multiples et pour que ça reste clair 7let isRequestInProgress = false; 8let preUrl = '/localdomaliance' 9let path; 10if (window.location.pathname.includes(preUrl)) { 11 path = preUrl + '/temoignages/'; 12} else { 13 path = '/temoignages/'; 14 preUrl = ''; 15} 16 17document.addEventListener('DOMContentLoaded', function () { 18 if (window.location.pathname === path) { 19 paginationOverseer(); 20 //throttle(mutationObserver('pagination'), 300); 21 throttle(mutationObserver('label_addressInput'), 300); 22 loaderBehavior(); 23 if (firstLoad) { 24 reviewListener(); 25 firstLoad = false; 26 } 27 } 28 //url watcher 29 if (window.location.search.includes("ville")) { 30 let codePostal = new URLSearchParams(window.location.search).get('code_postal'); 31 filterReviews('', codePostal, 'new'); 32 } 33}); 34 35// Le toggle "Voir plus/Voir moins" doit fonctionner sur toutes les pages 36// affichant des blocs d'avis (avis_v2-slider), pas uniquement /temoignages/. 37// On attend que le DOM ET le CSS soient entièrement appliqués (load + fonts + layout) 38// plutôt qu'un délai arbitraire, pour que les mesures scrollHeight/clientHeight soient fiables. 39window.addEventListener('load', () => { 40 const runInit = () => { 41 requestAnimationFrame(() => requestAnimationFrame(seeMoreOrLessInit)); 42 }; 43 if (document.fonts && document.fonts.ready) { 44 document.fonts.ready.then(runInit); 45 } else { 46 runInit(); 47 } 48}); 49 50function seeMoreOrLessInit(){ 51 const buttons = document.querySelectorAll(".toggle-text"); 52 buttons.forEach(button => { 53 const targetId = button.getAttribute("data-target"); 54 const text = document.getElementById(targetId); 55 text.classList.add("truncate-text"); 56 57 setTimeout(() => { 58 const isTruncated = text.scrollHeight - text.clientHeight; 59 if (isTruncated > 5) { 60 button.classList.remove("hidden"); 61 } else { 62 text.classList.remove("truncate-text"); 63 } 64 }, 100); 65 66 // On cible `text` (résolu via data-target) et non un sélecteur de classe : c'est le 67 // même élément que celui qui reçoit truncate-text ci-dessus, l'id est unique et le 68 // contrat est déclaré par le bouton. ReviewsCardRenderer rend la classe 69 // `a_avis-content` (préfixée) â un querySelector('.avis-content') ne matche rien. 70 button.addEventListener("click", () => { 71 const isExpanded = text.classList.contains("expanded"); 72 if (!isExpanded) { 73 text.style.maxHeight = text.scrollHeight + "px"; 74 text.classList.add("expanded"); 75 } else { 76 text.style.maxHeight = text.scrollHeight + "px"; 77 void text.offsetHeight; 78 text.style.maxHeight = "4.5em"; 79 setTimeout(() => { 80 text.classList.remove("expanded"); 81 }, 300); 82 } 83 button.setAttribute("aria-expanded", !isExpanded); 84 button.textContent = !isExpanded ? "Voir moins" : "Voir plus"; 85 }); 86 }); 87} 88 89function seeMoreOrLess2(){ 90 const buttons = document.querySelectorAll(".toggle-text"); 91 buttons.forEach(button => { 92 const targetId = button.getAttribute("data-target"); 93 const text = document.getElementById(targetId); 94 if(text.classList.contains("truncate-text")){ 95 return; 96 } 97 text.classList.add("truncate-text"); 98 99 setTimeout(() => { 100 const isTruncated = text.scrollHeight - text.clientHeight; 101 if (isTruncated > 5) { 102 button.classList.remove("hidden"); 103 } else { 104 text.classList.remove("truncate-text"); 105 } 106 }, 100); 107 }); 108} 109 110function loaderBehavior() { 111 //let container = document.querySelector('#data-container'); 112 //let pagination = document.querySelector('#pagination'); 113 //let loader = document.querySelector('#loader'); 114 ////container.classList.toggle('hide'); 115 ////pagination.classList.toggle('hide'); 116 //loader.classList.add('hide'); 117} 118 119function reviewListener() { 120 selectListener('.a_form-select_tri', (value) => { 121 tri = value; 122 useValue(tri); 123 }); 124 selectListener('.a_form-select', (value) => { 125 search = value; 126 useValue(search); 127 }); 128 communeListener('.commune', (value) => { 129 codePostal = value; 130 useValue(codePostal); 131 }); 132 resetListener(() => { 133 filterReviews('', '', '',); 134 }); 135 136} 137 138function useValue(value) { 139 filterReviews(search, codePostal, tri); 140} 141 142function communeListener(node, callback) { 143 if (window.location.search.includes("ville")) { 144 return new URLSearchParams(window.location.search).get('code_postal'); 145 } else { 146 waitForElem(node).then((e) => { 147 let parent = e.parentElement; 148 let sibling = parent.previousElementSibling; 149 if (sibling.getAttribute('id') === 'addressInput_avis') { 150 e.addEventListener('click', function () { 151 callback(e.getAttribute('data-code_postal')); 152 }); 153 } 154 }); 155 document.querySelector('#addressInput_avis').addEventListener('click', function () { 156 callback(this.getAttribute('data-code_postal')); 157 }); 158 } 159} 160 161function selectListener(node, callback) { 162 return waitForElem(node).then((e) => { 163 e.addEventListener('change', throttle(function () { 164 callback(e.value); 165 })); 166 }); 167} 168 169function resetSlField() { 170 let slField = document.querySelector('#addressInput_avis'); 171 slField.value = ''; 172 slField.setAttribute('data-code_postal', ''); 173 slField.setAttribute('data-code_ville', ''); 174} 175 176function resetListener() { 177 let reset = document.querySelector('.btn_ag '); 178 reset.addEventListener('click', function () { 179 filterReviews('', '', 'new').then(r => { 180 resetSlField(); 181 }); 182 }); 183} 184 185/** 186 * Generates a request to the specified URL with the provided type, action, and data. 187 * 188 * @return {Promise} A Promise that resolves with the response text if successful, or rejects with an error message. 189 * @param search 190 * @param codePostal 191 * @param tri 192 */ 193function filterReviews(search, codePostal, tri) { 194 codePostal = codePostal ?? ''; 195 let data = { 196 search: search, 197 tri: tri, 198 codePostal: codePostal, 199 } 200 let loader = document.querySelector('#loader');
201 let container = document.querySelector('#data-container'); 202 let pagination = document.querySelector('#pagination'); 203 loaderBehavior(); 204 makeRequest(ajaxUrl, 'POST', 'reviewsAjaxHandler', data).then(r => { 205 try { 206 let result = JSON.parse(r); 207 if (result.success && result.data) { 208 result = result.data; 209 let container = document.querySelector('#data-container'); 210 let pagination = document.querySelector('#pagination'); 211 container.innerHTML = ''; 212 pagination.innerHTML = ''; 213 loaderBehavior(); 214 if (result['renderCards']) { 215 appendNewCards(result['renderCards'], container); 216 new StoreLocatorBehavior('#form-field-communeHome', '#submit-button'); 217 } 218 if (result['pagination']) { 219 appendNewPagination(result['pagination'], pagination); 220 activePageCorrection(); 221 paginationOverseer(); 222 } 223 224 225 } else { 226 console.error('Failed to fetch new data:', result.message); 227 } 228 } catch (error) { 229 console.error('Error parsing JSON response:', error); 230 } finally { 231 } 232 }) 233 .catch(error => console.error('Error fetching new data:', error)); 234} 235 236function activePageCorrection() { 237 let activePage = getActivePage(); 238 let pagination = document.querySelector('.paginationjs-pages'); 239 let activePagination = pagination.querySelector('.active'); 240 activePagination = parseInt(activePagination.getAttribute('data-num')); 241 if (activePage !== activePagination) { 242 activePagination.classList.remove('active'); 243 activePagination = pagination.querySelector(`[data-num="${activePage}"]`); 244 activePagination.classList.add('active'); 245 } 246} 247 248function appendNewCards(html, container) { 249 return container.innerHTML = html; 250} 251 252function appendNewPagination(pagination, paginationjs) { 253 return paginationjs.innerHTML = pagination; 254} 255 256 257function appendLoader(loader) { 258 let container = document.querySelector('#data-container'); 259 container.innerHTML = loader; 260} 261 262 263function hideCards(cards, page) { 264 if (cards.length > 0) { 265 cards.forEach((e) => { 266 if (parseInt(e.getAttribute('page')) !== page) { 267 e.classList.add('hide'); 268 window.scrollTo(0, 0); 269 } else { 270 e.classList.remove('hide'); 271 } 272 }); 273 } 274} 275 276function setCurrentPage(page, pagination) { 277 for (let i = 0; i < pagination.length; i++) { 278 if (parseInt(pagination[i].getAttribute('data-num')) === page) { 279 pagination[i].classList.add('active'); 280 } else { 281 pagination[i].classList.remove('active'); 282 } 283 } 284} 285 286function paginationLayout(page, next, prev, paginationElements, activePage = 1) { 287 let maxPage = paginationElements[0].getAttribute('maxpage'); 288 maxPage = parseInt(maxPage); 289 let excludedLabels = ['', '1', '2', '19', '...', maxPage, activePage]; // 2 et 5 seront transformés en ... 290 //disable previous and next buttons 291 if (activePage === 1) { 292 prev.classList.add('disabled'); 293 prev.setAttribute('data-num', activePage); 294 } else { 295 prev.classList.remove('disabled'); 296 prev.setAttribute('data-num', activePage - 1); 297 } 298 if (activePage === maxPage) { 299 next.classList.add('disabled'); 300 next.setAttribute('data-num', activePage); 301 } else { 302 next.classList.remove('disabled'); 303 next.setAttribute('data-num', activePage + 1); 304 } 305 paginationElements.forEach((page) => { 306 let pageNum = page.getAttribute('data-num'); 307 if (pageNum === '') return; 308 let pageNumber = parseInt(pageNum); 309 if ((pageNumber < activePage - 3 || pageNumber >
309 activePage + 3) && (pageNumber !== 1 && pageNumber !== 2 && pageNumber !== 19 && pageNumber !== maxPage)) { 310 page.classList.add('hide'); 311 } else { 312 page.classList.remove('hide'); 313 } 314 if (activePage >= 6) { 315 if (pageNumber === 2) { 316 page.classList.add('paginationjs-ellipsis'); 317 page.classList.add('disabled'); 318 page.innerHTML = '<a class="disabled">...</a>'; 319 } 320 } else { 321 if (pageNumber === 2) { 322 page.classList.remove('paginationjs-ellipsis'); 323 page.classList.remove('disabled'); 324 page.innerHTML = '<a>2</a>'; 325 } 326 } 327 328 if (activePage <= 14) { 329 if (pageNumber === 19) { 330 page.classList.add('paginationjs-ellipsis'); 331 page.classList.add('disabled'); 332 page.innerHTML = '<a class="disabled">...</a>'; 333 } 334 } else { 335 if (pageNumber === 19) { 336 page.classList.remove('paginationjs-ellipsis'); 337 page.classList.remove('disabled'); 338 page.innerHTML = '<a>19</a>'; 339 } 340 } 341 }); 342} 343 344function paginationOverseer() { 345 waitForElem('.paginationjs-pages').then((e) => { 346 let pagination = document.querySelector('.paginationjs-pages'); 347 let cards = document.querySelectorAll('.a_avis-card'); 348 let paginationElements = pagination.querySelectorAll('.J-paginationjs-page'); 349 let next = pagination.querySelector('.paginationjs-next'); 350 let prev = pagination.querySelector('.paginationjs-prev'); 351 paginationLayout(page, next, prev, paginationElements); 352 353 function paginationClickHandler(pagi) { 354 return function () { 355 if (pagi.classList.contains('disabled')) { 356 return; 357 } 358 let page = parseInt(pagi.getAttribute('data-num')); 359 // paginationLayout(page, next, prev, paginationElements); 360 hideCards(cards, page); 361 setCurrentPage(page, paginationElements); 362 seeMoreOrLess2(); 363 364 let activePage = getActivePage(); 365 paginationLayout(page, next, prev, paginationElements, activePage); 366 }; 367 } 368 369 paginationElements.forEach((pagi) => { 370 pagi.addEventListener('click', paginationClickHandler(pagi)); 371 }); 372 }); 373} 374 375function getActivePage() { 376 let pagination = document.querySelector('.paginationjs-pages'); 377 let activePage = pagination.querySelector('.active'); 378 activePage = activePage.getAttribute('data-num'); 379 activePage = parseInt(activePage); 380 // let next = pagination.querySelector('.paginationjs-next'); 381 // let prev = pagination.querySelector('.paginationjs-prev'); 382 // let maxPage = pagination.getAttribute('maxPage'); 383 // maxPage = parseInt(maxPage); 384 return activePage; 385} 386 387// Fonction plus utilisée 388function storeLocatorRepositionner(page, activePage, maxPage) { 389 let dataContainer = document.querySelector('#data-container'); 390 let avis = dataContainer.querySelectorAll('.a_avis-card'); 391 let storeLocator = document.querySelector('#store-locator-avis-clone'); 392 let url = window.location.href; 393 if (url.includes('localdomaliance') || 'domaliance') { 394 for (let i = 0; i < avis.length; i++) { 395 let newPosition = 0; 396 if (avis.length < 3) { 397 avis[i].insertAdjacentElement('afterend', storeLocator); 398 } else { 399 newPosition = (9 * activePage) - 4; 400 avis[newPosition].insertAdjacentElement('afterend', storeLocator); 401 } 402 } 403 } else { 404 for (let i = 0; i < avis.length; i++) { 405 let newPosition = 0; 406 if (avis.length < 3) { 407 avis[i].insertAdjacentElement('afterend', storeLocator); 408 } else { 409 newPosition = (9 * activePage) - 6; 410 avis[newPosition].insertAdjacentElement('afterend', storeLocator); 411 } 412 } 413 } 414 415 const formFieldCommuneHome = "#form-field-communeHome"; 416 const submitButton = "#submit-button"; 417 new StoreLocatorBehavior(formFieldCommuneHome, submitButton); 418} 419 420 421//Tools 422function waitForElem(selector, parent = document) { 423 return new Promise((resolve) => { 424 if (parent) { 425 const target = parent.querySelector(selector); 426 if (target) { 427 return resolve(target); 428 } 429 const observer = new MutationObserver((mutations) => { 430 const target = parent.querySelector(selector); 431 if (target) { 432 resolve(target); 433 observer.disconnect(); 434 } 435 }); 436 observer.observe(parent, { 437 childList: true, 438 subtree: true, 439 }); 440 } 441 }); 442} 443
444function throttle(callback, delay = 50) { 445 let lastCallTime = 0; 446 let timerId; 447 return function () { 448 let currentTime = Date.now(); 449 let elapsedTime = currentTime - lastCallTime; 450 if (elapsedTime < delay) { 451 clearTimeout(timerId); 452 timerId = setTimeout(() => { 453 lastCallTime = Date.now(); 454 callback.apply(this, arguments); 455 }, delay); 456 } else { 457 lastCallTime = currentTime; 458 callback.apply(this, arguments); 459 } 460 }; 461} 462 463 464function makeRequest(url, type, action, data) { 465 return new Promise((resolve, reject) => { 466 if (isRequestInProgress) { 467 reject("Another request is already in progress"); 468 return; 469 } 470 isRequestInProgress = true; 471 472 // Show loader 473 const loader = document.querySelector('#loader'); 474 if (loader) { 475 loader.classList.remove('hide'); 476 } 477 478 let xhr = new XMLHttpRequest(); 479 480 if (!xhr) { 481 reject("XMLHttpRequest not supported"); 482 return; 483 } 484 485 xhr.open(type, url, true); 486 xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 487 488 let queryString = "action=" + action + "&"; 489 queryString += Object.keys(data) 490 .map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(data[key])) 491 .join("&"); 492 493 xhr.send(queryString); 494 495 xhr.onreadystatechange = function () { 496 try { 497 if (xhr.readyState === XMLHttpRequest.DONE) { 498 if (xhr.status === 200) { 499 resolve(xhr.responseText); 500 } else { 501 reject("There was a problem with the request. Status: " + xhr.status); 502 } 503 isRequestInProgress = false; 504 505 // Hide loader 506 if (loader) { 507 loader.classList.add('hide'); 508 } 509 } 510 } catch (e) { 511 reject("Caught Exception: " + e.description); 512 isRequestInProgress = false; 513 514 // Hide loader in case of error 515 if (loader) { 516 loader.classList.add('hide'); 517 } 518 } 519 }; 520 }); 521} 522 523 524function mutationObserver(target) { 525 if (!target) return; 526 try { 527 const targetElement = document.getElementById(target); 528 529 if (!targetElement) { 530 throw new Error('Target element not found'); 531 } 532 const config = {attributes: true, childList: true, subtree: true}; 533 534 const handleMutations = function (mutationsList, observer) { 535 for (const mutation of mutationsList) { 536 if (mutation.type === 'childList') { 537 reviewListener(); 538 } else if (mutation.type === 'attributes') { 539 540 } 541 } 542 }; 543 const observer = new MutationObserver(handleMutations); 544 observer.observe(targetElement, config); 545 } catch (error) { 546 console.error('Error setting up mutation observer:', error); 547 } 548}
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.