All files / mobile.startup/lazyImages lazyImageLoader.js

92.3% Statements 24/26
88.88% Branches 16/18
100% Functions 6/6
92.3% Lines 24/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86  1x 1x             2x                       4x 5x                       8x   8x 8x 8x     8x 8x 8x 8x     8x     5x 5x     5x   8x   2x         2x       8x 8x   8x           1x                  
var
	util = require( '../util' ),
	placeholderClass = 'lazy-image-placeholder';
 
/**
 * @param {HTMLElement} root
 * @return {HTMLElement[]}
 */
function queryPlaceholders( root ) {
	return Array.prototype.slice.call(
		root.getElementsByClassName( placeholderClass )
	);
}
 
/**
 * Load an image on demand
 *
 * @param {HTMLElement[]} placeholders a list of images that have not been loaded.
 * @return {jQuery.Deferred}
 */
function loadImages( placeholders ) {
	return util.Promise.all(
		placeholders.map( ( placeholder ) => module.exports.loadImage( placeholder ).promise )
	);
}
 
/**
 * Load an image on demand
 *
 * @param {HTMLElement} placeholder
 * @return {{promise: jQuery.Deferred<'load'|'error'>, image: HTMLImageElement}}
 */
function loadImage( placeholder ) {
	var
		deferred = util.Deferred(),
		// data-width and height are attributes and do not specify dimension.
		width = placeholder.dataset.width || '0',
		height = placeholder.dataset.height || '0',
		image = new Image( parseInt( width, 10 ), parseInt( height, 10 ) );
 
	// eslint-disable-next-line mediawiki/class-doc
	image.className = placeholder.dataset.class || '';
	image.alt = placeholder.dataset.alt || '';
	image.useMap = placeholder.dataset.usemap;
	image.style.cssText = placeholder.style.cssText || '';
 
	// When the image has loaded
	image.addEventListener( 'load', () => {
		// Swap the HTML inside the placeholder (to keep the layout and
		// dimensions the same and not trigger layouts
		image.classList.add( 'image-lazy-loaded' );
		Iif ( placeholder.parentNode ) {
			placeholder.parentNode.replaceChild( image, placeholder );
		}
		deferred.resolve( 'load' );
	}, { once: true } );
	image.addEventListener( 'error', () => {
		// Swap the HTML and let the browser decide what to do with the broken image.
		Iif ( placeholder.parentNode ) {
			placeholder.parentNode.replaceChild( image, placeholder );
		}
		// Never reject. Quietly resolve so that Promise.all() awaits for all Deferreds to complete.
		// Reevaluate using Deferred.reject in T136693.
		deferred.resolve( 'error' );
	}, { once: true } );
 
	// Trigger image download after binding the load handler
	image.src = placeholder.dataset.src || '';
	image.srcset = placeholder.dataset.srcset || '';
 
	return {
		promise: deferred,
		image
	};
}
 
module.exports = {
	placeholderClass,
	queryPlaceholders,
	loadImages,
	loadImage,
	test: {
		placeholderClass
	}
};