lazysizes
Version:
High performance (jankfree) lazy loader for images (including responsive images), iframes and scripts (widgets).
158 lines (124 loc) • 4.32 kB
JavaScript
(function(window, factory) {
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(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
'use strict';
if(!window.addEventListener){return;}
var regDescriptors = /\s+(\d+)(w|h)\s+(\d+)(w|h)/;
var regCssFit = /parent-fit["']*\s*:\s*["']*(contain|cover|width)/;
var regCssObject = /parent-container["']*\s*:\s*["']*(.+?)(?=(\s|$|,|'|"|;))/;
var regPicture = /^picture$/i;
var getCSS = function (elem){
return (getComputedStyle(elem, null) || {});
};
var parentFit = {
getParent: function(element, parentSel){
var parent = element;
var parentNode = element.parentNode;
if((!parentSel || parentSel == 'prev') && parentNode && regPicture.test(parentNode.nodeName || '')){
parentNode = parentNode.parentNode;
}
if(parentSel != 'self'){
if(parentSel == 'prev'){
parent = element.previousElementSibling;
} else if(parentSel && (parentNode.closest || window.jQuery)){
parent = (parentNode.closest ?
parentNode.closest(parentSel) :
jQuery(parentNode).closest(parentSel)[0]) ||
parentNode
;
} else {
parent = parentNode;
}
}
return parent;
},
getFit: function(element){
var tmpMatch, parentObj;
var css = getCSS(element);
var content = css.content || css.fontFamily;
var obj = {
fit: element._lazysizesParentFit || element.getAttribute('data-parent-fit')
};
if(!obj.fit && content && (tmpMatch = content.match(regCssFit))){
obj.fit = tmpMatch[1];
}
if(obj.fit){
parentObj = element._lazysizesParentContainer || element.getAttribute('data-parent-container');
if(!parentObj && content && (tmpMatch = content.match(regCssObject))){
parentObj = tmpMatch[1];
}
obj.parent = parentFit.getParent(element, parentObj);
} else {
obj.fit = css.objectFit;
}
return obj;
},
getImageRatio: function(element){
var i, srcset, media, ratio;
var parent = element.parentNode;
var elements = parent && regPicture.test(parent.nodeName || '') ?
parent.querySelectorAll('source, img') :
[element]
;
for(i = 0; i < elements.length; i++){
element = elements[i];
srcset = element.getAttribute(lazySizesConfig.srcsetAttr) || element.getAttribute('srcset') || element.getAttribute('data-pfsrcset') || element.getAttribute('data-risrcset') || '';
media = element._lsMedia || element.getAttribute('media');
media = lazySizesConfig.customMedia[element.getAttribute('data-media') || media] || media;
if(srcset && (!media || (window.matchMedia && matchMedia(media) || {}).matches )){
ratio = parseFloat(element.getAttribute('data-aspectratio'));
if(!ratio && srcset.match(regDescriptors)){
if(RegExp.$2 == 'w'){
ratio = RegExp.$1 / RegExp.$3;
} else {
ratio = RegExp.$3 / RegExp.$1;
}
}
break;
}
}
return ratio;
},
calculateSize: function(element, width){
var displayRatio, height, imageRatio, retWidth;
var fitObj = this.getFit(element);
var fit = fitObj.fit;
var fitElem = fitObj.parent;
if(fit != 'width' && ((fit != 'contain' && fit != 'cover') || !(imageRatio = this.getImageRatio(element)))){
return width;
}
if(fitElem){
width = fitElem.clientWidth;
} else {
fitElem = element;
}
retWidth = width;
if(fit == 'width'){
retWidth = width;
} else {
height = fitElem.clientHeight;
if(height > 40 && (displayRatio = width / height) && ((fit == 'cover' && displayRatio < imageRatio) || (fit == 'contain' && displayRatio > imageRatio))){
retWidth = width * (imageRatio / displayRatio);
}
}
return retWidth;
}
};
lazySizes.parentFit = parentFit;
document.addEventListener('lazybeforesizes', function(e){
if(e.defaultPrevented || e.detail.instance != lazySizes){return;}
var element = e.target;
e.detail.width = parentFit.calculateSize(element, e.detail.width);
});
}));