123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- /*!
- hey, [be]Lazy.js - v1.3.1 - 2015.02.01
- A lazy loading and multi-serving image script
- (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';
-
- //vars
- var source, options, viewport, images, count, isRetina, destroyed;
- //throttle vars
- var validateT, saveViewportOffsetT;
-
- // constructor
- function Blazy(settings) {
- //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;
- };
- }
- //init vars
- destroyed = true;
- images = [];
- viewport = {};
- //options
- options = settings || {};
- options.error = options.error || false;
- options.offset = options.offset || 100;
- options.success = options.success || false;
- options.selector = options.selector || '.b-lazy';
- options.separator = options.separator || '|';
- options.container = options.container ? document.querySelectorAll(options.container) : false;
- options.errorClass = options.errorClass || 'b-error';
- options.breakpoints = options.breakpoints || false;
- options.successClass = options.successClass || 'b-loaded';
- options.src = source = options.src || 'data-src';
- isRetina = window.devicePixelRatio > 1;
- viewport.top = 0 - options.offset;
- viewport.left = 0 - options.offset;
- //throttle, ensures that we don't call the functions too often
- validateT = throttle(validate, 25);
- saveViewportOffsetT = throttle(saveViewportOffset, 50);
- saveViewportOffset();
-
- //handle multi-served image src
- each(options.breakpoints, function(object){
- if(object.width >= window.screen.width) {
- source = object.src;
- return false;
- }
- });
-
- // start lazy load
- initialize();
- }
-
- /* public functions
- ************************************/
- Blazy.prototype.revalidate = function() {
- initialize();
- };
- Blazy.prototype.load = function(element, force){
- if(!isElementLoaded(element)) loadImage(element, force);
- };
- Blazy.prototype.destroy = function(){
- if(options.container){
- each(options.container, function(object){
- unbindEvent(object, 'scroll', validateT);
- });
- }
- unbindEvent(window, 'scroll', validateT);
- unbindEvent(window, 'resize', validateT);
- unbindEvent(window, 'resize', saveViewportOffsetT);
- count = 0;
- images.length = 0;
- destroyed = true;
- };
-
- /* private helper functions
- ************************************/
- function initialize(){
- // First we create an array of images to lazy load
- createImageArray(options.selector);
- // Then we bind resize and scroll events if not already binded
- if(destroyed) {
- destroyed = false;
- if(options.container) {
- each(options.container, function(object){
- bindEvent(object, 'scroll', validateT);
- });
- }
- bindEvent(window, 'resize', saveViewportOffsetT);
- bindEvent(window, 'resize', validateT);
- bindEvent(window, 'scroll', validateT);
- }
- // And finally, we start to lazy load. Should bLazy ensure domready?
- validate();
- }
-
- function validate() {
- for(var i = 0; i<count; i++){
- var image = images[i];
- if(elementInView(image) || isElementLoaded(image)) {
- Blazy.prototype.load(image);
- images.splice(i, 1);
- count--;
- i--;
- }
- }
- if(count === 0) {
- Blazy.prototype.destroy();
- }
- }
-
- function loadImage(ele, force){
- // if element is visible
- if(force || (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 img = new Image();
- // cleanup markup, remove data source attributes
- each(options.breakpoints, function(object){
- ele.removeAttribute(object.src);
- });
- ele.removeAttribute(options.src);
- img.onerror = function() {
- if(options.error) options.error(ele, "invalid");
- ele.className = ele.className + ' ' + options.errorClass;
- };
- img.onload = function() {
- // Is element an image or should we add the src as a background image?
- ele.nodeName.toLowerCase() === 'img' ? ele.src = src : ele.style.backgroundImage = 'url("' + src + '")';
- ele.className = ele.className + ' ' + options.successClass;
- if(options.success) options.success(ele);
- };
- img.src = src; //preload image
- } else {
- if(options.error) options.error(ele, "missing");
- ele.className = ele.className + ' ' + options.errorClass;
- }
- }
- }
-
- 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 isElementLoaded(ele) {
- return (' ' + ele.className + ' ').indexOf(' ' + options.successClass + ' ') !== -1;
- }
-
- function createImageArray(selector) {
- var nodelist = document.querySelectorAll(selector);
- count = nodelist.length;
- //converting nodelist to array
- for(var i = count; i--; images.unshift(nodelist[i])){}
- }
-
- function saveViewportOffset(){
- viewport.bottom = (window.innerHeight || document.documentElement.clientHeight) + options.offset;
- viewport.right = (window.innerWidth || document.documentElement.clientWidth) + options.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) {
- var lastCall = 0;
- return function() {
- var now = +new Date();
- if (now - lastCall < minDelay) {
- return;
- }
- lastCall = now;
- fn.apply(images, arguments);
- };
- }
-
- return Blazy;
- });
|