1 | var LazyLoad = (function () {
|
2 | 'use strict';
|
3 |
|
4 | const runningOnBrowser = typeof window !== "undefined";
|
5 | const isBot = runningOnBrowser && !("onscroll" in window) || typeof navigator !== "undefined" && /(gle|ing|ro)bot|crawl|spider/i.test(navigator.userAgent);
|
6 | const isHiDpi = runningOnBrowser && window.devicePixelRatio > 1;
|
7 |
|
8 | const defaultSettings = {
|
9 | elements_selector: ".lazy",
|
10 | container: isBot || runningOnBrowser ? document : null,
|
11 | threshold: 300,
|
12 | thresholds: null,
|
13 | data_src: "src",
|
14 | data_srcset: "srcset",
|
15 | data_sizes: "sizes",
|
16 | data_bg: "bg",
|
17 | data_bg_hidpi: "bg-hidpi",
|
18 | data_bg_multi: "bg-multi",
|
19 | data_bg_multi_hidpi: "bg-multi-hidpi",
|
20 | data_bg_set: "bg-set",
|
21 | data_poster: "poster",
|
22 | class_applied: "applied",
|
23 | class_loading: "loading",
|
24 | class_loaded: "loaded",
|
25 | class_error: "error",
|
26 | class_entered: "entered",
|
27 | class_exited: "exited",
|
28 | unobserve_completed: true,
|
29 | unobserve_entered: false,
|
30 | cancel_on_exit: true,
|
31 | callback_enter: null,
|
32 | callback_exit: null,
|
33 | callback_applied: null,
|
34 | callback_loading: null,
|
35 | callback_loaded: null,
|
36 | callback_error: null,
|
37 | callback_finish: null,
|
38 | callback_cancel: null,
|
39 | use_native: false,
|
40 | restore_on_error: false
|
41 | };
|
42 | const getExtendedSettings = customSettings => {
|
43 | return Object.assign({}, defaultSettings, customSettings);
|
44 | };
|
45 |
|
46 |
|
47 | const createInstance = function (classObj, options) {
|
48 | let event;
|
49 | const eventString = "LazyLoad::Initialized";
|
50 | const instance = new classObj(options);
|
51 | try {
|
52 |
|
53 | event = new CustomEvent(eventString, {
|
54 | detail: {
|
55 | instance
|
56 | }
|
57 | });
|
58 | } catch (err) {
|
59 |
|
60 | event = document.createEvent("CustomEvent");
|
61 | event.initCustomEvent(eventString, false, false, {
|
62 | instance
|
63 | });
|
64 | }
|
65 | window.dispatchEvent(event);
|
66 | };
|
67 |
|
68 | |
69 |
|
70 | const autoInitialize = (classObj, options) => {
|
71 | if (!options) {
|
72 | return;
|
73 | }
|
74 | if (!options.length) {
|
75 |
|
76 | createInstance(classObj, options);
|
77 | } else {
|
78 |
|
79 | for (let i = 0, optionsItem; optionsItem = options[i]; i += 1) {
|
80 | createInstance(classObj, optionsItem);
|
81 | }
|
82 | }
|
83 | };
|
84 |
|
85 | const SRC = "src";
|
86 | const SRCSET = "srcset";
|
87 | const SIZES = "sizes";
|
88 | const POSTER = "poster";
|
89 | const ORIGINALS = "llOriginalAttrs";
|
90 | const DATA = "data";
|
91 |
|
92 | const statusLoading = "loading";
|
93 | const statusLoaded = "loaded";
|
94 | const statusApplied = "applied";
|
95 | const statusEntered = "entered";
|
96 | const statusError = "error";
|
97 | const statusNative = "native";
|
98 |
|
99 | const dataPrefix = "data-";
|
100 | const statusDataName = "ll-status";
|
101 | const getData = (element, attribute) => {
|
102 | return element.getAttribute(dataPrefix + attribute);
|
103 | };
|
104 | const setData = (element, attribute, value) => {
|
105 | const attrName = dataPrefix + attribute;
|
106 | if (value === null) {
|
107 | element.removeAttribute(attrName);
|
108 | return;
|
109 | }
|
110 | element.setAttribute(attrName, value);
|
111 | };
|
112 | const getStatus = element => getData(element, statusDataName);
|
113 | const setStatus = (element, status) => setData(element, statusDataName, status);
|
114 | const resetStatus = element => setStatus(element, null);
|
115 | const hasEmptyStatus = element => getStatus(element) === null;
|
116 | const hasStatusLoading = element => getStatus(element) === statusLoading;
|
117 | const hasStatusError = element => getStatus(element) === statusError;
|
118 | const hasStatusNative = element => getStatus(element) === statusNative;
|
119 | const statusesAfterLoading = [statusLoading, statusLoaded, statusApplied, statusError];
|
120 | const hadStartedLoading = element => statusesAfterLoading.indexOf(getStatus(element)) >= 0;
|
121 |
|
122 | const safeCallback = (callback, arg1, arg2, arg3) => {
|
123 | if (!callback || typeof callback !== 'function') {
|
124 | return;
|
125 | }
|
126 | if (arg3 !== undefined) {
|
127 | callback(arg1, arg2, arg3);
|
128 | return;
|
129 | }
|
130 | if (arg2 !== undefined) {
|
131 | callback(arg1, arg2);
|
132 | return;
|
133 | }
|
134 | callback(arg1);
|
135 | };
|
136 |
|
137 | const addClass = (element, className) => {
|
138 | if (!runningOnBrowser) {
|
139 | return;
|
140 | }
|
141 | if (className === "") {
|
142 | return;
|
143 | }
|
144 | element.classList.add(className);
|
145 | };
|
146 | const removeClass = (element, className) => {
|
147 | if (!runningOnBrowser) {
|
148 | return;
|
149 | }
|
150 | if (className === "") {
|
151 | return;
|
152 | }
|
153 | element.classList.remove(className);
|
154 | };
|
155 |
|
156 | const addTempImage = element => {
|
157 | element.llTempImage = document.createElement("IMG");
|
158 | };
|
159 | const deleteTempImage = element => {
|
160 | delete element.llTempImage;
|
161 | };
|
162 | const getTempImage = element => element.llTempImage;
|
163 |
|
164 | const unobserve = (element, instance) => {
|
165 | if (!instance) return;
|
166 | const observer = instance._observer;
|
167 | if (!observer) return;
|
168 | observer.unobserve(element);
|
169 | };
|
170 | const resetObserver = observer => {
|
171 | observer.disconnect();
|
172 | };
|
173 | const unobserveEntered = (element, settings, instance) => {
|
174 | if (settings.unobserve_entered) unobserve(element, instance);
|
175 | };
|
176 |
|
177 | const updateLoadingCount = (instance, delta) => {
|
178 | if (!instance) return;
|
179 | instance.loadingCount += delta;
|
180 | };
|
181 | const decreaseToLoadCount = instance => {
|
182 | if (!instance) return;
|
183 | instance.toLoadCount -= 1;
|
184 | };
|
185 | const setToLoadCount = (instance, value) => {
|
186 | if (!instance) return;
|
187 | instance.toLoadCount = value;
|
188 | };
|
189 | const isSomethingLoading = instance => instance.loadingCount > 0;
|
190 | const haveElementsToLoad = instance => instance.toLoadCount > 0;
|
191 |
|
192 | const getSourceTags = parentTag => {
|
193 | let sourceTags = [];
|
194 | for (let i = 0, childTag; childTag = parentTag.children[i]; i += 1) {
|
195 | if (childTag.tagName === "SOURCE") {
|
196 | sourceTags.push(childTag);
|
197 | }
|
198 | }
|
199 | return sourceTags;
|
200 | };
|
201 | const forEachPictureSource = (element, fn) => {
|
202 | const parent = element.parentNode;
|
203 | if (!parent || parent.tagName !== "PICTURE") {
|
204 | return;
|
205 | }
|
206 | let sourceTags = getSourceTags(parent);
|
207 | sourceTags.forEach(fn);
|
208 | };
|
209 | const forEachVideoSource = (element, fn) => {
|
210 | let sourceTags = getSourceTags(element);
|
211 | sourceTags.forEach(fn);
|
212 | };
|
213 |
|
214 | const attrsSrc = [SRC];
|
215 | const attrsSrcPoster = [SRC, POSTER];
|
216 | const attrsSrcSrcsetSizes = [SRC, SRCSET, SIZES];
|
217 | const attrsData = [DATA];
|
218 | const hasOriginalAttrs = element => !!element[ORIGINALS];
|
219 | const getOriginalAttrs = element => element[ORIGINALS];
|
220 | const deleteOriginalAttrs = element => delete element[ORIGINALS];
|
221 |
|
222 |
|
223 |
|
224 | const setOriginalsObject = (element, attributes) => {
|
225 | if (hasOriginalAttrs(element)) {
|
226 | return;
|
227 | }
|
228 | const originals = {};
|
229 | attributes.forEach(attribute => {
|
230 | originals[attribute] = element.getAttribute(attribute);
|
231 | });
|
232 | element[ORIGINALS] = originals;
|
233 | };
|
234 | const saveOriginalBackgroundStyle = element => {
|
235 | if (hasOriginalAttrs(element)) {
|
236 | return;
|
237 | }
|
238 | element[ORIGINALS] = {
|
239 | backgroundImage: element.style.backgroundImage
|
240 | };
|
241 | };
|
242 |
|
243 |
|
244 |
|
245 | const setOrResetAttribute = (element, attrName, value) => {
|
246 | if (!value) {
|
247 | element.removeAttribute(attrName);
|
248 | return;
|
249 | }
|
250 | element.setAttribute(attrName, value);
|
251 | };
|
252 | const restoreOriginalAttrs = (element, attributes) => {
|
253 | if (!hasOriginalAttrs(element)) {
|
254 | return;
|
255 | }
|
256 | const originals = getOriginalAttrs(element);
|
257 | attributes.forEach(attribute => {
|
258 | setOrResetAttribute(element, attribute, originals[attribute]);
|
259 | });
|
260 | };
|
261 | const restoreOriginalBgImage = element => {
|
262 | if (!hasOriginalAttrs(element)) {
|
263 | return;
|
264 | }
|
265 | const originals = getOriginalAttrs(element);
|
266 | element.style.backgroundImage = originals.backgroundImage;
|
267 | };
|
268 |
|
269 | const manageApplied = (element, settings, instance) => {
|
270 | addClass(element, settings.class_applied);
|
271 | setStatus(element, statusApplied);
|
272 |
|
273 | if (!instance) return;
|
274 | if (settings.unobserve_completed) {
|
275 |
|
276 | unobserve(element, settings);
|
277 | }
|
278 | safeCallback(settings.callback_applied, element, instance);
|
279 | };
|
280 | const manageLoading = (element, settings, instance) => {
|
281 | addClass(element, settings.class_loading);
|
282 | setStatus(element, statusLoading);
|
283 |
|
284 | if (!instance) return;
|
285 | updateLoadingCount(instance, +1);
|
286 | safeCallback(settings.callback_loading, element, instance);
|
287 | };
|
288 | const setAttributeIfValue = (element, attrName, value) => {
|
289 | if (!value) {
|
290 | return;
|
291 | }
|
292 | element.setAttribute(attrName, value);
|
293 | };
|
294 | const setImageAttributes = (element, settings) => {
|
295 | setAttributeIfValue(element, SIZES, getData(element, settings.data_sizes));
|
296 | setAttributeIfValue(element, SRCSET, getData(element, settings.data_srcset));
|
297 | setAttributeIfValue(element, SRC, getData(element, settings.data_src));
|
298 | };
|
299 | const setSourcesImg = (imgEl, settings) => {
|
300 | forEachPictureSource(imgEl, sourceTag => {
|
301 | setOriginalsObject(sourceTag, attrsSrcSrcsetSizes);
|
302 | setImageAttributes(sourceTag, settings);
|
303 | });
|
304 | setOriginalsObject(imgEl, attrsSrcSrcsetSizes);
|
305 | setImageAttributes(imgEl, settings);
|
306 | };
|
307 | const setSourcesIframe = (iframe, settings) => {
|
308 | setOriginalsObject(iframe, attrsSrc);
|
309 | setAttributeIfValue(iframe, SRC, getData(iframe, settings.data_src));
|
310 | };
|
311 | const setSourcesVideo = (videoEl, settings) => {
|
312 | forEachVideoSource(videoEl, sourceEl => {
|
313 | setOriginalsObject(sourceEl, attrsSrc);
|
314 | setAttributeIfValue(sourceEl, SRC, getData(sourceEl, settings.data_src));
|
315 | });
|
316 | setOriginalsObject(videoEl, attrsSrcPoster);
|
317 | setAttributeIfValue(videoEl, POSTER, getData(videoEl, settings.data_poster));
|
318 | setAttributeIfValue(videoEl, SRC, getData(videoEl, settings.data_src));
|
319 | videoEl.load();
|
320 | };
|
321 | const setSourcesObject = (object, settings) => {
|
322 | setOriginalsObject(object, attrsData);
|
323 | setAttributeIfValue(object, DATA, getData(object, settings.data_src));
|
324 | };
|
325 | const setBackground = (element, settings, instance) => {
|
326 | const bg1xValue = getData(element, settings.data_bg);
|
327 | const bgHiDpiValue = getData(element, settings.data_bg_hidpi);
|
328 | const bgDataValue = isHiDpi && bgHiDpiValue ? bgHiDpiValue : bg1xValue;
|
329 | if (!bgDataValue) return;
|
330 | element.style.backgroundImage = `url("${bgDataValue}")`;
|
331 | getTempImage(element).setAttribute(SRC, bgDataValue);
|
332 | manageLoading(element, settings, instance);
|
333 | };
|
334 |
|
335 |
|
336 |
|
337 |
|
338 | const setMultiBackground = (element, settings, instance) => {
|
339 | const bg1xValue = getData(element, settings.data_bg_multi);
|
340 | const bgHiDpiValue = getData(element, settings.data_bg_multi_hidpi);
|
341 | const bgDataValue = isHiDpi && bgHiDpiValue ? bgHiDpiValue : bg1xValue;
|
342 | if (!bgDataValue) {
|
343 | return;
|
344 | }
|
345 | element.style.backgroundImage = bgDataValue;
|
346 | manageApplied(element, settings, instance);
|
347 | };
|
348 | const setImgsetBackground = (element, settings, instance) => {
|
349 | const bgImgSetDataValue = getData(element, settings.data_bg_set);
|
350 | if (!bgImgSetDataValue) {
|
351 | return;
|
352 | }
|
353 | const imgSetValues = bgImgSetDataValue.split("|");
|
354 | let bgImageValues = imgSetValues.map(value => `image-set(${value})`);
|
355 | element.style.backgroundImage = bgImageValues.join();
|
356 | manageApplied(element, settings, instance);
|
357 | };
|
358 | const setSourcesFunctions = {
|
359 | IMG: setSourcesImg,
|
360 | IFRAME: setSourcesIframe,
|
361 | VIDEO: setSourcesVideo,
|
362 | OBJECT: setSourcesObject
|
363 | };
|
364 | const setSourcesNative = (element, settings) => {
|
365 | const setSourcesFunction = setSourcesFunctions[element.tagName];
|
366 | if (!setSourcesFunction) {
|
367 | return;
|
368 | }
|
369 | setSourcesFunction(element, settings);
|
370 | };
|
371 | const setSources = (element, settings, instance) => {
|
372 | const setSourcesFunction = setSourcesFunctions[element.tagName];
|
373 | if (!setSourcesFunction) {
|
374 | return;
|
375 | }
|
376 | setSourcesFunction(element, settings);
|
377 | manageLoading(element, settings, instance);
|
378 | };
|
379 |
|
380 | const elementsWithLoadEvent = ["IMG", "IFRAME", "VIDEO", "OBJECT"];
|
381 | const hasLoadEvent = element => elementsWithLoadEvent.indexOf(element.tagName) > -1;
|
382 | const checkFinish = (settings, instance) => {
|
383 | if (instance && !isSomethingLoading(instance) && !haveElementsToLoad(instance)) {
|
384 | safeCallback(settings.callback_finish, instance);
|
385 | }
|
386 | };
|
387 | const addEventListener = (element, eventName, handler) => {
|
388 | element.addEventListener(eventName, handler);
|
389 | element.llEvLisnrs[eventName] = handler;
|
390 | };
|
391 | const removeEventListener = (element, eventName, handler) => {
|
392 | element.removeEventListener(eventName, handler);
|
393 | };
|
394 | const hasEventListeners = element => {
|
395 | return !!element.llEvLisnrs;
|
396 | };
|
397 | const addEventListeners = (element, loadHandler, errorHandler) => {
|
398 | if (!hasEventListeners(element)) element.llEvLisnrs = {};
|
399 | const loadEventName = element.tagName === "VIDEO" ? "loadeddata" : "load";
|
400 | addEventListener(element, loadEventName, loadHandler);
|
401 | addEventListener(element, "error", errorHandler);
|
402 | };
|
403 | const removeEventListeners = element => {
|
404 | if (!hasEventListeners(element)) {
|
405 | return;
|
406 | }
|
407 | const eventListeners = element.llEvLisnrs;
|
408 | for (let eventName in eventListeners) {
|
409 | const handler = eventListeners[eventName];
|
410 | removeEventListener(element, eventName, handler);
|
411 | }
|
412 | delete element.llEvLisnrs;
|
413 | };
|
414 | const doneHandler = (element, settings, instance) => {
|
415 | deleteTempImage(element);
|
416 | updateLoadingCount(instance, -1);
|
417 | decreaseToLoadCount(instance);
|
418 | removeClass(element, settings.class_loading);
|
419 | if (settings.unobserve_completed) {
|
420 | unobserve(element, instance);
|
421 | }
|
422 | };
|
423 | const loadHandler = (event, element, settings, instance) => {
|
424 | const goingNative = hasStatusNative(element);
|
425 | doneHandler(element, settings, instance);
|
426 | addClass(element, settings.class_loaded);
|
427 | setStatus(element, statusLoaded);
|
428 | safeCallback(settings.callback_loaded, element, instance);
|
429 | if (!goingNative) checkFinish(settings, instance);
|
430 | };
|
431 | const errorHandler = (event, element, settings, instance) => {
|
432 | const goingNative = hasStatusNative(element);
|
433 | doneHandler(element, settings, instance);
|
434 | addClass(element, settings.class_error);
|
435 | setStatus(element, statusError);
|
436 | safeCallback(settings.callback_error, element, instance);
|
437 | if (settings.restore_on_error) restoreOriginalAttrs(element, attrsSrcSrcsetSizes);
|
438 | if (!goingNative) checkFinish(settings, instance);
|
439 | };
|
440 | const addOneShotEventListeners = (element, settings, instance) => {
|
441 | const elementToListenTo = getTempImage(element) || element;
|
442 | if (hasEventListeners(elementToListenTo)) {
|
443 |
|
444 | return;
|
445 | }
|
446 | const _loadHandler = event => {
|
447 | loadHandler(event, element, settings, instance);
|
448 | removeEventListeners(elementToListenTo);
|
449 | };
|
450 | const _errorHandler = event => {
|
451 | errorHandler(event, element, settings, instance);
|
452 | removeEventListeners(elementToListenTo);
|
453 | };
|
454 | addEventListeners(elementToListenTo, _loadHandler, _errorHandler);
|
455 | };
|
456 |
|
457 | const loadBackground = (element, settings, instance) => {
|
458 | addTempImage(element);
|
459 | addOneShotEventListeners(element, settings, instance);
|
460 | saveOriginalBackgroundStyle(element);
|
461 | setBackground(element, settings, instance);
|
462 | setMultiBackground(element, settings, instance);
|
463 | setImgsetBackground(element, settings, instance);
|
464 | };
|
465 | const loadRegular = (element, settings, instance) => {
|
466 | addOneShotEventListeners(element, settings, instance);
|
467 | setSources(element, settings, instance);
|
468 | };
|
469 | const load = (element, settings, instance) => {
|
470 | if (hasLoadEvent(element)) {
|
471 | loadRegular(element, settings, instance);
|
472 | } else {
|
473 | loadBackground(element, settings, instance);
|
474 | }
|
475 | };
|
476 | const loadNative = (element, settings, instance) => {
|
477 | element.setAttribute("loading", "lazy");
|
478 | addOneShotEventListeners(element, settings, instance);
|
479 | setSourcesNative(element, settings);
|
480 | setStatus(element, statusNative);
|
481 | };
|
482 |
|
483 | const removeImageAttributes = element => {
|
484 | element.removeAttribute(SRC);
|
485 | element.removeAttribute(SRCSET);
|
486 | element.removeAttribute(SIZES);
|
487 | };
|
488 | const resetSourcesImg = element => {
|
489 | forEachPictureSource(element, sourceTag => {
|
490 | removeImageAttributes(sourceTag);
|
491 | });
|
492 | removeImageAttributes(element);
|
493 | };
|
494 |
|
495 | const restoreImg = imgEl => {
|
496 | forEachPictureSource(imgEl, sourceEl => {
|
497 | restoreOriginalAttrs(sourceEl, attrsSrcSrcsetSizes);
|
498 | });
|
499 | restoreOriginalAttrs(imgEl, attrsSrcSrcsetSizes);
|
500 | };
|
501 | const restoreVideo = videoEl => {
|
502 | forEachVideoSource(videoEl, sourceEl => {
|
503 | restoreOriginalAttrs(sourceEl, attrsSrc);
|
504 | });
|
505 | restoreOriginalAttrs(videoEl, attrsSrcPoster);
|
506 | videoEl.load();
|
507 | };
|
508 | const restoreIframe = iframeEl => {
|
509 | restoreOriginalAttrs(iframeEl, attrsSrc);
|
510 | };
|
511 | const restoreObject = objectEl => {
|
512 | restoreOriginalAttrs(objectEl, attrsData);
|
513 | };
|
514 | const restoreFunctions = {
|
515 | IMG: restoreImg,
|
516 | IFRAME: restoreIframe,
|
517 | VIDEO: restoreVideo,
|
518 | OBJECT: restoreObject
|
519 | };
|
520 | const restoreAttributes = element => {
|
521 | const restoreFunction = restoreFunctions[element.tagName];
|
522 | if (!restoreFunction) {
|
523 | restoreOriginalBgImage(element);
|
524 | return;
|
525 | }
|
526 | restoreFunction(element);
|
527 | };
|
528 | const resetClasses = (element, settings) => {
|
529 | if (hasEmptyStatus(element) || hasStatusNative(element)) {
|
530 | return;
|
531 | }
|
532 | removeClass(element, settings.class_entered);
|
533 | removeClass(element, settings.class_exited);
|
534 | removeClass(element, settings.class_applied);
|
535 | removeClass(element, settings.class_loading);
|
536 | removeClass(element, settings.class_loaded);
|
537 | removeClass(element, settings.class_error);
|
538 | };
|
539 | const restore = (element, settings) => {
|
540 | restoreAttributes(element);
|
541 | resetClasses(element, settings);
|
542 | resetStatus(element);
|
543 | deleteOriginalAttrs(element);
|
544 | };
|
545 |
|
546 | const cancelLoading = (element, entry, settings, instance) => {
|
547 | if (!settings.cancel_on_exit) return;
|
548 | if (!hasStatusLoading(element)) return;
|
549 | if (element.tagName !== "IMG") return;
|
550 | removeEventListeners(element);
|
551 | resetSourcesImg(element);
|
552 | restoreImg(element);
|
553 | removeClass(element, settings.class_loading);
|
554 | updateLoadingCount(instance, -1);
|
555 | resetStatus(element);
|
556 | safeCallback(settings.callback_cancel, element, entry, instance);
|
557 | };
|
558 |
|
559 | const onEnter = (element, entry, settings, instance) => {
|
560 | const dontLoad = hadStartedLoading(element); |
561 |
|
562 | setStatus(element, statusEntered);
|
563 | addClass(element, settings.class_entered);
|
564 | removeClass(element, settings.class_exited);
|
565 | unobserveEntered(element, settings, instance);
|
566 | safeCallback(settings.callback_enter, element, entry, instance);
|
567 | if (dontLoad) return;
|
568 | load(element, settings, instance);
|
569 | };
|
570 | const onExit = (element, entry, settings, instance) => {
|
571 | if (hasEmptyStatus(element)) return;
|
572 | addClass(element, settings.class_exited);
|
573 | cancelLoading(element, entry, settings, instance);
|
574 | safeCallback(settings.callback_exit, element, entry, instance);
|
575 | };
|
576 |
|
577 | const tagsWithNativeLazy = ["IMG", "IFRAME", "VIDEO"];
|
578 | const shouldUseNative = settings => settings.use_native && "loading" in HTMLImageElement.prototype;
|
579 | const loadAllNative = (elements, settings, instance) => {
|
580 | elements.forEach(element => {
|
581 | if (tagsWithNativeLazy.indexOf(element.tagName) === -1) {
|
582 | return;
|
583 | }
|
584 | loadNative(element, settings, instance);
|
585 | });
|
586 | setToLoadCount(instance, 0);
|
587 | };
|
588 |
|
589 | const isIntersecting = entry => entry.isIntersecting || entry.intersectionRatio > 0;
|
590 | const getObserverSettings = settings => ({
|
591 | root: settings.container === document ? null : settings.container,
|
592 | rootMargin: settings.thresholds || settings.threshold + "px"
|
593 | });
|
594 | const intersectionHandler = (entries, settings, instance) => {
|
595 | entries.forEach(entry => isIntersecting(entry) ? onEnter(entry.target, entry, settings, instance) : onExit(entry.target, entry, settings, instance));
|
596 | };
|
597 | const observeElements = (observer, elements) => {
|
598 | elements.forEach(element => {
|
599 | observer.observe(element);
|
600 | });
|
601 | };
|
602 | const updateObserver = (observer, elementsToObserve) => {
|
603 | resetObserver(observer);
|
604 | observeElements(observer, elementsToObserve);
|
605 | };
|
606 | const setObserver = (settings, instance) => {
|
607 | if (shouldUseNative(settings)) {
|
608 | return;
|
609 | }
|
610 | instance._observer = new IntersectionObserver(entries => {
|
611 | intersectionHandler(entries, settings, instance);
|
612 | }, getObserverSettings(settings));
|
613 | };
|
614 |
|
615 | const toArray = nodeSet => Array.prototype.slice.call(nodeSet);
|
616 | const queryElements = settings => settings.container.querySelectorAll(settings.elements_selector);
|
617 | const excludeManagedElements = elements => toArray(elements).filter(hasEmptyStatus);
|
618 | const hasError = element => hasStatusError(element);
|
619 | const filterErrorElements = elements => toArray(elements).filter(hasError);
|
620 | const getElementsToLoad = (elements, settings) => excludeManagedElements(elements || queryElements(settings));
|
621 |
|
622 | const retryLazyLoad = (settings, instance) => {
|
623 | const errorElements = filterErrorElements(queryElements(settings));
|
624 | errorElements.forEach(element => {
|
625 | removeClass(element, settings.class_error);
|
626 | resetStatus(element);
|
627 | });
|
628 | instance.update();
|
629 | };
|
630 | const setOnlineCheck = (settings, instance) => {
|
631 | if (!runningOnBrowser) {
|
632 | return;
|
633 | }
|
634 | instance._onlineHandler = () => {
|
635 | retryLazyLoad(settings, instance);
|
636 | };
|
637 | window.addEventListener("online", instance._onlineHandler);
|
638 | };
|
639 | const resetOnlineCheck = instance => {
|
640 | if (!runningOnBrowser) {
|
641 | return;
|
642 | }
|
643 | window.removeEventListener("online", instance._onlineHandler);
|
644 | };
|
645 |
|
646 | const LazyLoad = function (customSettings, elements) {
|
647 | const settings = getExtendedSettings(customSettings);
|
648 | this._settings = settings;
|
649 | this.loadingCount = 0;
|
650 | setObserver(settings, this);
|
651 | setOnlineCheck(settings, this);
|
652 | this.update(elements);
|
653 | };
|
654 | LazyLoad.prototype = {
|
655 | update: function (givenNodeset) {
|
656 | const settings = this._settings;
|
657 | const elementsToLoad = getElementsToLoad(givenNodeset, settings);
|
658 | setToLoadCount(this, elementsToLoad.length);
|
659 | if (isBot) {
|
660 | this.loadAll(elementsToLoad);
|
661 | return;
|
662 | }
|
663 | if (shouldUseNative(settings)) {
|
664 | loadAllNative(elementsToLoad, settings, this);
|
665 | return;
|
666 | }
|
667 | updateObserver(this._observer, elementsToLoad);
|
668 | },
|
669 | destroy: function () {
|
670 |
|
671 | if (this._observer) {
|
672 | this._observer.disconnect();
|
673 | }
|
674 |
|
675 | resetOnlineCheck(this);
|
676 |
|
677 | queryElements(this._settings).forEach(element => {
|
678 | deleteOriginalAttrs(element);
|
679 | });
|
680 |
|
681 | delete this._observer;
|
682 | delete this._settings;
|
683 | delete this._onlineHandler;
|
684 | delete this.loadingCount;
|
685 | delete this.toLoadCount;
|
686 | },
|
687 | loadAll: function (elements) {
|
688 | const settings = this._settings;
|
689 | const elementsToLoad = getElementsToLoad(elements, settings);
|
690 | elementsToLoad.forEach(element => {
|
691 | unobserve(element, this);
|
692 | load(element, settings, this);
|
693 | });
|
694 | },
|
695 | restoreAll: function () {
|
696 | const settings = this._settings;
|
697 | queryElements(settings).forEach(element => {
|
698 | restore(element, settings);
|
699 | });
|
700 | }
|
701 | };
|
702 | LazyLoad.load = (element, customSettings) => {
|
703 | const settings = getExtendedSettings(customSettings);
|
704 | load(element, settings);
|
705 | };
|
706 | LazyLoad.resetStatus = element => {
|
707 | resetStatus(element);
|
708 | };
|
709 |
|
710 |
|
711 | if (runningOnBrowser) {
|
712 | autoInitialize(LazyLoad, window.lazyLoadOptions);
|
713 | }
|
714 |
|
715 | return LazyLoad;
|
716 |
|
717 | })();
|