1import objectFitImages from 'object-fit-images'; 2import { sitecore, MediaQuery } from '../'; 3 4export class LazyLoad { 5 /* eslint-disable-next-line */ 6 constructor(...opts) { 7 this._refs = { 8 elements: Array.from( 9 document.querySelectorAll( 10 '.lazy, [data-background], [data-background-xs], [data-background-sm], [data-background-md], [data-background-lg], [data-background-xl], [data-background-xxl]', 11 ), 12 ), 13 }; 14 15 this._props = { 16 observer: null, 17 IE11: !!window.MSInputMethodContext && !!document.documentMode, 18 pageEditor: sitecore.isInPageEditor, 19 }; 20 21 if (this._props.observer != null) { 22 this._props.observer.disconnect(); 23 } 24 25 if (this._props.pageEditor) { 26 return false; 27 } 28 29 if ('IntersectionObserver' in window) { 30 const onIntersection = (entries, observer) => { 31 entries.forEach((x) => { 32 if (x.isIntersecting) { 33 this._onIntersection(x.target, (el) => { 34 observer.unobserve(el); 35 }); 36 } 37 }); 38 }, 39 oIntersectionConfig = { 40 rootMargin: '300px 0px', 41 }; 42 43 this._props.observer = new IntersectionObserver(onIntersection, oIntersectionConfig); 44 this._refs.elements.forEach((x) => this._props.observer.observe(x)); 45 } else { 46 this._refs.elements.forEach((x) => { 47 this._onIntersection(x, null); 48 }); 49 50 // Fallback 51 if (this._props.IE11) { 52 objectFitImages(); 53 } 54 } 55 } 56 57 /** 58 * Create and append elements to watched reference 59 * @param {String} query - Selector 60 * @param {HTMLElement} base - query base (document) 61 */ 62 add(query = '', base = document) { 63 if (!query.length) return false; 64 65 const arrElements = Array.from(base.querySelectorAll(query)); 66 67 if (arrElements.length) { 68 this._refs.elements.push(arrElements); 69 } 70 } 71 72 /** 73 * Handler for executing load-procedure for an item 74 * @param {HTMLElement} el - Watched element 75 * @param {Function} callback - Function to execute once load has completed 76 */ 77 _onIntersection(el = HTMLElement, callback = Function) { 78 /** 79 * Replace source with data attribute 80 * Remove used data attribute 81 * @param {element} el 82 * @param {string} dataSource 83 * @param {string} source 84 */ 85 const addSource = (el, dataSource, source) => { 86 if (typeof el.dataset[dataSource] != 'undefined') { 87 el[source] = el.dataset[dataSource]; 88 el.removeAttribute(`data-${dataSource}`); 89 } 90 }; 91 switch (el.nodeName) { 92 case 'OBJECT': 93 /* eslint-disable-next-line */ 94 const showImage = () => { 95 el.classList.remove('lazy'); 96 el.classList.add('is-rendered'); 97 }; 98 /* eslint-disable-next-line */ 99 const displayFallbackImage = () => { 100 const fallback = el.querySelector('img'); 101 addSource(fallback, 'src', 'src'); 102 showImage(); 103 }; 104 105 addSource(el, 'src', 'data'); 106 el.addEventListener('load', showImage, false); 107 el.addEventListener('error', displayFallbackImage, false); 108 break; 109 case 'PICTURE': 110 /* eslint-disable-next-line */ 111 let arrChildren = el.children, 112 intLoaded = 0; 113 114 for (let i = 0; Object.keys(arrChildren).length > i; i++) { 115 const x = arrChildren[i]; 116 if (typeof x.dataset === 'undefined') continue; 117 118 if (typeof x.dataset.srcset != 'undefined') { 119 x.srcset = x.dataset.srcset; 120 x.removeAttribute('data-srcset'); 121 } 122 123 if (typeof x.dataset.src != 'undefined') { 124 x.src = x.dataset.src; 125 x.removeAttribute('data-src'); 126 } 127 128 if (x) { 129 x.addEventListener('load', () => { 130 x.classList.remove('lazy'); 131 }); 132 } 133 134 intLoaded++; 135 } 136 137 if (intLoaded > 0) { 138 if (el.classList.contains('lazy')) { 139 el.classList.remove('lazy'); 140 } 141 142 el.classList.add('is-rendered'); 143 if (typeof callback === 'function') { 144 callback(el); 145 } 146 } 147 break; 148 case 'IMG':
149 if (typeof el.dataset.srcset != 'undefined') { 150 el.srcset = el.dataset.srcset; 151 el.removeAttribute('data-srcset'); 152 } 153 154 if (typeof el.dataset.src != 'undefined') { 155 el.src = el.dataset.src; 156 el.removeAttribute('data-src'); 157 } 158 159 if (el) { 160 el.addEventListener('load', () => { 161 el.classList.remove('lazy'); 162 163 const oParentEl = el.parentElement; 164 if (oParentEl.classList.contains('lazy')) { 165 el.parentElement.classList.remove('lazy'); 166 el.parentElement.classList.add('is-rendered'); 167 } 168 }); 169 } 170 break; 171 case 'DIV': 172 /* eslint-disable-next-line */ 173 const fn = (callback) => { 174 let props = {}, 175 strMediaItem = 176 typeof el.dataset.background != 'undefined' ? el.dataset.background : null; 177 178 // Populate props 179 for (const i in el.dataset) { 180 if (i.indexOf('background') > -1) { 181 const key = i.replace('background', '').toLowerCase(); 182 if (key != '') props[key] = el.dataset[i]; 183 } 184 } 185 186 const availablePointers = Object.keys(props).sort().reverse(); 187 props = this._sort(props); 188 189 availablePointers.forEach((x) => { 190 if (MediaQuery.is(x) || availablePointers.length === 1) { 191 strMediaItem = props[x]; 192 } 193 }); 194 195 if (strMediaItem != null) { 196 el.style.backgroundImage = 'url(' + strMediaItem + ')'; 197 } 198 199 if (typeof callback == 'function') { 200 callback(el); 201 } 202 }; 203 204 // OnIntersection 205 fn(callback); 206 207 // Event: MQ 208 window.addEventListener('change.mq', fn.bind(this)); 209 break; 210 } 211 } 212 213 _sort(obj) { 214 return Object.keys(obj) 215 .sort() 216 .reverse() 217 .reduce(function (result, key) { 218 result[key] = obj[key]; 219 return result; 220 }, {}); 221 } 222}
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.