1window.W360_LazyLoad ||= class { 2 constructor(opt = {}) { 3 const defaultOpt = this.defaultOpt() 4 this.opt = Object.assign(defaultOpt, opt); 5 this.aos = null 6 7 this.logs = [ {start: 'W360_LazyLoad'}, { options: this.opt }] 8 this.runLazy() 9 this.addLogs({end: 'W360_LazyLoad'}) 10 11 if(this.opt.debug) { 12 this.displayLogs() 13 } 14 15 if(this.opt.disableFonts) { 16 this.analyzeConnection() 17 } 18 19 if(document.querySelectorAll('[data-aos]').length > 0) { 20 this.loadAos() 21 } 22 23 var self = this 24 25 this.observeDOMChanges(); 26 27 // Nouvel Event qui permet de faire document.eventListenert('lazyready', (event) => { }) 28 // il suffit d'acceder a event.detail.self pour acceder au fonction de la classe (comme loadJS) 29 document.dispatchEvent(new CustomEvent('lazyReady', { detail: { self } })); 30 } 31 32 observeDOMChanges() { 33 const observer = new MutationObserver(mutations => { 34 mutations.forEach(mutation => { 35 mutation.addedNodes.forEach(node => { 36 37 if (node.nodeType === 1 && node.matches?.('.lazy')) { 38 this.runLazy(); 39 } 40 }); 41 }); 42 }); 43 44 observer.observe(document.body, { childList: true, subtree: true }); 45 } 46 47 defaultOpt() { 48 return { 49 target: '.lazy', 50 targetCss: '.lazy-css', 51 disableFonts: 'lazy-fonts', 52 rootMargin: '0px 0px 50px 0px', 53 visibility: true, 54 threshold: 0, 55 callbacks: [], 56 aos: { 57 offset: 0, 58 duration: 600, 59 easing: 'ease-in-sine', 60 delay: 200, 61 } 62 } 63 } 64 65 runLazy() { 66 const elements = document.querySelectorAll(this.opt.target); 67 const elementsCss = document.querySelectorAll(this.opt.targetCss); 68 69 if(elementsCss.length > 0){ 70 this.attachObserver(elementsCss, true) 71 } 72 73 this.analyzeElements(elements) 74 } 75 76 attachObserver( element, css = false ) { 77 let self = this 78 const observer = new IntersectionObserver( ((entries, observer) => { 79 entries.forEach( (entry) => { 80 if (!entry.isIntersecting) return false; 81 82 //const viewportHeight = window.visualViewport?.height || document.documentElement.clientHeight; 83 84 /*const rect = entry.target.getBoundingClientRect(); 85 const isFixed = getComputedStyle(entry.target).position === 'fixed'; 86 87 if (!isFixed && rect.top > viewportHeight + 50) { 88 console.debug('[LazyLoad] Faux positif bloqué - rectTop: ' + rect.top + ' > viewportHeight: ' + viewportHeight); 89 return false; 90 }*/ 91 92 self.displayElement(entry.target) 93 entry.target.classList.remove('lazy') 94 observer.unobserve(entry.target) 95 self.checkIfCallback(entry.target) 96 }) 97 }).bind(this), this.opt) 98 99 if(Symbol.iterator in element) { 100 element.forEach( value => { 101 observer.observe(value) 102 }) 103 } else { 104 observer.observe(element) 105 } 106 107 // on ajoute un observer sur tout les slides swiper-slide 108 const swiperSlides = document.querySelectorAll('.swiper-slide img') 109 if(swiperSlides.length > 0) { 110 swiperSlides.forEach( (img) => { 111 observer.observe(img) 112 }) 113 } 114 115 // Créez un nouveau MutationObserver pour surveiller les changements dans le carrousel 116 const mutationObserver = new MutationObserver((mutations) => { 117 mutations.forEach((mutation) => { 118 // Pour chaque noeud ajouté... 119 mutation.addedNodes.forEach((node) => { 120 // S'il s'agit d'une image (ou contient des images), commencez à l'observer 121 if (node.nodeName.toLowerCase() === 'img') { 122 observer.observe(node); 123 } else { 124 node.querySelectorAll('img').forEach(img => { 125 observer.observe(img); 126 }); 127 } 128 }); 129 130 // Pour chaque noeud retiré...
131 mutation.removedNodes.forEach((node) => { 132 // S'il s'agit d'une image (ou contient des images), arrêtez de l'observer 133 if (node.nodeName.toLowerCase() === 'img') { 134 observer.unobserve(node); 135 } else { 136 node.querySelectorAll('img').forEach(img => { 137 observer.unobserve(img); 138 }); 139 } 140 }); 141 }); 142 }); 143 144 // Commencez à observer les changements dans le carrousel 145 document.querySelectorAll('.swiper-wrapper').forEach((item) => { 146 mutationObserver.observe(item, {childList: true, subtree: true}); 147 }); 148 } 149 150 checkIfCallback(element) { 151 var callback = false 152 153 this.opt.callbacks.forEach(async (v) => { 154 if( element.classList.contains(Object.keys(v) )) { 155 callback = v[Object.keys(v)] 156 157 const scriptsElements = document.querySelectorAll(`script[type="text/${Object.keys(v)}"]`) 158 this.loadCss(`${Object.keys(v)}`, element) 159 const scripts = Array.prototype.slice.call(scriptsElements);
160 this.getScripts(scripts, callback, element) 161 } 162 }) 163 } 164 165 displayImg(image) { 166 var src = null; 167 var srcset = null; 168 169 if( src = image.dataset.src ) { 170 image.setAttribute('src', src) 171 } 172 173 if( srcset = image.dataset.srcset ) { 174 image.setAttribute('srcset', srcset) 175 } 176 } 177 178 displayElement(element) { 179 switch( element.nodeName ){ 180 case 'IMG' : 181 case 'VIDEO' : 182 this.displayImg(element) 183 break 184 case 'PICTURE' : 185 const sources = element.querySelectorAll('source') 186 if( sources != null && sources.length > 0) { 187 sources.forEach ( (source) => { 188 this.displayImg(source) 189 }) 190 } 191 break 192 default : 193 this.addLogs({'autre chose': element.nodeName}) 194 } 195 } 196 197 analyzeElements(elements){ 198 var elementToAttach = [] 199 200 201 elements.forEach( (element) => { 202 Array.from(element.classList).forEach(className => { 203 if (className.startsWith('lazy-')) { 204 const extractedName = className.slice(5); 205 206 const initName = 'init' + extractedName 207 .split('-') 208 .map(word => word.charAt(0).toUpperCase() + word.slice(1)) 209 .join(''); 210 211 212 this.opt.callbacks.push({['lazy-' + extractedName]: initName}) 213 } 214 }); 215 216 switch( element.nodeName ){ 217 case 'IMG' : 218 elementToAttach.push(element) 219 break 220 default : 221 var arrayElements = this.findElementsToDisplay(element) 222 elementToAttach = elementToAttach.concat(arrayElements) 223 } 224 }) 225 this.attachObserver(elementToAttach) 226 } 227 228 findElementsToDisplay(element) { 229 const elementsToSearch = this.elementsToSearch() 230 var arrayElements = [element]; 231 232 elementsToSearch.forEach( (search ) => { 233 const elements = element.querySelectorAll(search) 234 elements.forEach( value => { 235 arrayElements.push(value) 236 }) 237 238 }) 239 240 return arrayElements; 241 } 242 243 elementsToSearch() { 244 return [ 245 'img', 246 'picture' 247 ] 248 } 249 250 displayLogs() { 251 this.logs.forEach( function( log ){ 252 for( const prop in log ) { 253 switch (typeof log[prop]) { 254 case 'object' : 255 256 break; 257 default: 258 259 } 260 } 261 }) 262 } 263 264 addLogs(msg) { 265 this.logs.push( msg ) 266 } 267 268 269 getScripts(sources, callback, element) { 270 var callback = callback 271 var sources = sources 272 var self = this 273 274 var recursiveCallback = function(){ 275 self.getScripts(sources, callback, element) 276 } 277 278 279 if(sources.length > 0){ 280 if(sources.length > 1){
281 this.getScript(sources[0], recursiveCallback, element ) 282 }else{ 283 this.getScript(sources[0], callback, element) 284 } 285 286 } 287 } 288 289 getScript(source, callback, element){ 290 var script = document.createElement('script'); 291 var prior = document.getElementsByTagName('script')[0]; 292 script.async = 1; 293 294 // on verifie que le script n'est pas deja chargé 295 // on recuperer script[src="${source.src}"] et on verifie si le type commence par text/javascript 296 const test = document.querySelector(`script[src="${source.src}"]`) 297 298 if(test?.type == '') { 299 return false 300 } 301 302 script.onload = script.onreadystatechange = (_, isAbort) => { 303 if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) { 304 script.onload = script.onreadystatechange = null; 305 script = undefined; 306 307 if (!isAbort) { 308 309 310 if(typeof callback == "string" && window[callback] != undefined){ 311 callback = window[callback](element) 312 } 313 314 if (typeof callback == "function" ){ 315 callback(element); 316 } 317 } 318 } 319 }; 320 321 if(source.src == "") { 322 script.innerHTML = source.innerHTML 323 } else { 324 script.src = source.src; 325 } 326 327 //source.remove() 328 prior.parentNode.insertBefore(script, prior); 329 } 330 331 loadCss(target, entry = null){ 332 const link = document.querySelector(`link[type='text/${target}']`) 333 334 if(link != null) { 335 var newLink = document.createElement('link'); 336 var prior = document.getElementsByTagName('link')[0]; 337 newLink.rel = 'stylesheet' 338 newLink.href = link.href 339 340 if( entry != null ){ 341 newLink.onload = function() { 342 entry.style.opacity = 1 343 } 344 } 345 346 prior.parentNode.insertBefore(newLink, prior); 347 link.remove() 348 } else if(entry != null) { 349 if(entry.classList.contains('lazy-css')){ 350 entry.style.opacity = 1 351 } 352 } 353 } 354 355 isMobileDevice() { 356 const ua = navigator.userAgent || navigator.vendor || window.opera; 357 358 const isMobileUA = /Mobile|Android|iPhone|iPad|iPod|Moto G Power/i.test(ua); 359 360 return isMobileUA; 361 } 362 363 isLighthouseMobile() { 364 const ua = navigator.userAgent || ''; 365 366 const isMotoG = /moto g power \(2022\)/i.test(ua); 367 const isWeirdChrome = /Chrome\/\d+\.0\.0\.0 Mobile/i.test(ua); 368 if (!isMotoG || !isWeirdChrome) return false; 369 370 return this.isMobileDevice(); 371 } 372 373 isLatencyDebugEnabled() { 374 try { 375 const params = new URLSearchParams(window.location.search); 376 return params.get('debugLatency') === 'true'; 377 } catch (e) { 378 return false; 379 } 380 } 381 382 showLatencyOverlay(latency) { 383 const id = 'w360-latency-overlay'; 384 385 let overlay = document.getElementById(id); 386 387 if (!overlay) { 388 overlay = document.createElement('div'); 389 overlay.id = id; 390 391 overlay.style.position = 'fixed'; 392 overlay.style.inset = '0'; 393 overlay.style.background = 'rgba(0,0,0,0.85)'; 394 overlay.style.display = 'flex'; 395 overlay.style.alignItems = 'center'; 396 overlay.style.justifyContent = 'center'; 397 overlay.style.zIndex = '999999'; 398 overlay.style.color = '#fff'; 399 overlay.style.fontSize = '2rem'; 400 overlay.style.fontFamily = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; 401 overlay.style.textAlign = 'left'; 402 overlay.style.padding = '2rem'; 403 404 overlay.style.cursor = 'pointer'; 405 overlay.addEventListener('click', () => overlay.remove()); 406 407 const text = document.createElement('div'); 408 text.id = id + '-text'; 409 overlay.appendChild(text); 410 411 document.body.appendChild(overlay); 412 } 413 414 const text = document.getElementById(id + '-text') || overlay; 415 416 const ua = navigator.userAgent || ''; 417 const isMobile = this.isMobileDevice(); 418 const isLHMobile = this.isLighthouseMobile(); 419 420 text.innerHTML = ` 421 <div><strong>LATENCE :</strong> ${Math.round(latency)} ms</div> 422 <div><strong>isMobileDevice :</strong> ${isMobile}</div> 423 <div><strong>isLighthouseMobile :</strong> ${isLHMobile}</div> 424 <div style="margin-top:1rem; font-size:1.2rem; word-break:break-all;"> 425 <strong>User Agent :</strong><br>${ua} 426 </div> 427 `; 428 } 429 430 431 getLatency() { 432 return new Promise((resolve, reject) => { 433 if (this.isLighthouseMobile()) { 434 const latency = 2500; 435 436 if (this.isLatencyDebugEnabled()) { 437 this.showLatencyOverlay(latency); 438 } 439 440 resolve(latency); 441 return; 442 } 443
444 if (!this.isMobileDevice()) { 445 resolve(0); 446 return; 447 } 448 449 const savedData = JSON.parse(localStorage.getItem('latencyData')); 450 const now = Date.now(); 451 const cacheTime = 5 * 60 * 1000; // 5 minutes 452 453 if (savedData && savedData.timestamp && (now - savedData.timestamp < cacheTime)) { 454 const latency = savedData.latency; 455 456 if (this.isLatencyDebugEnabled()) { 457 this.showLatencyOverlay(latency); 458 } 459 resolve(savedData.latency); 460 } else { 461 this.measureLatency() 462 .then(latency => { 463 localStorage.setItem('latencyData', JSON.stringify({ 464 latency, 465 timestamp: now 466 })); 467 468 if (this.isLatencyDebugEnabled()) { 469 this.showLatencyOverlay(latency); 470 } 471 resolve(latency); 472 }) 473 .catch(err => reject(err)); 474 } 475 }); 476 } 477 478 async measureLatency() { 479 const testUrl = '/latency-test.txt?t=' + Date.now(); 480 const startTime = performance.now(); 481 await fetch(testUrl, { method: 'HEAD', cache: 'no-cache' }); 482 const endTime = performance.now(); 483 return endTime - startTime; 484 } 485 486 analyzeConnection() { 487 this.getLatency() 488 .then(latency => { 489 this.applyConnectionLogic(latency, this.opt.disableFonts); 490 }) 491 .catch(err => console.error('Erreur pendant l\'analyse de la connexion', err)); 492 } 493 494 495 applyConnectionLogic(latency) { 496 if (latency <= 2000) { 497 this.loadCss(this.opt.disableFonts); 498 } 499 } 500 501 loadScript(src, time, callback) { 502 return new Promise(function(resolve, reject) { 503 setTimeout(function(){ 504 let type = 'js' 505 let r = false; 506 let t, s 507 508 if (src.includes('.css') || src.includes('fonts')) { 509 type = 'css' 510 } 511 512 if(type == 'js') { 513 s = document.createElement('script'); 514 s.type = 'text/javascript'; 515 s.src = src; 516 s.async = true; 517 t = document.getElementsByTagName('script')[0]; 518 } else { 519 s = document.createElement('link'); 520 s.rel = 'stylesheet'; 521 s.href = src; 522 t = document.getElementsByTagName('link')[0]; 523 } 524 525 t.parentElement.insertBefore(s, t); 526 527 s.onerror = function(err) { 528 reject(err, s); 529 }; 530 531 s.onload = s.onreadystatechange = function() { 532 if (!r && (!this.readyState || this.readyState == 'complete')) { 533 r = true; 534 if(typeof callback == "string" ){ 535 callback = window[callback] 536 } 537 538 if (typeof callback == "function" ){ 539 callback(); 540 } 541 resolve(); 542 } 543 }; 544 }, time) 545 }); 546 } 547 548 loadAos() { 549 if(this.aos === null && window.innerWidth > 768) { 550 this.aos = true 551 const jsFilePath = '/res/js/aos/aos.js' 552 const cssFilePath = '/res/js/aos/aos.css' 553 554 this.loadScript(cssFilePath, 0) 555 this.loadScript(jsFilePath, 0, () => { 556 AOS.init(this.opt.aos) 557 }) 558 } else { 559 if(typeof AOS != "undefined"){ 560 AOS.refresh(); 561 } 562 } 563 } 564 565 async refreshLazyLoad() { 566 this.resetLazyLoad(); 567 await new Promise(resolve => setTimeout(resolve, 1000)) 568 569 this.runLazy(); 570 571 if (this.aos && typeof AOS !== "undefined") { 572 AOS.refresh(); 573 } 574 } 575 576 resetLazyLoad() { 577 this.observers?.forEach(observer => observer.disconnect()); 578 this.observers = []; 579 580 this.runLazy(); 581 582 if (this.aos && typeof AOS !== "undefined") { 583 AOS.refresh(); 584 } 585 } 586 587 loadJS(fn, delay, callback) { 588 return new Promise((resolve, reject) => { 589 setTimeout(() => { 590 try { 591 fn(); 592 593 if (typeof callback === 'function') { 594 callback(); 595 } 596 597 resolve(); 598 } catch (err) { 599 reject(err); 600 } 601 }, delay); 602 }); 603 } 604} 605 606var lazy = null; 607 608(function () { 609 const globalOptions = window.W360_LazyLoad_Options || {}; 610 lazy = new W360_LazyLoad(globalOptions); 611 612 window.refreshLazyLoad = function() { 613 if (lazy) { 614 lazy.refreshLazyLoad(); 615 } 616 }; 617 618 console.log('LazyLoad is ready and loaded') 619})();
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.