UNPKG

respimage

Version:

The fast, lightweight and reliable polyfill for responsive images (i.e. picture element and the srcset, sizes and media attributes). With a smart resource selection algorithm, that saves bandwidth.

429 lines (368 loc) 11 kB
(function( factory ) { "use strict"; var interValId; var intervalIndex = 0; var run = function(){ if ( window.respimage ) { factory( window.respimage ); } if(window.respimage || intervalIndex > 9999){ clearInterval(interValId); } intervalIndex++; }; interValId = setInterval(run, 8); run(); }( function( respimage ) { "use strict"; var document = window.document; var Element = window.Element; var MutationObserver = window.MutationObserver; var noop = function() {}; var riobserver = { disconnect: noop, take: noop, observe: noop, start: noop, stop: noop, connected: false }; var isReady = /^loade|^c|^i/.test(document.readyState || ""); var ri = respimage._; ri.mutationSupport = false; ri.observer = riobserver; if ( !Object.keys || !window.HTMLSourceElement || !document.addEventListener) { return; } var matches, observer, allowConnect, addMutation; var observeProps = { src: 1, srcset: 1, sizes: 1, media: 1 }; var attrFilter = Object.keys( observeProps ); var config = { attributes: true, childList: true, subtree: true, attributeFilter: attrFilter }; var elemProto = Element && Element.prototype; var sup = {}; var monkeyPatch = function( name, fn ) { sup[ name ] = ri[ name ]; ri[ name ] = fn; }; if ( elemProto && !elemProto.matches ) { elemProto.matches = elemProto.matchesSelector || elemProto.mozMatchesSelector || elemProto.webkitMatchesSelector || elemProto.msMatchesSelector; } if ( elemProto && elemProto.matches ) { matches = function( elem, sel ) { return elem.matches( sel ); }; ri.mutationSupport = !!( Object.create && Object.defineProperties ); } if ( !ri.mutationSupport ) { return; } riobserver.observe = function() { if ( allowConnect ) { riobserver.connected = true; if ( observer ) { observer.observe( document.documentElement, config ); } } }; riobserver.disconnect = function() { riobserver.connected = false; if ( observer ) { observer.disconnect(); } }; riobserver.take = function() { if ( observer ) { ri.onMutations( observer.takeRecords() ); } else if ( addMutation ) { addMutation.take(); } }; riobserver.start = function() { allowConnect = true; riobserver.observe(); }; riobserver.stop = function() { allowConnect = false; riobserver.disconnect(); }; monkeyPatch( "setupRun", function() { riobserver.disconnect(); return sup.setupRun.apply( this, arguments ); }); monkeyPatch( "teardownRun", function() { var ret = sup.setupRun.apply( this, arguments ); riobserver.observe(); return ret; }); monkeyPatch( "setSrc", function() { var ret; var wasConnected = riobserver.connected; riobserver.disconnect(); ret = sup.setSrc.apply( this, arguments ); if ( wasConnected ) { riobserver.observe(); } return ret; }); ri.onMutations = function( mutations ) { var i, len; var modifiedImgs = []; for (i = 0, len = mutations.length; i < len; i++) { if ( isReady && mutations[i].type === "childList" ) { ri.onSubtreeChange( mutations[i], modifiedImgs ); } else if ( mutations[i].type === "attributes" ) { ri.onAttrChange( mutations[i], modifiedImgs ); } } if ( modifiedImgs.length ) { ri.fillImgs({ elements: modifiedImgs, reevaluate: true }); } }; ri.onSubtreeChange = function( mutations, imgs ) { ri.findAddedMutations( mutations.addedNodes, imgs ); ri.findRemovedMutations( mutations.removedNodes, mutations.target, imgs ); }; ri.findAddedMutations = function( nodes, imgs ) { var i, len, node, nodeName; for ( i = 0, len = nodes.length; i < len; i++ ){ node = nodes[i]; if ( node.nodeType !== 1 ) {continue;} nodeName = node.nodeName.toUpperCase(); if ( nodeName === "PICTURE" ) { ri.addToElements( node.getElementsByTagName( "img" )[0], imgs ); } else if ( nodeName === "IMG" && matches( node, ri.selShort ) ){ ri.addToElements( node, imgs ); } else if ( nodeName === "SOURCE" ) { ri.addImgForSource( node, node.parentNode, imgs ); } else { ri.addToElements( ri.qsa( node, ri.selShort ), imgs ); } } }; ri.findRemovedMutations = function( nodes, target, imgs ) { var i, len, node; for ( i = 0, len = nodes.length; i < len; i++ ) { node = nodes[i]; if ( node.nodeType !== 1 ) {continue;} if ( node.nodeName.toUpperCase() === "SOURCE" ) { ri.addImgForSource( node, target, imgs ); } } }; ri.addImgForSource = function( node, parent, imgs ) { if ( parent && ( parent.nodeName || "" ).toUpperCase() !== "PICTURE" ) { parent = parent.parentNode; if(!parent || ( parent.nodeName || "" ).toUpperCase() !== "PICTURE" ) { parent = null; } } if(parent){ ri.addToElements( parent.getElementsByTagName( "img" )[0], imgs ); } }; ri.addToElements = function( img, imgs ) { var i, len; if ( img ) { if ( ("length" in img) && !img.nodeType ){ for ( i = 0, len = img.length; i < len; i++ ) { ri.addToElements( img[i], imgs ); } } else if ( img.parentNode && imgs.indexOf(img) === -1 ) { imgs.push( img ); } } }; ri.onAttrChange = function( mutation, modifiedImgs ) { var nodeName; var riData = mutation.target[ ri.ns ]; if ( !riData && mutation.attributeName === "srcset" && (nodeName = mutation.target.nodeName.toUpperCase()) === "IMG" ) { ri.addToElements( mutation.target, modifiedImgs ); } else if ( riData ) { if(!nodeName){ nodeName = mutation.target.nodeName.toUpperCase(); } if ( nodeName === "IMG" ) { if ( mutation.attributeName in riData ) { riData[ mutation.attributeName ] = undefined; } ri.addToElements( mutation.target, modifiedImgs ); } else if ( nodeName === "SOURCE" ) { ri.addImgForSource( mutation.target, mutation.target.parentNode, modifiedImgs ); } } }; if ( !ri.supPicture ) { if ( MutationObserver && !ri.testMutationEvents ) { observer = new MutationObserver( ri.onMutations ); } else { addMutation = (function() { var running = false; var mutations = []; var setImmediate = window.setImmediate || window.setTimeout; return function(mutation) { if ( !running ) { running = true; if ( !addMutation.take ) { addMutation.take = function() { if ( mutations.length ) { ri.onMutations( mutations ); mutations = []; } running = false; }; } setImmediate( addMutation.take ); } mutations.push( mutation ); }; })(); document.documentElement.addEventListener( "DOMNodeInserted", function( e ) { if ( riobserver.connected && isReady ) { addMutation( { type: "childList", addedNodes: [ e.target ], removedNodes: [] } ); } }, true); document.documentElement.addEventListener( "DOMNodeRemoved", function( e ) { if ( riobserver.connected && isReady && (e.target || {}).nodeName == 'SOURCE') { addMutation( { type: "childList", addedNodes: [], removedNodes: [ e.target ], target: e.target.parentNode } ); } }, true); document.documentElement.addEventListener( "DOMAttrModified", function( e ) { if ( riobserver.connected && observeProps[e.attrName] ) { addMutation( { type: "attributes", target: e.target, attributeName: e.attrName } ); } }, true); } if ( window.HTMLImageElement && Object.defineProperties ) { (function() { var image = document.createElement( "img" ); var imgIdls = []; var getImgAttr = image.getAttribute; var setImgAttr = image.setAttribute; var GETIMGATTRS = { src: 1 }; if ( ri.supSrcset && !ri.supSizes ) { GETIMGATTRS.srcset = 1; } Object.defineProperties(HTMLImageElement.prototype, { getAttribute: { value: function( attr ) { var internal; if ( GETIMGATTRS[ attr ] && (internal = this[ ri.ns ]) && ( internal[attr] !== undefined ) ) { return internal[ attr ]; } return getImgAttr.apply( this, arguments ); }, writeable: true, enumerable: true, configurable: true } }); if(!ri.supSrcset){ imgIdls.push('srcset'); } if(!ri.supSizes){ imgIdls.push('sizes'); } imgIdls.forEach(function(idl){ Object.defineProperty(HTMLImageElement.prototype, idl, { set: function( value ) { setImgAttr.call( this, idl, value ); }, get: function() { return getImgAttr.call( this, idl ) || ''; }, enumerable: true, configurable: true }); }); if(!('currentSrc' in image)){ (function(){ var ascendingSort; var updateCurSrc = function(elem, src) { if(src == null){ src = elem.src || ''; } Object.defineProperty(elem, 'riCurrentSrc', { value: src, writable: true }); }; var baseUpdateCurSrc = updateCurSrc; if(ri.supSrcset && window.devicePixelRatio){ ascendingSort = function( a, b ) { var aRes = a.res || a.d || a.x || a.w; var bRes = b.res || b.d || b.x || a.w; return aRes - bRes; }; updateCurSrc = function(elem) { var i, cands, length, ret; var imageData = elem[ ri.ns ]; if ( imageData && imageData.supported && imageData.srcset && imageData.sets && (cands = ri.parseSet(imageData.sets[0])) && cands.sort) { cands.sort( ascendingSort ); length = cands.length; ret = cands[ length - 1 ]; for(i = 0; i < length; i++){ if(cands[i].x >= window.devicePixelRatio){ ret = cands[i]; break; } } if(ret){ ret = ri.makeUrl(ret.url); } } baseUpdateCurSrc(elem, ret); }; } document.addEventListener('load', function(e){ if(e.target.nodeName.toUpperCase() == 'IMG'){ updateCurSrc(e.target); } }, true); Object.defineProperty(HTMLImageElement.prototype, 'currentSrc', { set: function() { if(window.console && console.warn){ console.warn('currentSrc can\'t be set on img element'); } }, get: function(){ if(this.complete){ updateCurSrc(this); } return (!this.src && !this.srcset) ? '' : this.riCurrentSrc || ''; }, enumerable: true, configurable: true }); })(); } if(window.HTMLSourceElement && !('srcset' in document.createElement('source'))){ ['srcset', 'sizes'].forEach(function(idl){ Object.defineProperty(HTMLSourceElement.prototype, idl, { set: function( value ) { this.setAttribute( idl, value ); }, get: function() { return this.getAttribute( idl ) || ''; }, enumerable: true, configurable: true }); }); } })(); } riobserver.start(); } if ( !isReady ) { document.addEventListener("DOMContentLoaded", function(event) { isReady = true; }); } }));