1
0
mirror of https://github.com/danbee/danbarber.me synced 2025-03-04 08:59:10 +00:00

Update picturefill.

This commit is contained in:
Daniel Barber 2015-05-28 11:10:20 +01:00
parent c7b51ffe8c
commit b6b8ff2e81

View File

@ -1,6 +1,6 @@
/*! Picturefill - v2.1.0 - 2014-08-20 /*! Picturefill - v2.3.1 - 2015-04-09
* http://scottjehl.github.io/picturefill * http://scottjehl.github.io/picturefill
* Copyright (c) 2014 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT */ * Copyright (c) 2015 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT */
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license */ /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license */
window.matchMedia || (window.matchMedia = function() { window.matchMedia || (window.matchMedia = function() {
@ -52,13 +52,28 @@ window.matchMedia || (window.matchMedia = function() {
* License: MIT/GPLv2 * License: MIT/GPLv2
* Spec: http://picture.responsiveimages.org/ * Spec: http://picture.responsiveimages.org/
*/ */
(function( w, doc ) { (function( w, doc, image ) {
// Enable strict mode // Enable strict mode
"use strict"; "use strict";
function expose(picturefill) {
/* expose picturefill */
if ( typeof module === "object" && typeof module.exports === "object" ) {
// CommonJS, just export
module.exports = picturefill;
} else if ( typeof define === "function" && define.amd ) {
// AMD support
define( "picturefill", function() { return picturefill; } );
}
if ( typeof w === "object" ) {
// If no AMD and we are in the browser, attach to window
w.picturefill = picturefill;
}
}
// If picture is supported, well, that's awesome. Let's get outta here... // If picture is supported, well, that's awesome. Let's get outta here...
if ( w.HTMLPictureElement ) { if ( w.HTMLPictureElement ) {
w.picturefill = function() { }; expose(function() { });
return; return;
} }
@ -66,24 +81,37 @@ window.matchMedia || (window.matchMedia = function() {
doc.createElement( "picture" ); doc.createElement( "picture" );
// local object for method references and testing exposure // local object for method references and testing exposure
var pf = {}; var pf = w.picturefill || {};
var regWDesc = /\s+\+?\d+(e\d+)?w/;
// namespace // namespace
pf.ns = "picturefill"; pf.ns = "picturefill";
// srcset support test // srcset support test
pf.srcsetSupported = "srcset" in doc.createElement( "img" ); (function() {
pf.sizesSupported = w.HTMLImageElement.sizes; pf.srcsetSupported = "srcset" in image;
pf.sizesSupported = "sizes" in image;
pf.curSrcSupported = "currentSrc" in image;
})();
// just a string trim workaround // just a string trim workaround
pf.trim = function( str ) { pf.trim = function( str ) {
return str.trim ? str.trim() : str.replace( /^\s+|\s+$/g, "" ); return str.trim ? str.trim() : str.replace( /^\s+|\s+$/g, "" );
}; };
// just a string endsWith workaround /**
pf.endsWith = function( str, suffix ) { * Gets a string and returns the absolute URL
return str.endsWith ? str.endsWith( suffix ) : str.indexOf( suffix, str.length - suffix.length ) !== -1; * @param src
* @returns {String} absolute URL
*/
pf.makeUrl = (function() {
var anchor = doc.createElement( "a" );
return function(src) {
anchor.href = src;
return anchor.href;
}; };
})();
/** /**
* Shortcut method for https://w3c.github.io/webappsec/specs/mixedcontent/#restricts-mixed-content ( for easy overriding in tests ) * Shortcut method for https://w3c.github.io/webappsec/specs/mixedcontent/#restricts-mixed-content ( for easy overriding in tests )
@ -94,13 +122,12 @@ window.matchMedia || (window.matchMedia = function() {
/** /**
* Shortcut method for matchMedia ( for easy overriding in tests ) * Shortcut method for matchMedia ( for easy overriding in tests )
*/ */
pf.matchesMedia = function( media ) { pf.matchesMedia = function( media ) {
return w.matchMedia && w.matchMedia( media ).matches; return w.matchMedia && w.matchMedia( media ).matches;
}; };
/** // Shortcut method for `devicePixelRatio` ( for easy overriding in tests )
* Shortcut method for `devicePixelRatio` ( for easy overriding in tests )
*/
pf.getDpr = function() { pf.getDpr = function() {
return ( w.devicePixelRatio || 1 ); return ( w.devicePixelRatio || 1 );
}; };
@ -110,87 +137,99 @@ window.matchMedia || (window.matchMedia = function() {
* http://dev.w3.org/csswg/css-values-3/#length-value * http://dev.w3.org/csswg/css-values-3/#length-value
*/ */
pf.getWidthFromLength = function( length ) { pf.getWidthFromLength = function( length ) {
// If a length is specified and doesnt contain a percentage, and it is greater than 0 or using `calc`, use it. Else, use the `100vw` default. var cssValue;
length = length && length.indexOf( "%" ) > -1 === false && ( parseFloat( length ) > 0 || length.indexOf( "calc(" ) > -1 ) ? length : "100vw"; // If a length is specified and doesnt contain a percentage, and it is greater than 0 or using `calc`, use it. Else, abort.
if ( !(length && length.indexOf( "%" ) > -1 === false && ( parseFloat( length ) > 0 || length.indexOf( "calc(" ) > -1 )) ) {
return false;
}
/** /**
* If length is specified in `vw` units, use `%` instead since the div were measuring * If length is specified in `vw` units, use `%` instead since the div were measuring
* is injected at the top of the document. * is injected at the top of the document.
* *
* TODO: maybe we should put this behind a feature test for `vw`? * TODO: maybe we should put this behind a feature test for `vw`? The risk of doing this is possible browser inconsistancies with vw vs %
*/ */
length = length.replace( "vw", "%" ); length = length.replace( "vw", "%" );
// Create a cached element for getting length value widths // Create a cached element for getting length value widths
if ( !pf.lengthEl ) { if ( !pf.lengthEl ) {
pf.lengthEl = doc.createElement( "div" ); pf.lengthEl = doc.createElement( "div" );
doc.documentElement.insertBefore( pf.lengthEl, doc.documentElement.firstChild );
// Positioning styles help prevent padding/margin/width on `html` or `body` from throwing calculations off.
pf.lengthEl.style.cssText = "border:0;display:block;font-size:1em;left:0;margin:0;padding:0;position:absolute;visibility:hidden";
// Add a class, so that everyone knows where this element comes from
pf.lengthEl.className = "helper-from-picturefill-js";
} }
// Positioning styles help prevent padding/margin/width on `html` from throwing calculations off. pf.lengthEl.style.width = "0px";
pf.lengthEl.style.cssText = "position: absolute; left: 0; width: " + length + ";";
if ( pf.lengthEl.offsetWidth <= 0 ) { try {
// Something has gone wrong. `calc()` is in use and unsupported, most likely. Default to `100vw` (`100%`, for broader support.): pf.lengthEl.style.width = length;
pf.lengthEl.style.cssText = "width: 100%;"; } catch ( e ) {}
doc.body.appendChild(pf.lengthEl);
cssValue = pf.lengthEl.offsetWidth;
if ( cssValue <= 0 ) {
cssValue = false;
} }
return pf.lengthEl.offsetWidth; doc.body.removeChild( pf.lengthEl );
return cssValue;
}; };
// container of supported mime types that one might need to qualify before using pf.detectTypeSupport = function( type, typeUri ) {
pf.types = {};
// Add support for standard mime types.
pf.types["image/jpeg"] = true;
pf.types["image/gif"] = true;
pf.types["image/png"] = true;
// test svg support
pf.types[ "image/svg+xml" ] = doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1");
// test webp support, only when the markup calls for it
pf.types[ "image/webp" ] = function() {
// based on Modernizr's lossless img-webp test // based on Modernizr's lossless img-webp test
// note: asynchronous // note: asynchronous
var img = new w.Image(), var image = new w.Image();
type = "image/webp"; image.onerror = function() {
img.onerror = function() {
pf.types[ type ] = false; pf.types[ type ] = false;
picturefill(); picturefill();
}; };
img.onload = function() { image.onload = function() {
pf.types[ type ] = img.width === 1; pf.types[ type ] = image.width === 1;
picturefill(); picturefill();
}; };
img.src = "data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA="; image.src = typeUri;
return "pending";
};
// container of supported mime types that one might need to qualify before using
pf.types = pf.types || {};
pf.initTypeDetects = function() {
// Add support for standard mime types
pf.types[ "image/jpeg" ] = true;
pf.types[ "image/gif" ] = true;
pf.types[ "image/png" ] = true;
pf.types[ "image/svg+xml" ] = doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1");
pf.types[ "image/webp" ] = pf.detectTypeSupport("image/webp", "data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=");
}; };
/**
* Takes a source element and checks if its type attribute is present and if so, supported
* Note: for type tests that require a async logic,
* you can define them as a function that'll run only if that type needs to be tested. Just make the test function call picturefill again when it is complete.
* see the async webp test above for example
*/
pf.verifyTypeSupport = function( source ) { pf.verifyTypeSupport = function( source ) {
var type = source.getAttribute( "type" ); var type = source.getAttribute( "type" );
// if type attribute exists, return test result, otherwise return true // if type attribute exists, return test result, otherwise return true
if ( type === null || type === "" ) { if ( type === null || type === "" ) {
return true; return true;
} else { } else {
var pfType = pf.types[ type ];
// if the type test is a function, run it and return "pending" status. The function will rerun picturefill on pending elements once finished. // if the type test is a function, run it and return "pending" status. The function will rerun picturefill on pending elements once finished.
if ( typeof( pf.types[ type ] ) === "function" ) { if ( typeof pfType === "string" && pfType !== "pending") {
pf.types[ type ](); pf.types[ type ] = pf.detectTypeSupport( type, pfType );
return "pending";
} else if ( typeof pfType === "function" ) {
pfType();
return "pending"; return "pending";
} else { } else {
return pf.types[ type ]; return pfType;
} }
} }
}; };
/** // Parses an individual `size` and returns the length, and optional media query
* Parses an individual `size` and returns the length, and optional media query
*/
pf.parseSize = function( sourceSizeStr ) { pf.parseSize = function( sourceSizeStr ) {
var match = /(\([^)]+\))?\s*(.+)/g.exec( sourceSizeStr ); var match = /(\([^)]+\))?\s*(.+)/g.exec( sourceSizeStr );
return { return {
@ -199,9 +238,7 @@ window.matchMedia || (window.matchMedia = function() {
}; };
}; };
/** // Takes a string of sizes and returns the width in pixels as a number
* Takes a string of sizes and returns the width in pixels as a number
*/
pf.findWidthFromSourceSize = function( sourceSizeListStr ) { pf.findWidthFromSourceSize = function( sourceSizeListStr ) {
// Split up source size list, ie ( max-width: 30em ) 100%, ( max-width: 50em ) 50%, 33% // Split up source size list, ie ( max-width: 30em ) 100%, ( max-width: 50em ) 50%, 33%
// or (min-width:30em) calc(30% - 15px) // or (min-width:30em) calc(30% - 15px)
@ -219,17 +256,17 @@ window.matchMedia || (window.matchMedia = function() {
if ( !length ) { if ( !length ) {
continue; continue;
} }
if ( !media || pf.matchesMedia( media ) ) {
// if there is no media query or it matches, choose this as our winning length // if there is no media query or it matches, choose this as our winning length
// and end algorithm if ( (!media || pf.matchesMedia( media )) &&
winningLength = length; // pass the length to a method that can properly determine length
// in pixels based on these formats: http://dev.w3.org/csswg/css-values-3/#length-value
(winningLength = pf.getWidthFromLength( length )) ) {
break; break;
} }
} }
// pass the length to a method that can properly determine length //if we have no winningLength fallback to 100vw
// in pixels based on these formats: http://dev.w3.org/csswg/css-values-3/#length-value return winningLength || Math.max(w.innerWidth || 0, doc.documentElement.clientWidth);
return pf.getWidthFromLength( winningLength );
}; };
pf.parseSrcset = function( srcset ) { pf.parseSrcset = function( srcset ) {
@ -255,7 +292,7 @@ window.matchMedia || (window.matchMedia = function() {
if ( pos !== -1 ) { if ( pos !== -1 ) {
url = srcset.slice( 0, pos ); url = srcset.slice( 0, pos );
var last = url[ url.length - 1 ]; var last = url.slice(-1);
// 6. If url ends with a U+002C COMMA character (,), remove that character from url // 6. If url ends with a U+002C COMMA character (,), remove that character from url
// and let descriptors be the empty string. Otherwise, follow these substeps // and let descriptors be the empty string. Otherwise, follow these substeps
@ -306,8 +343,7 @@ window.matchMedia || (window.matchMedia = function() {
if ( sizeDescriptor ) { if ( sizeDescriptor ) {
var splitDescriptor = sizeDescriptor.split(" "); var splitDescriptor = sizeDescriptor.split(" ");
for (var i = splitDescriptor.length + 1; i >= 0; i--) { for (var i = splitDescriptor.length - 1; i >= 0; i--) {
if ( splitDescriptor[ i ] !== undefined ) {
var curr = splitDescriptor[ i ], var curr = splitDescriptor[ i ],
lastchar = curr && curr.slice( curr.length - 1 ); lastchar = curr && curr.slice( curr.length - 1 );
@ -319,7 +355,6 @@ window.matchMedia || (window.matchMedia = function() {
} }
} }
} }
}
return resCandidate || 1; return resCandidate || 1;
}; };
@ -348,7 +383,7 @@ window.matchMedia || (window.matchMedia = function() {
return formattedCandidates; return formattedCandidates;
}; };
/* /**
* if it's an img element and it has a srcset property, * if it's an img element and it has a srcset property,
* we need to remove the attribute so we can manipulate src * we need to remove the attribute so we can manipulate src
* (the property's existence infers native srcset support, and a srcset-supporting browser will prioritize srcset's value over our winning picture candidate) * (the property's existence infers native srcset support, and a srcset-supporting browser will prioritize srcset's value over our winning picture candidate)
@ -357,13 +392,12 @@ window.matchMedia || (window.matchMedia = function() {
pf.dodgeSrcset = function( img ) { pf.dodgeSrcset = function( img ) {
if ( img.srcset ) { if ( img.srcset ) {
img[ pf.ns ].srcset = img.srcset; img[ pf.ns ].srcset = img.srcset;
img.removeAttribute( "srcset" ); img.srcset = "";
img.setAttribute( "data-pfsrcset", img[ pf.ns ].srcset );
} }
}; };
/* // Accept a source or img element and process its srcset and sizes attrs
* Accept a source or img element and process its srcset and sizes attrs
*/
pf.processSourceSet = function( el ) { pf.processSourceSet = function( el ) {
var srcset = el.getAttribute( "srcset" ), var srcset = el.getAttribute( "srcset" ),
sizes = el.getAttribute( "sizes" ), sizes = el.getAttribute( "sizes" ),
@ -380,6 +414,66 @@ window.matchMedia || (window.matchMedia = function() {
return candidates; return candidates;
}; };
pf.backfaceVisibilityFix = function( picImg ) {
// See: https://github.com/scottjehl/picturefill/issues/332
var style = picImg.style || {},
WebkitBackfaceVisibility = "webkitBackfaceVisibility" in style,
currentZoom = style.zoom;
if (WebkitBackfaceVisibility) {
style.zoom = ".999";
WebkitBackfaceVisibility = picImg.offsetWidth;
style.zoom = currentZoom;
}
};
pf.setIntrinsicSize = (function() {
var urlCache = {};
var setSize = function( picImg, width, res ) {
if ( width ) {
picImg.setAttribute( "width", parseInt(width / res, 10) );
}
};
return function( picImg, bestCandidate ) {
var img;
if ( !picImg[ pf.ns ] || w.pfStopIntrinsicSize ) {
return;
}
if ( picImg[ pf.ns ].dims === undefined ) {
picImg[ pf.ns].dims = picImg.getAttribute("width") || picImg.getAttribute("height");
}
if ( picImg[ pf.ns].dims ) { return; }
if ( bestCandidate.url in urlCache ) {
setSize( picImg, urlCache[bestCandidate.url], bestCandidate.resolution );
} else {
img = doc.createElement( "img" );
img.onload = function() {
urlCache[bestCandidate.url] = img.width;
//IE 10/11 don't calculate width for svg outside document
if ( !urlCache[bestCandidate.url] ) {
try {
doc.body.appendChild( img );
urlCache[bestCandidate.url] = img.width || img.offsetWidth;
doc.body.removeChild( img );
} catch(e){}
}
if ( picImg.src === bestCandidate.url ) {
setSize( picImg, urlCache[bestCandidate.url], bestCandidate.resolution );
}
picImg = null;
img.onload = null;
img = null;
};
img.src = bestCandidate.url;
}
};
})();
pf.applyBestCandidate = function( candidates, picImg ) { pf.applyBestCandidate = function( candidates, picImg ) {
var candidate, var candidate,
length, length,
@ -398,17 +492,28 @@ window.matchMedia || (window.matchMedia = function() {
} }
} }
if ( bestCandidate && !pf.endsWith( picImg.src, bestCandidate.url ) ) { if ( bestCandidate ) {
bestCandidate.url = pf.makeUrl( bestCandidate.url );
if ( picImg.src !== bestCandidate.url ) {
if ( pf.restrictsMixedContent() && bestCandidate.url.substr(0, "http:".length).toLowerCase() === "http:" ) { if ( pf.restrictsMixedContent() && bestCandidate.url.substr(0, "http:".length).toLowerCase() === "http:" ) {
if ( typeof console !== undefined ) { if ( window.console !== undefined ) {
console.warn( "Blocked mixed content image " + bestCandidate.url ); console.warn( "Blocked mixed content image " + bestCandidate.url );
} }
} else { } else {
picImg.src = bestCandidate.url; picImg.src = bestCandidate.url;
// currentSrc attribute and property to match // currentSrc attribute and property to match
// http://picture.responsiveimages.org/#the-img-element // http://picture.responsiveimages.org/#the-img-element
if ( !pf.curSrcSupported ) {
picImg.currentSrc = picImg.src; picImg.currentSrc = picImg.src;
} }
pf.backfaceVisibilityFix( picImg );
}
}
pf.setIntrinsicSize(picImg, bestCandidate);
} }
}; };
@ -416,7 +521,7 @@ window.matchMedia || (window.matchMedia = function() {
return a.resolution - b.resolution; return a.resolution - b.resolution;
}; };
/* /**
* In IE9, <source> elements get removed if they aren't children of * In IE9, <source> elements get removed if they aren't children of
* video elements. Thus, we conditionally wrap source elements * video elements. Thus, we conditionally wrap source elements
* using <!--[if IE 9]><video style="display: none;"><![endif]--> * using <!--[if IE 9]><video style="display: none;"><![endif]-->
@ -436,7 +541,7 @@ window.matchMedia || (window.matchMedia = function() {
} }
}; };
/* /**
* Find all `img` elements, and add them to the candidate list if they have * Find all `img` elements, and add them to the candidate list if they have
* a `picture` parent, a `sizes` attribute in basic `srcset` supporting browsers, * a `picture` parent, a `sizes` attribute in basic `srcset` supporting browsers,
* a `srcset` attribute at all, and they havent been evaluated already. * a `srcset` attribute at all, and they havent been evaluated already.
@ -513,8 +618,8 @@ window.matchMedia || (window.matchMedia = function() {
parent, parent,
firstMatch, firstMatch,
candidates, candidates,
options = opt || {}; options = opt || {};
elements = options.elements || pf.getAllElements(); elements = options.elements || pf.getAllElements();
// Loop through all elements // Loop through all elements
@ -524,20 +629,25 @@ window.matchMedia || (window.matchMedia = function() {
firstMatch = undefined; firstMatch = undefined;
candidates = undefined; candidates = undefined;
// immediately skip non-`img` nodes
if ( element.nodeName.toUpperCase() !== "IMG" ) {
continue;
}
// expando for caching data on the img // expando for caching data on the img
if ( !element[ pf.ns ] ) { if ( !element[ pf.ns ] ) {
element[ pf.ns ] = {}; element[ pf.ns ] = {};
} }
// if the element has already been evaluated, skip it // if the element has already been evaluated, skip it unless
// unless `options.force` is set to true ( this, for example, // `options.reevaluate` is set to true ( this, for example,
// is set to true when running `picturefill` on `resize` ). // is set to true when running `picturefill` on `resize` ).
if ( !options.reevaluate && element[ pf.ns ].evaluated ) { if ( !options.reevaluate && element[ pf.ns ].evaluated ) {
continue; continue;
} }
// if `img` is in a `picture` element // if `img` is in a `picture` element
if ( parent.nodeName.toUpperCase() === "PICTURE" ) { if ( parent && parent.nodeName.toUpperCase() === "PICTURE" ) {
// IE9 video workaround // IE9 video workaround
pf.removeVideoShim( parent ); pf.removeVideoShim( parent );
@ -558,9 +668,8 @@ window.matchMedia || (window.matchMedia = function() {
} }
// Cache and remove `srcset` if present and were going to be doing `picture`/`srcset`/`sizes` polyfilling to it. // Cache and remove `srcset` if present and were going to be doing `picture`/`srcset`/`sizes` polyfilling to it.
if ( parent.nodeName.toUpperCase() === "PICTURE" || if ( ( parent && parent.nodeName.toUpperCase() === "PICTURE" ) ||
( element.srcset && !pf.srcsetSupported ) || ( !pf.sizesSupported && ( element.srcset && regWDesc.test( element.srcset ) ) ) ) {
( !pf.sizesSupported && ( element.srcset && element.srcset.indexOf("w") > -1 ) ) ) {
pf.dodgeSrcset( element ); pf.dodgeSrcset( element );
} }
@ -588,28 +697,32 @@ window.matchMedia || (window.matchMedia = function() {
* Also attaches picturefill on resize * Also attaches picturefill on resize
*/ */
function runPicturefill() { function runPicturefill() {
pf.initTypeDetects();
picturefill(); picturefill();
var intervalId = setInterval( function() { var intervalId = setInterval( function() {
// When the document has finished loading, stop checking for new images // When the document has finished loading, stop checking for new images
// https://github.com/ded/domready/blob/master/ready.js#L15 // https://github.com/ded/domready/blob/master/ready.js#L15
picturefill(); picturefill();
if ( /^loaded|^i|^c/.test( doc.readyState ) ) { if ( /^loaded|^i|^c/.test( doc.readyState ) ) {
clearInterval( intervalId ); clearInterval( intervalId );
return; return;
} }
}, 250 ); }, 250 );
if ( w.addEventListener ) {
var resizeThrottle; var resizeTimer;
w.addEventListener( "resize", function() { var handleResize = function() {
if (!w._picturefillWorking) {
w._picturefillWorking = true;
w.clearTimeout( resizeThrottle );
resizeThrottle = w.setTimeout( function() {
picturefill({ reevaluate: true }); picturefill({ reevaluate: true });
w._picturefillWorking = false; };
}, 60 ); function checkResize() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout( handleResize, 60 );
} }
}, false );
if ( w.addEventListener ) {
w.addEventListener( "resize", checkResize, false );
} else if ( w.attachEvent ) {
w.attachEvent( "onresize", checkResize );
} }
} }
@ -618,16 +731,6 @@ window.matchMedia || (window.matchMedia = function() {
/* expose methods for testing */ /* expose methods for testing */
picturefill._ = pf; picturefill._ = pf;
/* expose picturefill */ expose( picturefill );
if ( typeof module === "object" && typeof module.exports === "object" ) {
// CommonJS, just export
module.exports = picturefill;
} else if ( typeof define === "function" && define.amd ){
// AMD support
define( function() { return picturefill; } );
} else if ( typeof w === "object" ) {
// If no AMD and we are in the browser, attach to window
w.picturefill = picturefill;
}
} )( this, this.document ); } )( window, window.document, new window.Image() );