lujanfernaud/prevy

View on GitHub
public/assets/lazysizes-0622dd876a33cdbe06d2c1c01c3e3b9a8ca531f3c3c3ec27639622d535ceeec0.js

Summary

Maintainability
F
1 wk
Test Coverage
(function(window, factory) {
    var lazySizes = factory(window, window.document);
    window.lazySizes = lazySizes;
    if(typeof module == 'object' && module.exports){
        module.exports = lazySizes;
    }
}(window, function l(window, document) {
    'use strict';
    /*jshint eqnull:true */
    if(!document.getElementsByClassName){return;}

    var lazysizes, lazySizesConfig;

    var docElem = document.documentElement;

    var Date = window.Date;

    var supportPicture = window.HTMLPictureElement;

    var _addEventListener = 'addEventListener';

    var _getAttribute = 'getAttribute';

    var addEventListener = window[_addEventListener];

    var setTimeout = window.setTimeout;

    var requestAnimationFrame = window.requestAnimationFrame || setTimeout;

    var requestIdleCallback = window.requestIdleCallback;

    var regPicture = /^picture$/i;

    var loadEvents = ['load', 'error', 'lazyincluded', '_lazyloaded'];

    var regClassCache = {};

    var forEach = Array.prototype.forEach;

    var hasClass = function(ele, cls) {
        if(!regClassCache[cls]){
            regClassCache[cls] = new RegExp('(\\s|^)'+cls+'(\\s|$)');
        }
        return regClassCache[cls].test(ele[_getAttribute]('class') || '') && regClassCache[cls];
    };

    var addClass = function(ele, cls) {
        if (!hasClass(ele, cls)){
            ele.setAttribute('class', (ele[_getAttribute]('class') || '').trim() + ' ' + cls);
        }
    };

    var removeClass = function(ele, cls) {
        var reg;
        if ((reg = hasClass(ele,cls))) {
            ele.setAttribute('class', (ele[_getAttribute]('class') || '').replace(reg, ' '));
        }
    };

    var addRemoveLoadEvents = function(dom, fn, add){
        var action = add ? _addEventListener : 'removeEventListener';
        if(add){
            addRemoveLoadEvents(dom, fn);
        }
        loadEvents.forEach(function(evt){
            dom[action](evt, fn);
        });
    };

    var triggerEvent = function(elem, name, detail, noBubbles, noCancelable){
        var event = document.createEvent('CustomEvent');

        if(!detail){
            detail = {};
        }

        detail.instance = lazysizes;

        event.initCustomEvent(name, !noBubbles, !noCancelable, detail);

        elem.dispatchEvent(event);
        return event;
    };

    var updatePolyfill = function (el, full){
        var polyfill;
        if( !supportPicture && ( polyfill = (window.picturefill || lazySizesConfig.pf) ) ){
            polyfill({reevaluate: true, elements: [el]});
        } else if(full && full.src){
            el.src = full.src;
        }
    };

    var getCSS = function (elem, style){
        return (getComputedStyle(elem, null) || {})[style];
    };

    var getWidth = function(elem, parent, width){
        width = width || elem.offsetWidth;

        while(width < lazySizesConfig.minSize && parent && !elem._lazysizesWidth){
            width =  parent.offsetWidth;
            parent = parent.parentNode;
        }

        return width;
    };

    var rAF = (function(){
        var running, waiting;
        var firstFns = [];
        var secondFns = [];
        var fns = firstFns;

        var run = function(){
            var runFns = fns;

            fns = firstFns.length ? secondFns : firstFns;

            running = true;
            waiting = false;

            while(runFns.length){
                runFns.shift()();
            }

            running = false;
        };

        var rafBatch = function(fn, queue){
            if(running && !queue){
                fn.apply(this, arguments);
            } else {
                fns.push(fn);

                if(!waiting){
                    waiting = true;
                    (document.hidden ? setTimeout : requestAnimationFrame)(run);
                }
            }
        };

        rafBatch._lsFlush = run;

        return rafBatch;
    })();

    var rAFIt = function(fn, simple){
        return simple ?
            function() {
                rAF(fn);
            } :
            function(){
                var that = this;
                var args = arguments;
                rAF(function(){
                    fn.apply(that, args);
                });
            }
        ;
    };

    var throttle = function(fn){
        var running;
        var lastTime = 0;
        var gDelay = lazySizesConfig.throttleDelay;
        var rICTimeout = lazySizesConfig.ricTimeout;
        var run = function(){
            running = false;
            lastTime = Date.now();
            fn();
        };
        var idleCallback = requestIdleCallback && rICTimeout > 49 ?
            function(){
                requestIdleCallback(run, {timeout: rICTimeout});

                if(rICTimeout !== lazySizesConfig.ricTimeout){
                    rICTimeout = lazySizesConfig.ricTimeout;
                }
            } :
            rAFIt(function(){
                setTimeout(run);
            }, true)
        ;

        return function(isPriority){
            var delay;

            if((isPriority = isPriority === true)){
                rICTimeout = 33;
            }

            if(running){
                return;
            }

            running =  true;

            delay = gDelay - (Date.now() - lastTime);

            if(delay < 0){
                delay = 0;
            }

            if(isPriority || delay < 9){
                idleCallback();
            } else {
                setTimeout(idleCallback, delay);
            }
        };
    };

    //based on http://modernjavascript.blogspot.de/2013/08/building-better-debounce.html
    var debounce = function(func) {
        var timeout, timestamp;
        var wait = 99;
        var run = function(){
            timeout = null;
            func();
        };
        var later = function() {
            var last = Date.now() - timestamp;

            if (last < wait) {
                setTimeout(later, wait - last);
            } else {
                (requestIdleCallback || run)(run);
            }
        };

        return function() {
            timestamp = Date.now();

            if (!timeout) {
                timeout = setTimeout(later, wait);
            }
        };
    };

    (function(){
        var prop;

        var lazySizesDefaults = {
            lazyClass: 'lazyload',
            loadedClass: 'lazyloaded',
            loadingClass: 'lazyloading',
            preloadClass: 'lazypreload',
            errorClass: 'lazyerror',
            //strictClass: 'lazystrict',
            autosizesClass: 'lazyautosizes',
            srcAttr: 'data-src',
            srcsetAttr: 'data-srcset',
            sizesAttr: 'data-sizes',
            //preloadAfterLoad: false,
            minSize: 40,
            customMedia: {},
            init: true,
            expFactor: 1.5,
            hFac: 0.8,
            loadMode: 2,
            loadHidden: true,
            ricTimeout: 0,
            throttleDelay: 125,
        };

        lazySizesConfig = window.lazySizesConfig || window.lazysizesConfig || {};

        for(prop in lazySizesDefaults){
            if(!(prop in lazySizesConfig)){
                lazySizesConfig[prop] = lazySizesDefaults[prop];
            }
        }

        window.lazySizesConfig = lazySizesConfig;

        setTimeout(function(){
            if(lazySizesConfig.init){
                init();
            }
        });
    })();

    var loader = (function(){
        var preloadElems, isCompleted, resetPreloadingTimer, loadMode, started;

        var eLvW, elvH, eLtop, eLleft, eLright, eLbottom;

        var defaultExpand, preloadExpand, hFac;

        var regImg = /^img$/i;
        var regIframe = /^iframe$/i;

        var supportScroll = ('onscroll' in window) && !(/glebot/.test(navigator.userAgent));

        var shrinkExpand = 0;
        var currentExpand = 0;

        var isLoading = 0;
        var lowRuns = -1;

        var resetPreloading = function(e){
            isLoading--;
            if(e && e.target){
                addRemoveLoadEvents(e.target, resetPreloading);
            }

            if(!e || isLoading < 0 || !e.target){
                isLoading = 0;
            }
        };

        var isNestedVisible = function(elem, elemExpand){
            var outerRect;
            var parent = elem;
            var visible = getCSS(document.body, 'visibility') == 'hidden' || getCSS(elem, 'visibility') != 'hidden';

            eLtop -= elemExpand;
            eLbottom += elemExpand;
            eLleft -= elemExpand;
            eLright += elemExpand;

            while(visible && (parent = parent.offsetParent) && parent != document.body && parent != docElem){
                visible = ((getCSS(parent, 'opacity') || 1) > 0);

                if(visible && getCSS(parent, 'overflow') != 'visible'){
                    outerRect = parent.getBoundingClientRect();
                    visible = eLright > outerRect.left &&
                        eLleft < outerRect.right &&
                        eLbottom > outerRect.top - 1 &&
                        eLtop < outerRect.bottom + 1
                    ;
                }
            }

            return visible;
        };

        var checkElements = function() {
            var eLlen, i, rect, autoLoadElem, loadedSomething, elemExpand, elemNegativeExpand, elemExpandVal, beforeExpandVal;

            var lazyloadElems = lazysizes.elements;

            if((loadMode = lazySizesConfig.loadMode) && isLoading < 8 && (eLlen = lazyloadElems.length)){

                i = 0;

                lowRuns++;

                if(preloadExpand == null){
                    if(!('expand' in lazySizesConfig)){
                        lazySizesConfig.expand = docElem.clientHeight > 500 && docElem.clientWidth > 500 ? 500 : 370;
                    }

                    defaultExpand = lazySizesConfig.expand;
                    preloadExpand = defaultExpand * lazySizesConfig.expFactor;
                }

                if(currentExpand < preloadExpand && isLoading < 1 && lowRuns > 2 && loadMode > 2 && !document.hidden){
                    currentExpand = preloadExpand;
                    lowRuns = 0;
                } else if(loadMode > 1 && lowRuns > 1 && isLoading < 6){
                    currentExpand = defaultExpand;
                } else {
                    currentExpand = shrinkExpand;
                }

                for(; i < eLlen; i++){

                    if(!lazyloadElems[i] || lazyloadElems[i]._lazyRace){continue;}

                    if(!supportScroll){unveilElement(lazyloadElems[i]);continue;}

                    if(!(elemExpandVal = lazyloadElems[i][_getAttribute]('data-expand')) || !(elemExpand = elemExpandVal * 1)){
                        elemExpand = currentExpand;
                    }

                    if(beforeExpandVal !== elemExpand){
                        eLvW = innerWidth + (elemExpand * hFac);
                        elvH = innerHeight + elemExpand;
                        elemNegativeExpand = elemExpand * -1;
                        beforeExpandVal = elemExpand;
                    }

                    rect = lazyloadElems[i].getBoundingClientRect();

                    if ((eLbottom = rect.bottom) >= elemNegativeExpand &&
                        (eLtop = rect.top) <= elvH &&
                        (eLright = rect.right) >= elemNegativeExpand * hFac &&
                        (eLleft = rect.left) <= eLvW &&
                        (eLbottom || eLright || eLleft || eLtop) &&
                        (lazySizesConfig.loadHidden || getCSS(lazyloadElems[i], 'visibility') != 'hidden') &&
                        ((isCompleted && isLoading < 3 && !elemExpandVal && (loadMode < 3 || lowRuns < 4)) || isNestedVisible(lazyloadElems[i], elemExpand))){
                        unveilElement(lazyloadElems[i]);
                        loadedSomething = true;
                        if(isLoading > 9){break;}
                    } else if(!loadedSomething && isCompleted && !autoLoadElem &&
                        isLoading < 4 && lowRuns < 4 && loadMode > 2 &&
                        (preloadElems[0] || lazySizesConfig.preloadAfterLoad) &&
                        (preloadElems[0] || (!elemExpandVal && ((eLbottom || eLright || eLleft || eLtop) || lazyloadElems[i][_getAttribute](lazySizesConfig.sizesAttr) != 'auto')))){
                        autoLoadElem = preloadElems[0] || lazyloadElems[i];
                    }
                }

                if(autoLoadElem && !loadedSomething){
                    unveilElement(autoLoadElem);
                }
            }
        };

        var throttledCheckElements = throttle(checkElements);

        var switchLoadingClass = function(e){
            addClass(e.target, lazySizesConfig.loadedClass);
            removeClass(e.target, lazySizesConfig.loadingClass);
            addRemoveLoadEvents(e.target, rafSwitchLoadingClass);
            triggerEvent(e.target, 'lazyloaded');
        };
        var rafedSwitchLoadingClass = rAFIt(switchLoadingClass);
        var rafSwitchLoadingClass = function(e){
            rafedSwitchLoadingClass({target: e.target});
        };

        var changeIframeSrc = function(elem, src){
            try {
                elem.contentWindow.location.replace(src);
            } catch(e){
                elem.src = src;
            }
        };

        var handleSources = function(source){
            var customMedia;

            var sourceSrcset = source[_getAttribute](lazySizesConfig.srcsetAttr);

            if( (customMedia = lazySizesConfig.customMedia[source[_getAttribute]('data-media') || source[_getAttribute]('media')]) ){
                source.setAttribute('media', customMedia);
            }

            if(sourceSrcset){
                source.setAttribute('srcset', sourceSrcset);
            }
        };

        var lazyUnveil = rAFIt(function (elem, detail, isAuto, sizes, isImg){
            var src, srcset, parent, isPicture, event, firesLoad;

            if(!(event = triggerEvent(elem, 'lazybeforeunveil', detail)).defaultPrevented){

                if(sizes){
                    if(isAuto){
                        addClass(elem, lazySizesConfig.autosizesClass);
                    } else {
                        elem.setAttribute('sizes', sizes);
                    }
                }

                srcset = elem[_getAttribute](lazySizesConfig.srcsetAttr);
                src = elem[_getAttribute](lazySizesConfig.srcAttr);

                if(isImg) {
                    parent = elem.parentNode;
                    isPicture = parent && regPicture.test(parent.nodeName || '');
                }

                firesLoad = detail.firesLoad || (('src' in elem) && (srcset || src || isPicture));

                event = {target: elem};

                if(firesLoad){
                    addRemoveLoadEvents(elem, resetPreloading, true);
                    clearTimeout(resetPreloadingTimer);
                    resetPreloadingTimer = setTimeout(resetPreloading, 2500);

                    addClass(elem, lazySizesConfig.loadingClass);
                    addRemoveLoadEvents(elem, rafSwitchLoadingClass, true);
                }

                if(isPicture){
                    forEach.call(parent.getElementsByTagName('source'), handleSources);
                }

                if(srcset){
                    elem.setAttribute('srcset', srcset);
                } else if(src && !isPicture){
                    if(regIframe.test(elem.nodeName)){
                        changeIframeSrc(elem, src);
                    } else {
                        elem.src = src;
                    }
                }

                if(isImg && (srcset || isPicture)){
                    updatePolyfill(elem, {src: src});
                }
            }

            if(elem._lazyRace){
                delete elem._lazyRace;
            }
            removeClass(elem, lazySizesConfig.lazyClass);

            rAF(function(){
                if( !firesLoad || (elem.complete && elem.naturalWidth > 1)){
                    if(firesLoad){
                        resetPreloading(event);
                    } else {
                        isLoading--;
                    }
                    switchLoadingClass(event);
                }
            }, true);
        });

        var unveilElement = function (elem){
            var detail;

            var isImg = regImg.test(elem.nodeName);

            //allow using sizes="auto", but don't use. it's invalid. Use data-sizes="auto" or a valid value for sizes instead (i.e.: sizes="80vw")
            var sizes = isImg && (elem[_getAttribute](lazySizesConfig.sizesAttr) || elem[_getAttribute]('sizes'));
            var isAuto = sizes == 'auto';

            if( (isAuto || !isCompleted) && isImg && (elem[_getAttribute]('src') || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesConfig.errorClass) && hasClass(elem, lazySizesConfig.lazyClass)){return;}

            detail = triggerEvent(elem, 'lazyunveilread').detail;

            if(isAuto){
                 autoSizer.updateElem(elem, true, elem.offsetWidth);
            }

            elem._lazyRace = true;
            isLoading++;

            lazyUnveil(elem, detail, isAuto, sizes, isImg);
        };

        var onload = function(){
            if(isCompleted){return;}
            if(Date.now() - started < 999){
                setTimeout(onload, 999);
                return;
            }
            var afterScroll = debounce(function(){
                lazySizesConfig.loadMode = 3;
                throttledCheckElements();
            });

            isCompleted = true;

            lazySizesConfig.loadMode = 3;

            throttledCheckElements();

            addEventListener('scroll', function(){
                if(lazySizesConfig.loadMode == 3){
                    lazySizesConfig.loadMode = 2;
                }
                afterScroll();
            }, true);
        };

        return {
            _: function(){
                started = Date.now();

                lazysizes.elements = document.getElementsByClassName(lazySizesConfig.lazyClass);
                preloadElems = document.getElementsByClassName(lazySizesConfig.lazyClass + ' ' + lazySizesConfig.preloadClass);
                hFac = lazySizesConfig.hFac;

                addEventListener('scroll', throttledCheckElements, true);

                addEventListener('resize', throttledCheckElements, true);

                if(window.MutationObserver){
                    new MutationObserver( throttledCheckElements ).observe( docElem, {childList: true, subtree: true, attributes: true} );
                } else {
                    docElem[_addEventListener]('DOMNodeInserted', throttledCheckElements, true);
                    docElem[_addEventListener]('DOMAttrModified', throttledCheckElements, true);
                    setInterval(throttledCheckElements, 999);
                }

                addEventListener('hashchange', throttledCheckElements, true);

                //, 'fullscreenchange'
                ['focus', 'mouseover', 'click', 'load', 'transitionend', 'animationend', 'webkitAnimationEnd'].forEach(function(name){
                    document[_addEventListener](name, throttledCheckElements, true);
                });

                if((/d$|^c/.test(document.readyState))){
                    onload();
                } else {
                    addEventListener('load', onload);
                    document[_addEventListener]('DOMContentLoaded', throttledCheckElements);
                    setTimeout(onload, 20000);
                }

                if(lazysizes.elements.length){
                    checkElements();
                    rAF._lsFlush();
                } else {
                    throttledCheckElements();
                }
            },
            checkElems: throttledCheckElements,
            unveil: unveilElement
        };
    })();


    var autoSizer = (function(){
        var autosizesElems;

        var sizeElement = rAFIt(function(elem, parent, event, width){
            var sources, i, len;
            elem._lazysizesWidth = width;
            width += 'px';

            elem.setAttribute('sizes', width);

            if(regPicture.test(parent.nodeName || '')){
                sources = parent.getElementsByTagName('source');
                for(i = 0, len = sources.length; i < len; i++){
                    sources[i].setAttribute('sizes', width);
                }
            }

            if(!event.detail.dataAttr){
                updatePolyfill(elem, event.detail);
            }
        });
        var getSizeElement = function (elem, dataAttr, width){
            var event;
            var parent = elem.parentNode;

            if(parent){
                width = getWidth(elem, parent, width);
                event = triggerEvent(elem, 'lazybeforesizes', {width: width, dataAttr: !!dataAttr});

                if(!event.defaultPrevented){
                    width = event.detail.width;

                    if(width && width !== elem._lazysizesWidth){
                        sizeElement(elem, parent, event, width);
                    }
                }
            }
        };

        var updateElementsSizes = function(){
            var i;
            var len = autosizesElems.length;
            if(len){
                i = 0;

                for(; i < len; i++){
                    getSizeElement(autosizesElems[i]);
                }
            }
        };

        var debouncedUpdateElementsSizes = debounce(updateElementsSizes);

        return {
            _: function(){
                autosizesElems = document.getElementsByClassName(lazySizesConfig.autosizesClass);
                addEventListener('resize', debouncedUpdateElementsSizes);
            },
            checkElems: debouncedUpdateElementsSizes,
            updateElem: getSizeElement
        };
    })();

    var init = function(){
        if(!init.i){
            init.i = true;
            autoSizer._();
            loader._();
        }
    };

    lazysizes = {
        cfg: lazySizesConfig,
        autoSizer: autoSizer,
        loader: loader,
        init: init,
        uP: updatePolyfill,
        aC: addClass,
        rC: removeClass,
        hC: hasClass,
        fire: triggerEvent,
        gW: getWidth,
        rAF: rAF,
    };

    return lazysizes;
}
));
/*
This plugin extends lazySizes to lazyLoad:
background images, videos/posters and scripts

Background-Image:
For background images, use data-bg attribute:
<div class="lazyload" data-bg="bg-img.jpg"></div>

 Video:
 For video/audio use data-poster and preload="none":
 <video class="lazyload" data-poster="poster.jpg" preload="none">
 <!-- sources -->
 </video>

 Scripts:
 For scripts use data-script:
 <div class="lazyload" data-script="module-name.js"></div>


 Script modules using require:
 For modules using require use data-require:
 <div class="lazyload" data-require="module-name"></div>
*/


(function(window, factory) {
    var globalInstall = function(){
        factory(window.lazySizes);
        window.removeEventListener('lazyunveilread', globalInstall, true);
    };

    factory = factory.bind(null, window, window.document);

    if(typeof module == 'object' && module.exports){
        factory(require('lazysizes'));
    } else if(window.lazySizes) {
        globalInstall();
    } else {
        window.addEventListener('lazyunveilread', globalInstall, true);
    }
}(window, function(window, document, lazySizes) {
    /*jshint eqnull:true */
    'use strict';
    var bgLoad, regBgUrlEscape;
    var uniqueUrls = {};

    if(document.addEventListener){
        regBgUrlEscape = /\(|\)|\s|'/;

        bgLoad = function (url, cb){
            var img = document.createElement('img');
            img.onload = function(){
                img.onload = null;
                img.onerror = null;
                img = null;
                cb();
            };
            img.onerror = img.onload;

            img.src = url;

            if(img && img.complete && img.onload){
                img.onload();
            }
        };

        addEventListener('lazybeforeunveil', function(e){
            if(e.detail.instance != lazySizes){return;}

            var tmp, load, bg, poster;
            if(!e.defaultPrevented) {

                if(e.target.preload == 'none'){
                    e.target.preload = 'auto';
                }

                tmp = e.target.getAttribute('data-link');
                if(tmp){
                    addStyleScript(tmp, true);
                }

                // handle data-script
                tmp = e.target.getAttribute('data-script');
                if(tmp){
                    addStyleScript(tmp);
                }

                // handle data-require
                tmp = e.target.getAttribute('data-require');
                if(tmp){
                    if(lazySizes.cfg.requireJs){
                        lazySizes.cfg.requireJs([tmp]);
                    } else {
                        addStyleScript(tmp);
                    }
                }

                // handle data-bg
                bg = e.target.getAttribute('data-bg');
                if (bg) {
                    e.detail.firesLoad = true;
                    load = function(){
                        e.target.style.backgroundImage = 'url(' + (regBgUrlEscape.test(bg) ? JSON.stringify(bg) : bg ) + ')';
                        e.detail.firesLoad = false;
                        lazySizes.fire(e.target, '_lazyloaded', {}, true, true);
                    };

                    bgLoad(bg, load);
                }

                // handle data-poster
                poster = e.target.getAttribute('data-poster');
                if(poster){
                    e.detail.firesLoad = true;
                    load = function(){
                        e.target.poster = poster;
                        e.detail.firesLoad = false;
                        lazySizes.fire(e.target, '_lazyloaded', {}, true, true);
                    };

                    bgLoad(poster, load);

                }
            }
        }, false);

    }

    function addStyleScript(src, style){
        if(uniqueUrls[src]){
            return;
        }
        var elem = document.createElement(style ? 'link' : 'script');
        var insertElem = document.getElementsByTagName('script')[0];

        if(style){
            elem.rel = 'stylesheet';
            elem.href = src;
        } else {
            elem.src = src;
        }
        uniqueUrls[src] = true;
        uniqueUrls[elem.src || elem.href] = true;
        insertElem.parentNode.insertBefore(elem, insertElem);
    }
}));



lazySizes.init();