lazysizes
Version:
High performance (jankfree) lazy loader for images (including responsive images), iframes and scripts (widgets).
312 lines (264 loc) • 7.91 kB
JavaScript
(function(window, factory) {
if(!window) {return;}
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
var polyfill;
var lazySizesCfg = lazySizes.cfg;
var img = document.createElement('img');
var supportSrcset = ('sizes' in img) && ('srcset' in img);
var regHDesc = /\s+\d+h/g;
var fixEdgeHDescriptor = (function(){
var regDescriptors = /\s+(\d+)(w|h)\s+(\d+)(w|h)/;
var forEach = Array.prototype.forEach;
return function(){
var img = document.createElement('img');
var removeHDescriptors = function(source){
var ratio, match;
var srcset = source.getAttribute(lazySizesCfg.srcsetAttr);
if(srcset){
if((match = srcset.match(regDescriptors))){
if(match[2] == 'w'){
ratio = match[1] / match[3];
} else {
ratio = match[3] / match[1];
}
if(ratio){
source.setAttribute('data-aspectratio', ratio);
}
source.setAttribute(lazySizesCfg.srcsetAttr, srcset.replace(regHDesc, ''));
}
}
};
var handler = function(e){
if(e.detail.instance != lazySizes){return;}
var picture = e.target.parentNode;
if(picture && picture.nodeName == 'PICTURE'){
forEach.call(picture.getElementsByTagName('source'), removeHDescriptors);
}
removeHDescriptors(e.target);
};
var test = function(){
if(!!img.currentSrc){
document.removeEventListener('lazybeforeunveil', handler);
}
};
document.addEventListener('lazybeforeunveil', handler);
img.onload = test;
img.onerror = test;
img.srcset = 'data:,a 1w 1h';
if(img.complete){
test();
}
};
})();
if(!lazySizesCfg.supportsType){
lazySizesCfg.supportsType = function(type/*, elem*/){
return !type;
};
}
if (window.HTMLPictureElement && supportSrcset) {
if(!lazySizes.hasHDescriptorFix && document.msElementsFromPoint){
lazySizes.hasHDescriptorFix = true;
fixEdgeHDescriptor();
}
return;
}
if(window.picturefill || lazySizesCfg.pf){return;}
lazySizesCfg.pf = function(options){
var i, len;
if(window.picturefill){return;}
for(i = 0, len = options.elements.length; i < len; i++){
polyfill(options.elements[i]);
}
};
// partial polyfill
polyfill = (function(){
var ascendingSort = function( a, b ) {
return a.w - b.w;
};
var regPxLength = /^\s*\d+\.*\d*px\s*$/;
var reduceCandidate = function (srces) {
var lowerCandidate, bonusFactor;
var len = srces.length;
var candidate = srces[len -1];
var i = 0;
for(i; i < len;i++){
candidate = srces[i];
candidate.d = candidate.w / srces.w;
if(candidate.d >= srces.d){
if(!candidate.cached && (lowerCandidate = srces[i - 1]) &&
lowerCandidate.d > srces.d - (0.13 * Math.pow(srces.d, 2.2))){
bonusFactor = Math.pow(lowerCandidate.d - 0.6, 1.6);
if(lowerCandidate.cached) {
lowerCandidate.d += 0.15 * bonusFactor;
}
if(lowerCandidate.d + ((candidate.d - srces.d) * bonusFactor) > srces.d){
candidate = lowerCandidate;
}
}
break;
}
}
return candidate;
};
var parseWsrcset = (function(){
var candidates;
var regWCandidates = /(([^,\s].[^\s]+)\s+(\d+)w)/g;
var regMultiple = /\s/;
var addCandidate = function(match, candidate, url, wDescriptor){
candidates.push({
c: candidate,
u: url,
w: wDescriptor * 1
});
};
return function(input){
candidates = [];
input = input.trim();
input
.replace(regHDesc, '')
.replace(regWCandidates, addCandidate)
;
if(!candidates.length && input && !regMultiple.test(input)){
candidates.push({
c: input,
u: input,
w: 99
});
}
return candidates;
};
})();
var runMatchMedia = function(){
if(runMatchMedia.init){return;}
runMatchMedia.init = true;
addEventListener('resize', (function(){
var timer;
var matchMediaElems = document.getElementsByClassName('lazymatchmedia');
var run = function(){
var i, len;
for(i = 0, len = matchMediaElems.length; i < len; i++){
polyfill(matchMediaElems[i]);
}
};
return function(){
clearTimeout(timer);
timer = setTimeout(run, 66);
};
})());
};
var createSrcset = function(elem, isImage){
var parsedSet;
var srcSet = elem.getAttribute('srcset') || elem.getAttribute(lazySizesCfg.srcsetAttr);
if(!srcSet && isImage){
srcSet = !elem._lazypolyfill ?
(elem.getAttribute(lazySizesCfg.srcAttr) || elem.getAttribute('src')) :
elem._lazypolyfill._set
;
}
if(!elem._lazypolyfill || elem._lazypolyfill._set != srcSet){
parsedSet = parseWsrcset( srcSet || '' );
if(isImage && elem.parentNode){
parsedSet.isPicture = elem.parentNode.nodeName.toUpperCase() == 'PICTURE';
if(parsedSet.isPicture){
if(window.matchMedia){
lazySizes.aC(elem, 'lazymatchmedia');
runMatchMedia();
}
}
}
parsedSet._set = srcSet;
Object.defineProperty(elem, '_lazypolyfill', {
value: parsedSet,
writable: true
});
}
};
var getX = function(elem){
var dpr = window.devicePixelRatio || 1;
var optimum = lazySizes.getX && lazySizes.getX(elem);
return Math.min(optimum || dpr, 2.5, dpr);
};
var matchesMedia = function(media){
if(window.matchMedia){
matchesMedia = function(media){
return !media || (matchMedia(media) || {}).matches;
};
} else {
return !media;
}
return matchesMedia(media);
};
var getCandidate = function(elem){
var sources, i, len, media, source, srces, src, width;
source = elem;
createSrcset(source, true);
srces = source._lazypolyfill;
if(srces.isPicture){
for(i = 0, sources = elem.parentNode.getElementsByTagName('source'), len = sources.length; i < len; i++){
if( lazySizesCfg.supportsType(sources[i].getAttribute('type'), elem) && matchesMedia( sources[i].getAttribute('media')) ){
source = sources[i];
createSrcset(source);
srces = source._lazypolyfill;
break;
}
}
}
if(srces.length > 1){
width = source.getAttribute('sizes') || '';
width = regPxLength.test(width) && parseInt(width, 10) || lazySizes.gW(elem, elem.parentNode);
srces.d = getX(elem);
if(!srces.src || !srces.w || srces.w < width){
srces.w = width;
src = reduceCandidate(srces.sort(ascendingSort));
srces.src = src;
} else {
src = srces.src;
}
} else {
src = srces[0];
}
return src;
};
var p = function(elem){
if(supportSrcset && elem.parentNode && elem.parentNode.nodeName.toUpperCase() != 'PICTURE'){return;}
var candidate = getCandidate(elem);
if(candidate && candidate.u && elem._lazypolyfill.cur != candidate.u){
elem._lazypolyfill.cur = candidate.u;
candidate.cached = true;
elem.setAttribute(lazySizesCfg.srcAttr, candidate.u);
elem.setAttribute('src', candidate.u);
}
};
p.parse = parseWsrcset;
return p;
})();
if(lazySizesCfg.loadedClass && lazySizesCfg.loadingClass){
(function(){
var sels = [];
['img[sizes$="px"][srcset].', 'picture > img:not([srcset]).'].forEach(function(sel){
sels.push(sel + lazySizesCfg.loadedClass);
sels.push(sel + lazySizesCfg.loadingClass);
});
lazySizesCfg.pf({
elements: document.querySelectorAll(sels.join(', '))
});
})();
}
}));