Advertisement
KzDrew

Modified jquery.lazy.js

Aug 13th, 2018
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
jQuery 32.16 KB | None | 0 0
  1. /*!
  2.  * jQuery & Zepto Lazy - v1.7.9
  3.  * http://jquery.eisbehr.de/lazy/
  4.  *
  5.  * Copyright 2012 - 2018, Daniel 'Eisbehr' Kern
  6.  *
  7.  * Dual licensed under the MIT and GPL-2.0 licenses:
  8.  * http://www.opensource.org/licenses/mit-license.php
  9.  * http://www.gnu.org/licenses/gpl-2.0.html
  10.  *
  11.  * $("img.lazy").lazy();
  12.  */
  13.  
  14. ;(function(window, undefined) {
  15.     "use strict";
  16.  
  17.     // noinspection JSUnresolvedVariable
  18.     /**
  19.      * library instance - here and not in construct to be shorter in minimization
  20.      * @return void
  21.      */
  22.     var $ = window.jQuery || window.Zepto,
  23.  
  24.     /**
  25.      * unique plugin instance id counter
  26.      * @type {number}
  27.      */
  28.     lazyInstanceId = 0,
  29.  
  30.     /**
  31.      * helper to register window load for jQuery 3
  32.      * @type {boolean}
  33.      */    
  34.     windowLoaded = false;
  35.  
  36.     /**
  37.      * make lazy available to jquery - and make it a bit more case-insensitive :)
  38.      * @access public
  39.      * @type {function}
  40.      * @param {object} settings
  41.      * @return {LazyPlugin}
  42.      */
  43.     $.fn.Lazy = $.fn.lazy = function(settings) {
  44.         return new LazyPlugin(this, settings);
  45.     };
  46.  
  47.     /**
  48.      * helper to add plugins to lazy prototype configuration
  49.      * @access public
  50.      * @type {function}
  51.      * @param {string|Array} names
  52.      * @param {string|Array|function} [elements]
  53.      * @param {function} loader
  54.      * @return void
  55.      */
  56.     $.Lazy = $.lazy = function(names, elements, loader) {
  57.         // make second parameter optional
  58.         if ($.isFunction(elements)) {
  59.             loader = elements;
  60.             elements = [];
  61.         }
  62.  
  63.         // exit here if parameter is not a callable function
  64.         if (!$.isFunction(loader)) {
  65.             return;
  66.         }
  67.  
  68.         // make parameters an array of names to be sure
  69.         names = $.isArray(names) ? names : [names];
  70.         elements = $.isArray(elements) ? elements : [elements];
  71.  
  72.         var config = LazyPlugin.prototype.config,
  73.             forced = config._f || (config._f = {});
  74.  
  75.         // add the loader plugin for every name
  76.         for (var i = 0, l = names.length; i < l; i++) {
  77.             if (config[names[i]] === undefined || $.isFunction(config[names[i]])) {
  78.                 config[names[i]] = loader;
  79.             }
  80.         }
  81.  
  82.         // add forced elements loader
  83.         for (var c = 0, a = elements.length; c < a; c++) {
  84.             forced[elements[c]] = names[0];
  85.         }
  86.     };
  87.  
  88.     /**
  89.      * contains all logic and the whole element handling
  90.      * is packed in a private function outside class to reduce memory usage, because it will not be created on every plugin instance
  91.      * @access private
  92.      * @type {function}
  93.      * @param {LazyPlugin} instance
  94.      * @param {object} config
  95.      * @param {object|Array} items
  96.      * @param {object} events
  97.      * @param {string} namespace
  98.      * @return void
  99.      */
  100.     function _executeLazy(instance, config, items, events, namespace) {
  101.         /**
  102.          * a helper to trigger the 'onFinishedAll' callback after all other events
  103.          * @access private
  104.          * @type {number}
  105.          */
  106.         var _awaitingAfterLoad = 0,
  107.  
  108.         /**
  109.          * visible content width
  110.          * @access private
  111.          * @type {number}
  112.          */
  113.         _actualWidth = -1,
  114.  
  115.         /**
  116.          * visible content height
  117.          * @access private
  118.          * @type {number}
  119.          */
  120.         _actualHeight = -1,
  121.  
  122.         /**
  123.          * determine possibly detected high pixel density
  124.          * @access private
  125.          * @type {boolean}
  126.          */
  127.         _isRetinaDisplay = false,
  128.  
  129.         /**
  130.          * dictionary entry for better minimization
  131.          * @access private
  132.          * @type {string}
  133.          */
  134.         _afterLoad = 'afterLoad',
  135.  
  136.         /**
  137.          * dictionary entry for better minimization
  138.          * @access private
  139.          * @type {string}
  140.          */
  141.         _load = 'load',
  142.  
  143.         /**
  144.          * dictionary entry for better minimization
  145.          * @access private
  146.          * @type {string}
  147.          */
  148.         _error = 'error',
  149.  
  150.         /**
  151.          * dictionary entry for better minimization
  152.          * @access private
  153.          * @type {string}
  154.          */
  155.         _img = 'img',
  156.  
  157.         /**
  158.          * dictionary entry for better minimization
  159.          * @access private
  160.          * @type {string}
  161.          */
  162.         _src = 'src',
  163.  
  164.         /**
  165.          * dictionary entry for better minimization
  166.          * @access private
  167.          * @type {string}
  168.          */
  169.         _srcset = 'srcset',
  170.  
  171.         /**
  172.          * dictionary entry for better minimization
  173.          * @access private
  174.          * @type {string}
  175.          */
  176.         _sizes = 'sizes',
  177.  
  178.         /**
  179.          * dictionary entry for better minimization
  180.          * @access private
  181.          * @type {string}
  182.          */
  183.         _backgroundImage = 'background-image';
  184.  
  185.         /**
  186.          * initialize plugin
  187.          * bind loading to events or set delay time to load all items at once
  188.          * @access private
  189.          * @return void
  190.          */
  191.         function _initialize() {
  192.  
  193.             // detect actual device pixel ratio
  194.             // noinspection JSUnresolvedVariable
  195.             _isRetinaDisplay = window.devicePixelRatio > 1;
  196.  
  197.             // prepare all initial items
  198.             items = _prepareItems(items);            
  199.  
  200.             // if delay time is set load all items at once after delay time
  201.             if (config.delay >= 0) {
  202.                 setTimeout(function() {
  203.                     _lazyLoadItems(true);
  204.                 }, config.delay);
  205.             }
  206.  
  207.             // if no delay is set or combine usage is active bind events
  208.             if (config.delay < 0 || config.combined) {
  209.                 // create unique event function
  210.                 events.e = _throttle(config.throttle, function(event) {
  211.                     // reset detected window size on resize event
  212.                     if (event.type === 'resize') {
  213.                         _actualWidth = _actualHeight = -1;
  214.                     }                    
  215.                     // execute 'lazy magic'
  216.                     _lazyLoadItems(event.all);
  217.                 });
  218.  
  219.                 // create function to add new items to instance
  220.                 events.a = function(additionalItems) {
  221.                     additionalItems = _prepareItems(additionalItems);
  222.                     items.push.apply(items, additionalItems);
  223.                 };
  224.  
  225.                 // create function to get all instance items left
  226.                 events.g = function() {
  227.                     // filter loaded items before return in case internal filter was not running until now
  228.                     return (items = $(items).filter(function() {
  229.                         return !$(this).data(config.loadedName);
  230.                     }));
  231.                 };
  232.  
  233.                 // create function to force loading elements
  234.                 events.f = function(forcedItems) {
  235.                     for (var i = 0; i < forcedItems.length; i++) {
  236.                         // only handle item if available in current instance
  237.                         // use a compare function, because Zepto can't handle object parameter for filter
  238.                         // var item = items.filter(forcedItems[i]);
  239.                         /* jshint loopfunc: true */
  240.                         var item = items.filter(function() {
  241.                             return this === forcedItems[i];
  242.                         });
  243.  
  244.                         if (item.length) {
  245.                             _lazyLoadItems(false, item);  
  246.                         }
  247.                     }
  248.                 };
  249.  
  250.                 // load initial items
  251.                 _lazyLoadItems();
  252.  
  253.                 // bind lazy load functions to scroll and resize event
  254.                 // noinspection JSUnresolvedVariable                
  255.                 $(config.appendScroll).on('scroll.' + namespace + ' resize.' + namespace, events.e);
  256.             }
  257.         }
  258.  
  259.         /**
  260.          * prepare items before handle them
  261.          * @access private
  262.          * @param {Array|object|jQuery} items
  263.          * @return {Array|object|jQuery}
  264.          */
  265.         function _prepareItems(items) {
  266.             // fetch used configurations before loops
  267.             var defaultImage = config.defaultImage,
  268.                 placeholder = config.placeholder,
  269.                 imageBase = config.imageBase,
  270.                 srcsetAttribute = config.srcsetAttribute,
  271.                 loaderAttribute = config.loaderAttribute,
  272.                 forcedTags = config._f || {};
  273.  
  274.             // filter items and only add those who not handled yet and got needed attributes available
  275.             items = $(items).filter(function() {
  276.                 var element = $(this),
  277.                     tag = _getElementTagName(this);
  278.                 return !element.data(config.handledName) &&                        
  279.                    (element.data(config.attribute) || element.data(srcsetAttribute) || element.data(loaderAttribute) || forcedTags[tag] !== undefined);
  280.                     //(element.attr(config.attribute) || element.attr(srcsetAttribute) || element.attr(loaderAttribute) || forcedTags[tag] !== undefined);
  281.             })
  282.  
  283.            
  284.  
  285.             // append plugin instance to all elements
  286.             .data('plugin_' + config.name, instance);
  287.  
  288.             for (var i = 0, l = items.length; i < l; i++) {
  289.                 var element = $(items[i]),
  290.                     tag = _getElementTagName(items[i]),
  291.                     elementImageBase = element.data(config.imageBaseAttribute) || imageBase;
  292.                     //elementImageBase = element.attr(config.imageBaseAttribute) || imageBase;
  293.  
  294.                 // generate and update source set if an image base is set
  295.                 if (tag === _img && elementImageBase && element.attr(srcsetAttribute)) {
  296.                     element.data(srcsetAttribute, _getCorrectedSrcSet(element.data(srcsetAttribute), elementImageBase));
  297.                     //element.attr(srcsetAttribute, _getCorrectedSrcSet(element.attr(srcsetAttribute), elementImageBase));
  298.                 }
  299.  
  300.                 // add loader to forced element types
  301.                 if (forcedTags[tag] !== undefined && !element.attr(loaderAttribute)) {
  302.                     element.data(loaderAttribute, forcedTags[tag]);
  303.                     //element.attr(loaderAttribute, forcedTags[tag]);
  304.                 }
  305.  
  306.                 // set default image on every element without source
  307.                 if (tag === _img && defaultImage && !element.attr(_src)) {
  308.                     element.attr(_src, defaultImage);
  309.                 }
  310.  
  311.                 // set placeholder on every element without background image
  312.                 else if (tag !== _img && placeholder && (!element.css(_backgroundImage) || element.css(_backgroundImage) === 'none')) {
  313.                     element.css(_backgroundImage, "url('" + placeholder + "')");
  314.                 }
  315.             }
  316.  
  317.             return items;
  318.         }
  319.  
  320.         /**
  321.          * the 'lazy magic' - check all items
  322.          * @access private
  323.          * @param {boolean} [allItems]
  324.          * @param {object} [forced]
  325.          * @return void
  326.          */
  327.         function _lazyLoadItems(allItems, forced) {            
  328.             // skip if no items where left
  329.             if (!items.length) {
  330.                 // destroy instance if option is enabled
  331.                 if (config.autoDestroy) {
  332.                     // noinspection JSUnresolvedFunction
  333.                     instance.destroy();
  334.                 }
  335.  
  336.                 return;
  337.             }
  338.  
  339.             var elements = forced || items,                
  340.                 loadTriggered = false,
  341.                 imageBase = config.imageBase || '',
  342.                 srcsetAttribute = config.srcsetAttribute,
  343.                 handledName = config.handledName;            
  344.  
  345.             // loop all available items
  346.             for (var i = 0; i < elements.length; i++) {
  347.                 // item is at least in loadable area
  348.                 if (allItems || forced || _isInLoadableArea(elements[i])) {
  349.                     var element = $(elements[i]),
  350.                         tag = _getElementTagName(elements[i]),
  351.                         attribute = element.data(config.attribute),
  352.                         //attribute = element.attr(config.attribute),                        
  353.                         elementImageBase = element.data(config.imageBaseAttribute) || imageBase,
  354.                         //elementImageBase = element.attr(config.imageBaseAttribute) || imageBase,
  355.                         customLoader = element.data(config.loaderAttribute);
  356.                         //customLoader = element.attr(config.loaderAttribute);
  357.  
  358.                         // is not already handled
  359.                     if (!element.data(handledName) &&
  360.                         // and is visible or visibility doesn't matter
  361.                         (!config.visibleOnly || element.is(':visible')) && (
  362.                         // and image source or source set attribute is available
  363.                         (attribute || element.data(srcsetAttribute)) && (
  364.                         //(attribute || element.attr(srcsetAttribute)) && (
  365.                             // and is image tag where attribute is not equal source or source set
  366.                             (tag === _img && (elementImageBase + attribute !== element.attr(_src) || element.attr(srcsetAttribute) !== element.attr(_srcset))) ||
  367.                             // or is non image tag where attribute is not equal background
  368.                             (tag !== _img && elementImageBase + attribute !== element.css(_backgroundImage))
  369.                         ) ||
  370.                         // or custom loader is available
  371.                         customLoader))
  372.                     {
  373.                         // mark element always as handled as this point to prevent double handling                        
  374.                         loadTriggered = true;
  375.                         element.data(handledName, true);
  376.  
  377.                         // load item
  378.                         _handleItem(element, tag, elementImageBase, customLoader);
  379.                     }
  380.                 }
  381.             }
  382.  
  383.             // when something was loaded remove them from remaining items
  384.             if (loadTriggered) {
  385.                 items = $(items).filter(function() {
  386.                     return !$(this).data(handledName);
  387.                 });
  388.             }
  389.         }
  390.  
  391.         /**
  392.          * load the given element the lazy way
  393.          * @access private
  394.          * @param {object} element
  395.          * @param {string} tag
  396.          * @param {string} imageBase
  397.          * @param {function} [customLoader]
  398.          * @return void
  399.          */
  400.         function _handleItem(element, tag, imageBase, customLoader) {
  401.             // increment count of items waiting for after load
  402.             ++_awaitingAfterLoad;
  403.  
  404.             // extended error callback for correct 'onFinishedAll' handling
  405.             var errorCallback = function() {
  406.                 _triggerCallback('onError', element);
  407.                 _reduceAwaiting();
  408.  
  409.                 // prevent further callback calls
  410.                 errorCallback = $.noop;
  411.             };
  412.  
  413.             // trigger function before loading image
  414.             _triggerCallback('beforeLoad', element);
  415.  
  416.             // fetch all double used data here for better code minimization
  417.             var srcAttribute = config.attribute,
  418.                 srcsetAttribute = config.srcsetAttribute,
  419.                 sizesAttribute = config.sizesAttribute,
  420.                 retinaAttribute = config.retinaAttribute,
  421.                 removeAttribute = config.removeAttribute,
  422.                 loadedName = config.loadedName,
  423.                 elementRetina = element.data(retinaAttribute);
  424.                 //elementRetina = element.attr(retinaAttribute);
  425.  
  426.             // handle custom loader
  427.             if (customLoader) {
  428.                 // on load callback
  429.                 var loadCallback = function() {
  430.                     // remove attribute from element
  431.                     if (removeAttribute) {
  432.                         element.removeAttr(config.loaderAttribute);
  433.                     }
  434.  
  435.                     // mark element as loaded
  436.                     element.data(loadedName, true);
  437.  
  438.                     // call after load event
  439.                     _triggerCallback(_afterLoad, element);
  440.  
  441.                     // remove item from waiting queue and possibly trigger finished event
  442.                     // it's needed to be asynchronous to run after filter was in _lazyLoadItems
  443.                     setTimeout(_reduceAwaiting, 1);
  444.  
  445.                     // prevent further callback calls
  446.                     loadCallback = $.noop;
  447.                 };
  448.  
  449.                 // bind error event to trigger callback and reduce waiting amount
  450.                 element.off(_error).one(_error, errorCallback)
  451.  
  452.                 // bind after load callback to element
  453.                 .one(_load, loadCallback);
  454.  
  455.                 // trigger custom loader and handle response
  456.                 if (!_triggerCallback(customLoader, element, function(response) {
  457.                     if(response) {
  458.                         element.off(_load);
  459.                         loadCallback();
  460.                     }
  461.                     else {
  462.                         element.off(_error);
  463.                         errorCallback();
  464.                     }
  465.                 })) {
  466.                     element.trigger(_error);
  467.                 }
  468.             }
  469.  
  470.             // handle images
  471.             else {
  472.                 // create image object
  473.                 var imageObj = $(new Image());
  474.  
  475.                 // bind error event to trigger callback and reduce waiting amount
  476.                 imageObj.one(_error, errorCallback)
  477.  
  478.                 // bind after load callback to image
  479.                 .one(_load, function() {
  480.                     // remove element from view
  481.                     element.hide();
  482.  
  483.                     // set image back to element
  484.                     // do it as single 'attr' calls, to be sure 'src' is set after 'srcset'
  485.                     if (tag === _img) {
  486.                         element.attr(_sizes, imageObj.attr(_sizes))
  487.                                .attr(_srcset, imageObj.attr(_srcset))
  488.                                .attr(_src, imageObj.attr(_src));
  489.                     }
  490.                     else {
  491.                         element.css(_backgroundImage, "url('" + imageObj.attr(_src) + "')");
  492.                     }
  493.  
  494.                     // bring it back with some effect!
  495.                     element[config.effect](config.effectTime);
  496.  
  497.                     // remove attribute from element
  498.                     if (removeAttribute) {
  499.                         element.removeAttr('data-' + srcAttribute + ' data-' + srcsetAttribute + ' data-' + retinaAttribute + ' data-' + config.imageBaseAttribute);
  500.  
  501.                         // only remove 'sizes' attribute, if it was a custom one
  502.                         if (sizesAttribute !== _sizes) {
  503.                             element.removeAttr('data-' + sizesAttribute);
  504.                         }
  505.                     }
  506.  
  507.                     // mark element as loaded
  508.                     element.data(loadedName, true);
  509.  
  510.                     // call after load event
  511.                     _triggerCallback(_afterLoad, element);
  512.  
  513.                     // cleanup image object
  514.                     imageObj.remove();
  515.  
  516.                     // remove item from waiting queue and possibly trigger finished event
  517.                     _reduceAwaiting();
  518.                 });
  519.  
  520.                 // set sources
  521.                 // do it as single 'attr' calls, to be sure 'src' is set after 'srcset'
  522.                 var imageSrc = (_isRetinaDisplay && elementRetina ? elementRetina : element.data(srcAttribute)) || '';
  523.                 //var imageSrc = (_isRetinaDisplay && elementRetina ? elementRetina : element.attr(srcAttribute)) || '';
  524.                 imageObj.attr(_sizes, element.attr(sizesAttribute))
  525.                         .attr(_srcset, element.attr(srcsetAttribute))
  526.                         .attr(_src, imageSrc ? imageBase + imageSrc : null);
  527.  
  528.                 // call after load even on cached image
  529.                 imageObj.complete && imageObj.trigger(_load); // jshint ignore : line
  530.             }
  531.         }
  532.  
  533.         /**
  534.          * check if the given element is inside the current viewport or threshold
  535.          * @access private
  536.          * @param {object} element
  537.          * @return {boolean}
  538.          */
  539.         function _isInLoadableArea(element) {            
  540.             var elementBound = element.getBoundingClientRect(),
  541.                 direction    = config.scrollDirection,
  542.                 threshold    = config.threshold,
  543.                 vertical     = // check if element is in loadable area from top
  544.                                ((_getActualHeight() + threshold) > elementBound.top) &&
  545.                                // check if element is even in loadable are from bottom
  546.                                (-threshold < elementBound.bottom),
  547.                 horizontal   = // check if element is in loadable area from left
  548.                                ((_getActualWidth() + threshold) > elementBound.left) &&
  549.                                // check if element is even in loadable area from right
  550.                                (-threshold < elementBound.right);
  551.            
  552.             if (direction === 'vertical') {                
  553.                 return vertical;
  554.             }
  555.             else if (direction === 'horizontal') {                
  556.                 return horizontal;
  557.             }            
  558.             return vertical && horizontal;
  559.         }
  560.  
  561.         /**
  562.          * receive the current viewed width of the browser
  563.          * @access private
  564.          * @return {number}
  565.          */
  566.         function _getActualWidth() {
  567.             return _actualWidth >= 0 ? _actualWidth : (_actualWidth = $(window).width());
  568.         }
  569.  
  570.         /**
  571.          * receive the current viewed height of the browser
  572.          * @access private
  573.          * @return {number}
  574.          */
  575.         function _getActualHeight() {
  576.             return _actualHeight >= 0 ? _actualHeight : (_actualHeight = $(window).height());
  577.         }
  578.  
  579.         /**
  580.          * get lowercase tag name of an element
  581.          * @access private
  582.          * @param {object} element
  583.          * @returns {string}
  584.          */
  585.         function _getElementTagName(element) {
  586.             return element.tagName.toLowerCase();
  587.         }
  588.  
  589.         /**
  590.          * prepend image base to all srcset entries
  591.          * @access private
  592.          * @param {string} srcset
  593.          * @param {string} imageBase
  594.          * @returns {string}
  595.          */
  596.         function _getCorrectedSrcSet(srcset, imageBase) {
  597.             if (imageBase) {
  598.                 // trim, remove unnecessary spaces and split entries
  599.                 var entries = srcset.split(',');
  600.                 srcset = '';
  601.  
  602.                 for (var i = 0, l = entries.length; i < l; i++) {
  603.                     srcset += imageBase + entries[i].trim() + (i !== l - 1 ? ',' : '');
  604.                 }
  605.             }
  606.  
  607.             return srcset;
  608.         }
  609.  
  610.         /**
  611.          * helper function to throttle down event triggering
  612.          * @access private
  613.          * @param {number} delay
  614.          * @param {function} callback
  615.          * @return {function}
  616.          */
  617.         function _throttle(delay, callback) {
  618.             var timeout,
  619.                 lastExecute = 0;
  620.  
  621.             return function(event, ignoreThrottle) {
  622.                 var elapsed = +new Date() - lastExecute;
  623.  
  624.                 function run() {
  625.                     lastExecute = +new Date();
  626.                     // noinspection JSUnresolvedFunction
  627.                     callback.call(instance, event);
  628.                 }
  629.  
  630.                 timeout && clearTimeout(timeout); // jshint ignore : line
  631.  
  632.                 if (elapsed > delay || !config.enableThrottle || ignoreThrottle) {
  633.                     run();
  634.                 }
  635.                 else {
  636.                     timeout = setTimeout(run, delay - elapsed);
  637.                 }
  638.             };
  639.         }
  640.  
  641.         /**
  642.          * reduce count of awaiting elements to 'afterLoad' event and fire 'onFinishedAll' if reached zero
  643.          * @access private
  644.          * @return void
  645.          */
  646.         function _reduceAwaiting() {
  647.             --_awaitingAfterLoad;
  648.  
  649.             // if no items were left trigger finished event
  650.             if (!items.length && !_awaitingAfterLoad) {
  651.                 _triggerCallback('onFinishedAll');
  652.             }
  653.         }
  654.  
  655.         /**
  656.          * single implementation to handle callbacks, pass element and set 'this' to current instance
  657.          * @access private
  658.          * @param {string|function} callback
  659.          * @param {object} [element]
  660.          * @param {*} [args]
  661.          * @return {boolean}
  662.          */
  663.         function _triggerCallback(callback, element, args) {
  664.             if ((callback = config[callback])) {
  665.                 // jQuery's internal '$(arguments).slice(1)' are causing problems at least on old iPads
  666.                 // below is shorthand of 'Array.prototype.slice.call(arguments, 1)'
  667.                 callback.apply(instance, [].slice.call(arguments, 1));
  668.                 return true;
  669.             }
  670.  
  671.             return false;
  672.         }
  673.  
  674.         // if event driven or window is already loaded don't wait for page loading
  675.         if (config.bind === 'event' || windowLoaded) {
  676.             _initialize();
  677.         }
  678.  
  679.         // otherwise load initial items and start lazy after page load
  680.         else {
  681.             // noinspection JSUnresolvedVariable
  682.             $(window).on(_load + '.' + namespace, _initialize);
  683.         }  
  684.     }
  685.  
  686.     /**
  687.      * lazy plugin class constructor
  688.      * @constructor
  689.      * @access private
  690.      * @param {object} elements
  691.      * @param {object} settings
  692.      * @return {object|LazyPlugin}
  693.      */
  694.     function LazyPlugin(elements, settings) {
  695.         /**
  696.          * this lazy plugin instance
  697.          * @access private
  698.          * @type {object|LazyPlugin|LazyPlugin.prototype}
  699.          */
  700.         var _instance = this,
  701.  
  702.         /**
  703.          * this lazy plugin instance configuration
  704.          * @access private
  705.          * @type {object}
  706.          */
  707.         _config = $.extend({}, _instance.config, settings),
  708.  
  709.         /**
  710.          * instance generated event executed on container scroll or resize
  711.          * packed in an object to be referenceable and short named because properties will not be minified
  712.          * @access private
  713.          * @type {object}
  714.          */
  715.         _events = {},
  716.  
  717.         /**
  718.          * unique namespace for instance related events
  719.          * @access private
  720.          * @type {string}
  721.          */
  722.         _namespace = _config.name + '-' + (++lazyInstanceId);
  723.  
  724.         // noinspection JSUndefinedPropertyAssignment
  725.         /**
  726.          * wrapper to get or set an entry from plugin instance configuration
  727.          * much smaller on minify as direct access
  728.          * @access public
  729.          * @type {function}
  730.          * @param {string} entryName
  731.          * @param {*} [value]
  732.          * @return {LazyPlugin|*}
  733.          */
  734.         _instance.config = function(entryName, value) {
  735.             if (value === undefined) {
  736.                 return _config[entryName];
  737.             }
  738.  
  739.             _config[entryName] = value;
  740.             return _instance;
  741.         };
  742.  
  743.         // noinspection JSUndefinedPropertyAssignment
  744.         /**
  745.          * add additional items to current instance
  746.          * @access public
  747.          * @param {Array|object|string} items
  748.          * @return {LazyPlugin}
  749.          */
  750.         _instance.addItems = function(items) {
  751.             _events.a && _events.a($.type(items) === 'string' ? $(items) : items); // jshint ignore : line
  752.             return _instance;
  753.         };
  754.  
  755.         // noinspection JSUndefinedPropertyAssignment
  756.         /**
  757.          * get all left items of this instance
  758.          * @access public
  759.          * @returns {object}
  760.          */
  761.         _instance.getItems = function() {
  762.             return _events.g ? _events.g() : {};
  763.         };
  764.  
  765.         // noinspection JSUndefinedPropertyAssignment
  766.         /**
  767.          * force lazy to load all items in loadable area right now
  768.          * by default without throttle
  769.          * @access public
  770.          * @type {function}
  771.          * @param {boolean} [useThrottle]
  772.          * @return {LazyPlugin}
  773.          */
  774.         _instance.update = function(useThrottle) {
  775.             _events.e && _events.e({}, !useThrottle); // jshint ignore : line
  776.             return _instance;
  777.         };
  778.  
  779.         // noinspection JSUndefinedPropertyAssignment
  780.         /**
  781.          * force element(s) to load directly, ignoring the viewport
  782.          * @access public
  783.          * @param {Array|object|string} items
  784.          * @return {LazyPlugin}
  785.          */
  786.         _instance.force = function(items) {
  787.             _events.f && _events.f($.type(items) === 'string' ? $(items) : items); // jshint ignore : line
  788.             return _instance;
  789.         };
  790.  
  791.         // noinspection JSUndefinedPropertyAssignment
  792.         /**
  793.          * force lazy to load all available items right now
  794.          * this call ignores throttling
  795.          * @access public
  796.          * @type {function}
  797.          * @return {LazyPlugin}
  798.          */
  799.         _instance.loadAll = function() {
  800.             _events.e && _events.e({all: true}, true); // jshint ignore : line
  801.             return _instance;
  802.         };
  803.  
  804.         // noinspection JSUndefinedPropertyAssignment
  805.         /**
  806.          * destroy this plugin instance
  807.          * @access public
  808.          * @type {function}
  809.          * @return undefined
  810.          */
  811.         _instance.destroy = function() {
  812.             // unbind instance generated events
  813.             // noinspection JSUnresolvedFunction, JSUnresolvedVariable
  814.             $(_config.appendScroll).off('.' + _namespace, _events.e);
  815.             // noinspection JSUnresolvedVariable
  816.             $(window).off('.' + _namespace);
  817.  
  818.             console.log('!!! destroyed !!!');
  819.  
  820.             // clear events
  821.             _events = {};
  822.  
  823.             return undefined;
  824.         };
  825.  
  826.         // start using lazy and return all elements to be chainable or instance for further use
  827.         // noinspection JSUnresolvedVariable
  828.         _executeLazy(_instance, _config, elements, _events, _namespace);
  829.         return _config.chainable ? elements : _instance;
  830.     }
  831.  
  832.     /**
  833.      * settings and configuration data
  834.      * @access public
  835.      * @type {object|*}
  836.      */
  837.     LazyPlugin.prototype.config = {
  838.         // general
  839.         name               : 'lazy',
  840.         chainable          : true,
  841.         autoDestroy        : true,
  842.         bind               : 'load',
  843.         threshold          : 500,
  844.         visibleOnly        : false,
  845.         appendScroll       : window,
  846.         scrollDirection    : 'both',
  847.         imageBase          : null,
  848.         defaultImage       : 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
  849.         placeholder        : null,
  850.         delay              : -1,
  851.         combined           : false,
  852.  
  853.         // attributes
  854.         attribute          : 'src',
  855.         srcsetAttribute    : 'srcset',
  856.         sizesAttribute     : 'sizes',
  857.         retinaAttribute    : 'retina',
  858.         loaderAttribute    : 'loader',
  859.         imageBaseAttribute : 'imagebase',
  860.         removeAttribute    : true,
  861.         handledName        : 'handled',
  862.         loadedName         : 'loaded',
  863.  
  864.         // effect
  865.         effect             : 'show',
  866.         effectTime         : 0,
  867.  
  868.         // throttle
  869.         enableThrottle     : true,
  870.         throttle           : 250,
  871.  
  872.         // callbacks
  873.         beforeLoad         : undefined,
  874.         afterLoad          : undefined,
  875.         onError            : undefined,
  876.         onFinishedAll      : undefined
  877.     };
  878.  
  879.     // register window load event globally to prevent not loading elements
  880.     // since jQuery 3.X ready state is fully async and may be executed after 'load'
  881.     $(window).on('load', function() {
  882.         windowLoaded = true;
  883.     });
  884. })(window);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement