rich-filemanager
Version:
Highly customizable open-source file manager
196 lines (171 loc) • 7.2 kB
JavaScript
import defaultSettings from "./lazyload.defaults";
import {isBot, callCallback} from "./lazyload.utils";
import isInsideViewport from "./lazyload.viewport";
import autoInitialize from "./lazyload.autoInitialize";
import setSources from "./lazyload.setSources";
/*
* Constructor
*/
const LazyLoad = function(instanceSettings) {
this._settings = Object.assign({}, defaultSettings, instanceSettings);
this._queryOriginNode = this._settings.container === window ? document : this._settings.container;
this._previousLoopTime = 0;
this._loopTimeout = null;
this._boundHandleScroll = this.handleScroll.bind(this);
this._isFirstLoop = true;
window.addEventListener("resize", this._boundHandleScroll);
this.update();
};
LazyLoad.prototype = {
/*
* Private methods
*/
_reveal: function (element) {
const settings = this._settings;
const errorCallback = function () {
/* As this method is asynchronous, it must be protected against external destroy() calls */
if (!settings) { return; }
element.removeEventListener("load", loadCallback);
element.removeEventListener("error", errorCallback);
element.classList.remove(settings.class_loading);
element.classList.add(settings.class_error);
callCallback(settings.callback_error, element);
};
const loadCallback = function () {
/* As this method is asynchronous, it must be protected against external destroy() calls */
if (!settings) { return; }
element.classList.remove(settings.class_loading);
element.classList.add(settings.class_loaded);
element.removeEventListener("load", loadCallback);
element.removeEventListener("error", errorCallback);
/* Calling LOAD callback */
callCallback(settings.callback_load, element);
};
if (element.tagName === "IMG" || element.tagName === "IFRAME") {
element.addEventListener("load", loadCallback);
element.addEventListener("error", errorCallback);
element.classList.add(settings.class_loading);
}
setSources(element, settings.data_srcset, settings.data_src);
/* Calling SET callback */
callCallback(settings.callback_set, element);
},
_loopThroughElements: function () {
const settings = this._settings,
elements = this._elements,
elementsLength = (!elements) ? 0 : elements.length;
let i,
processedIndexes = [],
firstLoop = this._isFirstLoop;
for (i = 0; i < elementsLength; i++) {
let element = elements[i];
/* If must skip_invisible and element is invisible, skip it */
if (settings.skip_invisible && (element.offsetParent === null)) {
continue;
}
if (isBot || isInsideViewport(element, settings.container, settings.threshold)) {
if (firstLoop) {
element.classList.add(settings.class_initial);
}
/* Start loading the image */
this._reveal(element);
/* Marking the element as processed. */
processedIndexes.push(i);
element.dataset.wasProcessed = true;
}
}
/* Removing processed elements from this._elements. */
while (processedIndexes.length) {
elements.splice(processedIndexes.pop(), 1);
/* Calling the end loop callback */
callCallback(settings.callback_processed, elements.length);
}
/* Stop listening to scroll event when 0 elements remains */
if (elementsLength === 0) {
this._stopScrollHandler();
}
/* Sets isFirstLoop to false */
if (firstLoop) {
this._isFirstLoop = false;
}
},
_purgeElements: function () {
const elements = this._elements,
elementsLength = elements.length;
let i,
elementsToPurge = [];
for (i = 0; i < elementsLength; i++) {
let element = elements[i];
/* If the element has already been processed, skip it */
if (element.dataset.wasProcessed) {
elementsToPurge.push(i);
}
}
/* Removing elements to purge from this._elements. */
while (elementsToPurge.length > 0) {
elements.splice(elementsToPurge.pop(), 1);
}
},
_startScrollHandler: function () {
if (!this._isHandlingScroll) {
this._isHandlingScroll = true;
this._settings.container.addEventListener("scroll", this._boundHandleScroll);
}
},
_stopScrollHandler: function () {
if (this._isHandlingScroll) {
this._isHandlingScroll = false;
this._settings.container.removeEventListener("scroll", this._boundHandleScroll);
}
},
/*
* Public methods
*/
handleScroll: function () {
const throttle = this._settings.throttle;
if (throttle !== 0) {
let now = Date.now();
let remainingTime = throttle - (now - this._previousLoopTime);
if (remainingTime <= 0 || remainingTime > throttle) {
if (this._loopTimeout) {
clearTimeout(this._loopTimeout);
this._loopTimeout = null;
}
this._previousLoopTime = now;
this._loopThroughElements();
} else if (!this._loopTimeout) {
this._loopTimeout = setTimeout(function () {
this._previousLoopTime = Date.now();
this._loopTimeout = null;
this._loopThroughElements();
}.bind(this), remainingTime);
}
} else {
this._loopThroughElements();
}
},
update: function () {
// Converts to array the nodeset obtained querying the DOM from _queryOriginNode with elements_selector
this._elements = Array.prototype.slice.call(this._queryOriginNode.querySelectorAll(this._settings.elements_selector));
this._purgeElements();
this._loopThroughElements();
this._startScrollHandler();
},
destroy: function () {
window.removeEventListener("resize", this._boundHandleScroll);
if (this._loopTimeout) {
clearTimeout(this._loopTimeout);
this._loopTimeout = null;
}
this._stopScrollHandler();
this._elements = null;
this._queryOriginNode = null;
this._settings = null;
}
}
/* Automatic instances creation if required (useful for async script loading!) */
let autoInitOptions = window.lazyLoadOptions;
if (autoInitOptions) {
autoInitialize(LazyLoad, autoInitOptions);
}
export default LazyLoad;