MyShaarli/inc/blazy-1.3.1.js

233 lines
7.0 KiB
JavaScript

/*!
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;
});