| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*!
- hey, [be]Lazy.js - v1.6.2 - 2016.05.09
- A fast, small and dependency free lazy load script (https://github.com/dinbror/blazy)
- (c) Bjoern Klinggaard - @bklinggaard - http://dinbror.dk/blazy
- */
- ;
- (function(root, blazy) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register bLazy as an anonymous module
- define(blazy);
- } else if (typeof exports === 'object') {
- // Node. Does not work with strict CommonJS, but
- // only CommonJS-like environments that support module.exports,
- // like Node.
- module.exports = blazy();
- } else {
- // Browser globals. Register bLazy on window
- root.Blazy = blazy();
- }
- })(this, function() {
- 'use strict';
- //private vars
- var _source, _viewport, _isRetina, _attrSrc = 'src',
- _attrSrcset = 'srcset';
- // constructor
- return function Blazy(options) {
- //IE7- fallback for missing querySelectorAll support
- if (!document.querySelectorAll) {
- var s = document.createStyleSheet();
- document.querySelectorAll = function(r, c, i, j, a) {
- a = document.all, c = [], r = r.replace(/\[for\b/gi, '[htmlFor').split(',');
- for (i = r.length; i--;) {
- s.addRule(r[i], 'k:v');
- for (j = a.length; j--;) a[j].currentStyle.k && c.push(a[j]);
- s.removeRule(0);
- }
- return c;
- };
- }
- //options and helper vars
- var scope = this;
- var util = scope._util = {};
- util.elements = [];
- util.destroyed = true;
- scope.options = options || {};
- scope.options.error = scope.options.error || false;
- scope.options.offset = scope.options.offset || 100;
- scope.options.success = scope.options.success || false;
- scope.options.selector = scope.options.selector || '.b-lazy';
- scope.options.separator = scope.options.separator || '|';
- scope.options.container = scope.options.container ? document.querySelectorAll(scope.options.container) : false;
- scope.options.errorClass = scope.options.errorClass || 'b-error';
- scope.options.breakpoints = scope.options.breakpoints || false; // obsolete
- scope.options.loadInvisible = scope.options.loadInvisible || false;
- scope.options.successClass = scope.options.successClass || 'b-loaded';
- scope.options.validateDelay = scope.options.validateDelay || 25;
- scope.options.saveViewportOffsetDelay = scope.options.saveViewportOffsetDelay || 50;
- scope.options.srcset = scope.options.srcset || 'data-srcset';
- scope.options.src = _source = scope.options.src || 'data-src';
- _isRetina = window.devicePixelRatio > 1;
- _viewport = {};
- _viewport.top = 0 - scope.options.offset;
- _viewport.left = 0 - scope.options.offset;
- /* public functions
- ************************************/
- scope.revalidate = function() {
- initialize(this);
- };
- scope.load = function(elements, force) {
- var opt = this.options;
- if (elements.length === undefined) {
- loadElement(elements, force, opt);
- } else {
- each(elements, function(element) {
- loadElement(element, force, opt);
- });
- }
- };
- scope.destroy = function() {
- var self = this;
- var util = self._util;
- if (self.options.container) {
- each(self.options.container, function(object) {
- unbindEvent(object, 'scroll', util.validateT);
- });
- }
- unbindEvent(window, 'scroll', util.validateT);
- unbindEvent(window, 'resize', util.validateT);
- unbindEvent(window, 'resize', util.saveViewportOffsetT);
- // destroy handler for scroller
- if (self.scroller) {
- self.scroller._xscroll && self.scroller._xscroll.off("scroll scrollend afterrender", util.validateT, self.scroller._xscroll);
- }
- util.count = 0;
- util.elements.length = 0;
- util.destroyed = true;
- };
- //throttle, ensures that we don't call the functions too often
- util.validateT = throttle(function() {
- validate(scope);
- }, scope.options.validateDelay, scope);
- util.saveViewportOffsetT = throttle(function() {
- saveViewportOffset(scope.options.offset);
- }, scope.options.saveViewportOffsetDelay, scope);
- saveViewportOffset(scope.options.offset);
- //handle multi-served image src (obsolete)
- each(scope.options.breakpoints, function(object) {
- if (object.width >= window.screen.width) {
- _source = object.src;
- return false;
- }
- });
- // start lazy load
- setTimeout(function() {
- initialize(scope);
- }); // "dom ready" fix
- };
- /* Private helper functions
- ************************************/
- function initialize(self) {
- var util = self._util;
- // First we create an array of elements to lazy load
- util.elements = toArray(self.options.selector);
- util.count = util.elements.length;
- // Then we bind resize and scroll events if not already binded
- if (util.destroyed) {
- util.destroyed = false;
- if (self.options.container) {
- each(self.options.container, function(object) {
- bindEvent(object, 'scroll', util.validateT);
- });
- }
- bindEvent(window, 'resize', util.saveViewportOffsetT);
- bindEvent(window, 'resize', util.validateT);
- bindEvent(window, 'scroll', util.validateT);
- // scroll handler for scroller
- if (self.options.scroller) {
- var scroller = self.options.scroller._xscroll
- var eventType = scroller.userConfig.useOriginScroll ? "scroll" : "scrollend";
- scroller.on("afterrender", util.validateT, self);
- scroller.on(eventType, util.validateT, self);
- }
- }
- // And finally, we start to lazy load.
- validate(self);
- }
- function validate(self) {
- var util = self._util;
- for (var i = 0; i < util.count; i++) {
- var element = util.elements[i];
- if (elementInView(element) || hasClass(element, self.options.successClass)) {
- self.load(element);
- util.elements.splice(i, 1);
- util.count--;
- i--;
- }
- }
- if (util.count === 0) {
- self.destroy();
- }
- }
- function elementInView(ele) {
- var rect = ele.getBoundingClientRect();
- return (
- // Intersection
- rect.right >= _viewport.left && rect.bottom >= _viewport.top && rect.left <= _viewport.right && rect.top <= _viewport.bottom
- );
- }
- function loadElement(ele, force, options) {
- // if element is visible, not loaded or forced
- if (!hasClass(ele, options.successClass) && (force || options.loadInvisible || (ele.offsetWidth > 0 && ele.offsetHeight > 0))) {
- var dataSrc = ele.getAttribute(_source) || ele.getAttribute(options.src); // fallback to default 'data-src'
- if (dataSrc) {
- var dataSrcSplitted = dataSrc.split(options.separator);
- var src = dataSrcSplitted[_isRetina && dataSrcSplitted.length > 1 ? 1 : 0];
- var isImage = equal(ele, 'img');
- // Image or background image
- if (isImage || ele.src === undefined) {
- var img = new Image();
- // using EventListener instead of onerror and onload
- // due to bug introduced in chrome v50
- // (https://productforums.google.com/forum/#!topic/chrome/p51Lk7vnP2o)
- var onErrorHandler = function() {
- if (options.error) options.error(ele, "invalid");
- addClass(ele, options.errorClass);
- unbindEvent(img, 'error', onErrorHandler);
- unbindEvent(img, 'load', onLoadHandler);
- };
- var onLoadHandler = function() {
- // Is element an image
- if (isImage) {
- setSrc(ele, src); //src
- handleSource(ele, _attrSrcset, options.srcset); //srcset
- //picture element
- var parent = ele.parentNode;
- if (parent && equal(parent, 'picture')) {
- each(parent.getElementsByTagName('source'), function(source) {
- handleSource(source, _attrSrcset, options.srcset);
- });
- }
- if (options.scroller) {
- options.scroller.reset()
- }
- // or background-image
- } else {
- ele.style.backgroundImage = 'url("' + src + '")';
- }
- itemLoaded(ele, options);
- unbindEvent(img, 'load', onLoadHandler);
- unbindEvent(img, 'error', onErrorHandler);
- };
- bindEvent(img, 'error', onErrorHandler);
- bindEvent(img, 'load', onLoadHandler);
- setSrc(img, src); //preload
- } else { // An item with src like iframe, unity, simpelvideo etc
- setSrc(ele, src);
- itemLoaded(ele, options);
- }
- } else {
- // video with child source
- if (equal(ele, 'video')) {
- each(ele.getElementsByTagName('source'), function(source) {
- handleSource(source, _attrSrc, options.src);
- });
- ele.load();
- itemLoaded(ele, options);
- } else {
- if (options.error) options.error(ele, "missing");
- addClass(ele, options.errorClass);
- }
- }
- }
- }
- function itemLoaded(ele, options) {
- addClass(ele, options.successClass);
- if (options.success) options.success(ele);
- // cleanup markup, remove data source attributes
- ele.removeAttribute(options.src);
- each(options.breakpoints, function(object) {
- ele.removeAttribute(object.src);
- });
- }
- function setSrc(ele, src) {
- ele[_attrSrc] = src;
- }
- function handleSource(ele, attr, dataAttr) {
- var dataSrc = ele.getAttribute(dataAttr);
- if (dataSrc) {
- ele[attr] = dataSrc;
- ele.removeAttribute(dataAttr);
- }
- }
- function equal(ele, str) {
- return ele.nodeName.toLowerCase() === str;
- }
- function hasClass(ele, className) {
- return (' ' + ele.className + ' ').indexOf(' ' + className + ' ') !== -1;
- }
- function addClass(ele, className) {
- if (!hasClass(ele, className)) {
- ele.className += ' ' + className;
- }
- }
- function toArray(selector) {
- var array = [];
- var nodelist = document.querySelectorAll(selector);
- for (var i = nodelist.length; i--; array.unshift(nodelist[i])) {}
- return array;
- }
- function saveViewportOffset(offset) {
- _viewport.bottom = (window.innerHeight || document.documentElement.clientHeight) + offset;
- _viewport.right = (window.innerWidth || document.documentElement.clientWidth) + offset;
- }
- function bindEvent(ele, type, fn) {
- if (ele.attachEvent) {
- ele.attachEvent && ele.attachEvent('on' + type, fn);
- } else {
- ele.addEventListener(type, fn, false);
- }
- }
- function unbindEvent(ele, type, fn) {
- if (ele.detachEvent) {
- ele.detachEvent && ele.detachEvent('on' + type, fn);
- } else {
- ele.removeEventListener(type, fn, false);
- }
- }
- function each(object, fn) {
- if (object && fn) {
- var l = object.length;
- for (var i = 0; i < l && fn(object[i], i) !== false; i++) {}
- }
- }
- function throttle(fn, minDelay, scope) {
- var lastCall = 0;
- return function() {
- var now = +new Date();
- if (now - lastCall < minDelay) {
- return;
- }
- lastCall = now;
- fn.apply(scope, arguments);
- };
- }
- });
|