25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

874 lines
30 KiB

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