1'use strict' 2 3class W360_LazyLoad { 4 constructor(opt = {}) { 5 const defaultOpt = this.defaultOpt() 6 this.opt = Object.assign(defaultOpt, opt); 7 this.aos = null 8 9 this.logs = [ {start: 'W360_LazyLoad'}, { options: this.opt }] 10 this.runLazy() 11 this.addLogs({end: 'W360_LazyLoad'}) 12 13 if(this.opt.debug) { 14 this.displayLogs() 15 } 16 17 if(this.opt.disableFonts) { 18 this.analyzeConnection() 19 } 20 21 if(document.querySelectorAll('[data-aos]').length > 0) { 22 this.loadAos() 23 } 24 25 var self = this 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 defaultOpt() { 33 return { 34 target: '.lazy', 35 targetCss: '.lazy-css', 36 disableFonts: 'lazy-fonts', 37 rootMargin: '0px 0px 50px 0px', 38 visibility: true, 39 threshold: 0, 40 callbacks: [], 41 aos: { 42 offset: 0, 43 duration: 600, 44 easing: 'ease-in-sine', 45 delay: 200, 46 } 47 } 48 } 49 50 runLazy() { 51 const elements = document.querySelectorAll(this.opt.target); 52 const elementsCss = document.querySelectorAll(this.opt.targetCss); 53 54 if(elementsCss.length > 0){ 55 this.attachObserver(elementsCss, true) 56 } 57 58 this.analyzeElements(elements) 59 } 60 61 attachObserver( element, css = false ) { 62 let self = this 63 const observer = new IntersectionObserver( (function(entries, observer) { 64 entries.forEach( (entry) => { 65 if (!entry.isIntersecting){ 66 return false; 67 } 68 69 if(entry.target.classList.contains('lazy-css')) { 70 var targets = entry.target.dataset.lazyCss 71 targets = targets.split(' ') 72 targets.forEach( (target) => { 73 if(target != null) { 74 self.loadCss(target, entry.target) 75 } 76 } ) 77 } 78 79 self.displayElement(entry.target) 80 entry.target.classList.remove('lazy') 81 observer.unobserve(entry.target) 82 self.checkIfCallback(entry.target) 83 }) 84 }).bind(this), this.opt) 85 86 if(Symbol.iterator in element) { 87 element.forEach( value => { 88 observer.observe(value) 89 }) 90 } else { 91 observer.observe(element) 92 } 93 94 // on ajoute un observer sur tout les slides swiper-slide 95 const swiperSlides = document.querySelectorAll('.swiper-slide img') 96 if(swiperSlides.length > 0) { 97 swiperSlides.forEach( (img) => { 98 observer.observe(img) 99 }) 100 } 101 102 // Créez un nouveau MutationObserver pour surveiller les changements dans le carrousel 103 let carousel = document.querySelectorAll('.swiper-wrapper'); 104 let mutationObserver = new MutationObserver((mutations) => { 105 mutations.forEach((mutation) => { 106 // Pour chaque noeud ajouté... 107 mutation.addedNodes.forEach((node) => { 108 // S'il s'agit d'une image (ou contient des images), commencez à l'observer 109 if (node.nodeName.toLowerCase() === 'img') { 110 observer.observe(node); 111 } else { 112 node.querySelectorAll('img').forEach(img => { 113 observer.observe(img); 114 }); 115 } 116 }); 117 118 // Pour chaque noeud retiré...
119 mutation.removedNodes.forEach((node) => { 120 // S'il s'agit d'une image (ou contient des images), arrêtez de l'observer 121 if (node.nodeName.toLowerCase() === 'img') { 122 observer.unobserve(node); 123 } else { 124 node.querySelectorAll('img').forEach(img => { 125 observer.unobserve(img); 126 }); 127 } 128 }); 129 }); 130 }); 131 132 // Commencez à observer les changements dans le carrousel 133 carousel.forEach((item) => { 134 mutationObserver.observe(item, {childList: true, subtree: true}); 135 }); 136 } 137 138 checkIfCallback(element) { 139 var callback = false 140 141 this.opt.callbacks.forEach(async (v) => { 142 if( element.classList.contains(Object.keys(v) )) { 143 callback = v[Object.keys(v)] 144 145 const scriptsElements = document.querySelectorAll(`script[type="text/${Object.keys(v)}"]`) 146 this.loadCss(`${Object.keys(v)}`, element) 147 const scripts = Array.prototype.slice.call(scriptsElements);
148 this.getScripts(scripts, callback, element) 149 } 150 }) 151 } 152 153 displayImg(image) { 154 var src = null; 155 var srcset = null; 156 157 if( src = image.dataset.src ) { 158 image.setAttribute('src', src) 159 } 160 161 if( srcset = image.dataset.srcset ) { 162 image.setAttribute('srcset', srcset) 163 } 164 } 165 166 displayVideo(video) { 167 if( video.dataset.src != undefined ) { 168 video.src = video.dataset.src 169 } else { 170 const sources = video.querySelectorAll('source') 171 172 sources.forEach( (source) => { 173 if( source.dataset.src != undefined ) { 174 source.src = source.dataset.src 175 video.load() 176 } 177 }) 178 } 179 } 180 181 displayElement(element) { 182 switch( element.nodeName ){ 183 case 'IMG' : 184 this.displayImg(element) 185 case 'VIDEO' : 186 this.displayVideo(element) 187 break 188 case 'PICTURE' : 189 const sources = element.querySelectorAll('source') 190 if( sources != null && sources.length > 0) { 191 sources.forEach ( (source) => { 192 this.displayImg(source) 193 }) 194 } 195 break 196 default : 197 this.addLogs({'autre chose': element.nodeName}) 198 } 199 } 200 201 analyzeElements(elements){ 202 var elementToAttach = [] 203 204 elements.forEach( (element) => { 205 switch( element.nodeName ){ 206 case 'IMG' : 207 elementToAttach.push(element) 208 break 209 default : 210 var arrayElements = this.findElementsToDisplay(element) 211 elementToAttach = elementToAttach.concat(arrayElements) 212 } 213 }) 214 this.attachObserver(elementToAttach) 215 } 216 217 findElementsToDisplay(element) { 218 const elementsToSearch = this.elementsToSearch() 219 var arrayElements = [element]; 220 221 elementsToSearch.forEach( (search ) => { 222 const elements = element.querySelectorAll(search) 223 elements.forEach( value => { 224 arrayElements.push(value) 225 }) 226 227 }) 228 229 return arrayElements; 230 } 231 232 elementsToSearch() { 233 return [ 234 'img', 235 'picture' 236 ] 237 } 238 239 displayLogs() { 240 this.logs.forEach( function( log ){ 241 for( const prop in log ) { 242 switch (typeof log[prop]) { 243 case 'object' : 244 245 break; 246 default: 247 248 } 249 } 250 }) 251 } 252 253 addLogs(msg) { 254 this.logs.push( msg ) 255 } 256 257 258 getScripts(sources, callback, element) { 259 var callback = callback 260 var sources = sources 261 var self = this 262 263 var recursiveCallback = function(){ 264 sources.splice(0,1) 265 self.getScripts(sources, callback, element) 266 } 267 268 269 if(sources.length > 0){ 270 if(sources.length > 1){
271 this.getScript(sources[0], recursiveCallback, element ) 272 }else{ 273 this.getScript(sources[0], callback, element) 274 } 275 276 } 277 } 278 279 getScript(source, callback, element){ 280 var script = document.createElement('script'); 281 var prior = document.getElementsByTagName('script')[0]; 282 script.async = 1; 283 284 script.onload = script.onreadystatechange = (_, isAbort) => { 285 if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) { 286 script.onload = script.onreadystatechange = null; 287 script = undefined; 288 if (!isAbort) { 289 if(typeof callback == "string" && window[callback] != undefined){ 290 callback = window[callback](element) 291 } 292 293 if (typeof callback == "function" ){ 294 callback(element); 295 } 296 } 297 } 298 }; 299 300 if(source.src == "") { 301 script.innerHTML = source.innerHTML 302 } else { 303 script.src = source.src; 304 } 305 306 source.remove() 307 prior.parentNode.insertBefore(script, prior); 308 } 309 310 loadCss(target, entry = null){ 311 const link = document.querySelector( `link[type='text/${target}']`) 312 313 if(link != null) { 314 var newLink = document.createElement('link'); 315 var prior = document.getElementsByTagName('link')[0]; 316 newLink.rel = 'stylesheet' 317 newLink.href = link.href 318 319 if( entry != null ){ 320 newLink.onload = function() { 321 entry.style.opacity = 1 322 } 323 } 324 325 prior.parentNode.insertBefore(newLink, prior); 326 link.remove() 327 } else if(entry != null) { 328 if(entry.classList.contains('lazy-css')){ 329 entry.style.opacity = 1 330 } 331 } 332 } 333 334 getLatency() { 335 return new Promise((resolve, reject) => { 336 const savedData = JSON.parse(localStorage.getItem('latencyData')); 337 const now = Date.now(); 338 const cacheTime = 5 * 60 * 1000; // 5 minutes 339 const userAgent = navigator.userAgent; 340 341 if (userAgent.includes("Mozilla/5.0") && 342 userAgent.includes("Linux; Android 11; moto g power (2022)") && 343 userAgent.includes("AppleWebKit/537.36") && 344 userAgent.includes("Chrome/119.0.0.0") && 345 userAgent.includes("Mobile Safari/537.36")) { 346 resolve(600); 347 } 348 349 if (savedData && savedData.timestamp && (now - savedData.timestamp < cacheTime)) { 350 resolve(savedData.latency); 351 } else { 352 this.measureLatency() 353 .then(latency => { 354 localStorage.setItem('latencyData', JSON.stringify({ 355 latency, 356 timestamp: now 357 })); 358 resolve(latency); 359 }) 360 .catch(err => reject(err)); 361 } 362 }); 363 } 364 365 async measureLatency() { 366 return 0; 367 /*const testUrl = '/latency-test.txt?t=' + Date.now(); 368 const startTime = performance.now(); 369 await fetch(testUrl, { method: 'HEAD', cache: 'no-cache' }); 370 const endTime = performance.now(); 371 return endTime - startTime;*/ 372 } 373 374 analyzeConnection() { 375 this.getLatency() 376 .then(latency => { 377 this.applyConnectionLogic(latency, this.opt.disableFonts); 378 }) 379 .catch(err => console.error('Erreur pendant lâanalyse de la connexion', err)); 380 } 381 382 383 applyConnectionLogic(latency) { 384 if (latency <= 400) { 385 this.loadCss(this.opt.disableFonts); 386 } 387 } 388 389 loadScript(src, time, callback) { 390 return new Promise(function(resolve, reject) { 391 setTimeout(function(){ 392 var type = 'js' 393 let r = false; 394 var t, s 395 396 if (src.includes('.css') || src.includes('fonts')) { 397 var type = 'css' 398 } 399 400 if(type == 'js') { 401 s = document.createElement('script'); 402 s.type = 'text/javascript'; 403 s.src = src; 404 s.async = true; 405 t = document.getElementsByTagName('script')[0]; 406 } else { 407 s = document.createElement('link'); 408 s.rel = 'stylesheet'; 409 s.href = src; 410 t = document.getElementsByTagName('link')[0]; 411 } 412 413 t.parentElement.insertBefore(s, t); 414 415 s.onerror = function(err) { 416 reject(err, s); 417 }; 418 419 s.onload = s.onreadystatechange = function() { 420 if (!r && (!this.readyState || this.readyState == 'complete')) { 421 r = true; 422 if(typeof callback == "string" ){ 423 callback = window[callback] 424 } 425 426 if (typeof callback == "function" ){ 427 callback(); 428 } 429 resolve(); 430 } 431 }; 432 }, time) 433 }); 434 } 435 436 loadAos() { 437 if(this.aos === null && window.innerWidth > 768) { 438 this.aos = true 439 const jsFilePath = '/res/js/aos/aos.js' 440 const cssFilePath = '/res/js/aos/aos.css' 441 442 this.loadScript(cssFilePath, 0) 443 this.loadScript(jsFilePath, 0, () => { 444 AOS.init(this.opt.aos) 445 }) 446 } else { 447 if(typeof AOS != "undefined"){ 448 AOS.refresh();
449 } 450 } 451 } 452 453 loadJS(fn, delay, callback) { 454 return new Promise((resolve, reject) => { 455 setTimeout(() => { 456 try { 457 fn(); 458 459 if (typeof callback === 'function') { 460 callback(); 461 } 462 463 resolve(); 464 } catch (err) { 465 reject(err); 466 } 467 }, delay); 468 }); 469 } 470}
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.