/*! 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 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