diff --git a/.travis.yml b/.travis.yml index 0029787..bff1f9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,10 @@ php: - 7.0 env: - - WP_VERSION=latest WP_MULTISITE=0 - - WP_VERSION=latest WP_MULTISITE=1 + - WP_VERSION=trunk WP_MULTISITE=0 + - WP_VERSION=trunk WP_MULTISITE=1 + - WP_VERSION=4.3 WP_MULTISITE=0 + - WP_VERSION=4.3 WP_MULTISITE=1 - WP_VERSION=4.2 WP_MULTISITE=0 - WP_VERSION=4.2 WP_MULTISITE=1 - WP_VERSION=4.1 WP_MULTISITE=0 diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh index a3f1184..5baa6cb 100755 --- a/bin/install-wp-tests.sh +++ b/bin/install-wp-tests.sh @@ -14,21 +14,56 @@ WP_VERSION=${5-latest} WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib} WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/} +download() { + if [ `which curl` ]; then + curl -s "$1" > "$2"; + elif [ `which wget` ]; then + wget -nv -O "$2" "$1" + fi +} + +if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then + WP_TESTS_TAG="tags/$WP_VERSION" +elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + WP_TESTS_TAG="trunk" +else + # http serves a single offer, whereas https serves multiple. we only want one + download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json + grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json + LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') + if [[ -z "$LATEST_VERSION" ]]; then + echo "Latest WordPress version could not be found" + exit 1 + fi + WP_TESTS_TAG="tags/$LATEST_VERSION" +fi + set -ex install_wp() { + + if [ -d $WP_CORE_DIR ]; then + return; + fi + mkdir -p $WP_CORE_DIR - if [ $WP_VERSION == 'latest' ]; then - local ARCHIVE_NAME='latest' + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p /tmp/wordpress-nightly + download https://wordpress.org/nightly-builds/wordpress-latest.zip /tmp/wordpress-nightly/wordpress-nightly.zip + unzip -q /tmp/wordpress-nightly/wordpress-nightly.zip -d /tmp/wordpress-nightly/ + mv /tmp/wordpress-nightly/wordpress/* $WP_CORE_DIR else - local ARCHIVE_NAME="wordpress-$WP_VERSION" + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz + tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR fi - wget -nv -O /tmp/wordpress.tar.gz https://wordpress.org/${ARCHIVE_NAME}.tar.gz - tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR - - wget -nv -O $WP_CORE_DIR/wp-content/db.php https://raw.github.com/markoheijnen/wp-mysqli/master/db.php + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php } install_test_suite() { @@ -39,17 +74,24 @@ install_test_suite() { local ioption='-i' fi - # set up testing suite - mkdir -p $WP_TESTS_DIR + # set up testing suite if it doesn't yet exist + if [ ! -d $WP_TESTS_DIR ]; then + # set up testing suite + mkdir -p $WP_TESTS_DIR + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes + fi + cd $WP_TESTS_DIR - svn co --quiet https://develop.svn.wordpress.org/trunk/tests/phpunit/includes/ - - wget -nv -O wp-tests-config.php https://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php - sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" wp-tests-config.php - sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" wp-tests-config.php - sed $ioption "s/yourusernamehere/$DB_USER/" wp-tests-config.php - sed $ioption "s/yourpasswordhere/$DB_PASS/" wp-tests-config.php - sed $ioption "s|localhost|${DB_HOST}|" wp-tests-config.php + + if [ ! -f wp-tests-config.php ]; then + download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php + fi + } install_db() { diff --git a/class-respimg.php b/class-respimg.php index d645a9c..2050a46 100644 --- a/class-respimg.php +++ b/class-respimg.php @@ -1,7 +1,7 @@ + * Hacked up version of php-respimg: https://github.com/nwtn/php-respimg * * @package wp-respimg * @version 0.0.1 @@ -35,9 +35,10 @@ class Respimg extends Imagick { * * @access public * - * @param integer $columns The number of columns in the output image. 0 = maintain aspect ratio based on $rows. - * @param integer $rows The number of rows in the output image. 0 = maintain aspect ratio based on $columns. - * @param bool $optim Whether you intend to perform optimization on the resulting image. Note that setting this to `true` doesn’t actually perform any optimization. + * @param integer $columns The number of columns in the output image. 0 = maintain aspect ratio based on $rows. + * @param integer $rows The number of rows in the output image. 0 = maintain aspect ratio based on $columns. + * @param bool $optim Whether you intend to perform optimization on the resulting image. + * Note that setting this to 'true' doesn't actually perform any optimization. */ public function smartResize( $columns, $rows, $optim = false ) { @@ -61,41 +62,41 @@ public function smartResize( $columns, $rows, $optim = false ) { if ( ! $optim ) { $this->stripImage(); } - } - /** * Changes the size of an image to the given dimensions and removes any associated profiles. * * `thumbnailImage` changes the size of an image to the given dimensions and - * removes any associated profiles. The goal is to produce small low cost + * removes any associated profiles. The goal is to produce small low cost * thumbnail images suited for display on the Web. * * With the original Imagick thumbnailImage implementation, there is no way to choose a * resampling filter. This class recreates Imagick’s C implementation and adds this * additional feature. * - * Note: has been filed for this issue. + * Note: https://github.com/mkoppanen/imagick/issues/90 has been filed for this issue. * * @access public * - * @param integer $columns The number of columns in the output image. 0 = maintain aspect ratio based on $rows. - * @param integer $rows The number of rows in the output image. 0 = maintain aspect ratio based on $columns. - * @param bool $bestfit Treat $columns and $rows as a bounding box in which to fit the image. - * @param bool $fill Fill in the bounding box with the background colour. - * @param integer $filter The resampling filter to use. Refer to the list of filter constants at . + * @param integer $columns The number of columns in the output image. 0 = maintain aspect ratio based on $rows. + * @param integer $rows The number of rows in the output image. 0 = maintain aspect ratio based on $columns. + * @param bool $bestfit Treat $columns and $rows as a bounding box in which to fit the image. + * @param bool $fill Fill in the bounding box with the background colour. + * @param integer $filter The resampling filter to use. Refer to the list of filter constants at . * - * @return bool Indicates whether the operation was performed successfully. + * @return bool Indicates whether the operation was performed successfully. */ public function thumbnailImage( $columns, $rows, $bestfit = false, $fill = false, $filter = Imagick::FILTER_TRIANGLE ) { - // sample factor; defined in original ImageMagick thumbnailImage function - // the scale to which the image should be resized using the `sample` function + /* + * Sample factor; defined in original ImageMagick thumbnailImage function + * the scale to which the image should be resized using the 'sample' function. + */ $SampleFactor = 5; - // filter whitelist + // Filter whitelist. $filters = array( Imagick::FILTER_POINT, Imagick::FILTER_BOX, @@ -114,23 +115,23 @@ public function thumbnailImage( $columns, $rows, $bestfit = false, $fill = false Imagick::FILTER_SINC ); - // Parse parameters given to function + // Parse parameters given to function. $columns = (double) $columns; $rows = (double) $rows; $bestfit = (bool) $bestfit; $fill = (bool) $fill; - // We can’t resize to (0,0) + // We can’t resize to (0,0). if ( $rows < 1 && $columns < 1 ) { return false; } - // Set a default filter if an acceptable one wasn’t passed + // Set a default filter if an acceptable one wasn’t passed. if ( ! in_array( $filter, $filters ) ) { $filter = Imagick::FILTER_TRIANGLE; } - // figure out the output width and height + // Figure out the output width and height. $width = (double) $this->getImageWidth(); $height = (double) $this->getImageHeight(); $new_width = $columns; @@ -144,8 +145,10 @@ public function thumbnailImage( $columns, $rows, $bestfit = false, $fill = false $new_width = round( $y_factor * $width ); } - // if bestfit is true, the new_width/new_height of the image will be different than - // the columns/rows parameters; those will define a bounding box in which the image will be fit + /* + * If bestfit is true, the new_width/new_height of the image will be different than + * the columns/rows parameters; those will define a bounding box in which the image will be fit. + */ if ( $bestfit && $x_factor > $y_factor ) { $x_factor = $y_factor; $new_width = round( $y_factor * $width ); @@ -160,8 +163,10 @@ public function thumbnailImage( $columns, $rows, $bestfit = false, $fill = false $new_height = 1; } - // if we’re resizing the image to more than about 1/3 it’s original size - // then just use the resize function + /* + * If we’re resizing the image to more than about 1/3 it’s original size + * then just use the resize function. + */ if ( ( $x_factor * $y_factor ) > 0.1 ) { $this->resizeImage( $new_width, $new_height, $filter, 1 ); @@ -215,8 +220,10 @@ public function thumbnailImage( $columns, $rows, $bestfit = false, $fill = false $this->setImageProperty( 'Thumb::Document::Pages', '' ); } - // In case user wants to fill use extent for it rather than creating a new canvas - // …fill out the bounding box + /* + * In case user wants to fill use extent for it rather than creating a new canvas + * fill out the bounding box. + */ if ( $bestfit && $fill && ( $new_width != $columns || $new_height != $rows ) ) { $extent_x = 0; $extent_y = 0; @@ -233,7 +240,5 @@ public function thumbnailImage( $columns, $rows, $bestfit = false, $fill = false } return true; - } - } diff --git a/class-wp-image-editor-respimg.php b/class-wp-image-editor-respimg.php index 123d98e..1e27be1 100644 --- a/class-wp-image-editor-respimg.php +++ b/class-wp-image-editor-respimg.php @@ -11,7 +11,7 @@ require_once( ABSPATH . WPINC . '/class-wp-image-editor-imagick.php' ); /** - * WordPress Image Editor Class for Image Manipulation through Imagick PHP Module with php-respimg + * WordPress Image Editor Class for Image Manipulation through Imagick PHP Module with php-respimg. * * @package wp-respimg * @uses WP_Image_Editor_Imagick Extends class @@ -34,8 +34,11 @@ public function load() { return new WP_Error( 'error_loading_image', __('File doesn’t exist?'), $this->file ); } - /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */ - // Even though Imagick uses less PHP memory than GD, set higher limit for users that have low PHP.ini limits + /* + * This filter is documented in wp-includes/class-wp-image-editor-imagick.php + * + * Even though Imagick uses less PHP memory than GD, set higher limit for users that have low PHP.ini limits. + */ @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) ); try { @@ -44,7 +47,7 @@ public function load() { if ( ! $this->image->valid() ) { return new WP_Error( 'invalid_image', __('File is not an image.'), $this->file); } - // Select the first frame to handle animated images properly + // Select the first frame to handle animated images properly. if ( is_callable( array( $this->image, 'setIteratorIndex' ) ) ) { $this->image->setIteratorIndex(0); } @@ -200,8 +203,10 @@ public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = nu $this->image->setImagePage( $src_w, $src_h, 0, 0); if ( $dst_w || $dst_h ) { - // If destination width/height isn't specified, use same as - // width/height from source. + /* + * If destination width/height isn't specified, use same as + * width/height from source. + */ if ( ! $dst_w ) { $dst_w = $src_w; } @@ -222,5 +227,4 @@ public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = nu } return $this->update_size(); } - } diff --git a/js/picturefill.min.js b/js/picturefill.min.js index 26b1d36..5bb1dd8 100755 --- a/js/picturefill.min.js +++ b/js/picturefill.min.js @@ -1,4 +1,5 @@ -/*! Picturefill - v2.3.1 - 2015-04-09 -* http://scottjehl.github.io/picturefill -* Copyright (c) 2015 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT */ -window.matchMedia||(window.matchMedia=function(){"use strict";var a=window.styleMedia||window.media;if(!a){var b=document.createElement("style"),c=document.getElementsByTagName("script")[0],d=null;b.type="text/css",b.id="matchmediajs-test",c.parentNode.insertBefore(b,c),d="getComputedStyle"in window&&window.getComputedStyle(b,null)||b.currentStyle,a={matchMedium:function(a){var c="@media "+a+"{ #matchmediajs-test { width: 1px; } }";return b.styleSheet?b.styleSheet.cssText=c:b.textContent=c,"1px"===d.width}}}return function(b){return{matches:a.matchMedium(b||"all"),media:b||"all"}}}()),function(a,b,c){"use strict";function d(b){"object"==typeof module&&"object"==typeof module.exports?module.exports=b:"function"==typeof define&&define.amd&&define("picturefill",function(){return b}),"object"==typeof a&&(a.picturefill=b)}function e(a){var b,c,d,e,f,i=a||{};b=i.elements||g.getAllElements();for(var j=0,k=b.length;k>j;j++)if(c=b[j],d=c.parentNode,e=void 0,f=void 0,"IMG"===c.nodeName.toUpperCase()&&(c[g.ns]||(c[g.ns]={}),i.reevaluate||!c[g.ns].evaluated)){if(d&&"PICTURE"===d.nodeName.toUpperCase()){if(g.removeVideoShim(d),e=g.getMatch(c,d),e===!1)continue}else e=void 0;(d&&"PICTURE"===d.nodeName.toUpperCase()||!g.sizesSupported&&c.srcset&&h.test(c.srcset))&&g.dodgeSrcset(c),e?(f=g.processSourceSet(e),g.applyBestCandidate(f,c)):(f=g.processSourceSet(c),(void 0===c.srcset||c[g.ns].srcset)&&g.applyBestCandidate(f,c)),c[g.ns].evaluated=!0}}function f(){function c(){clearTimeout(d),d=setTimeout(h,60)}g.initTypeDetects(),e();var d,f=setInterval(function(){return e(),/^loaded|^i|^c/.test(b.readyState)?void clearInterval(f):void 0},250),h=function(){e({reevaluate:!0})};a.addEventListener?a.addEventListener("resize",c,!1):a.attachEvent&&a.attachEvent("onresize",c)}if(a.HTMLPictureElement)return void d(function(){});b.createElement("picture");var g=a.picturefill||{},h=/\s+\+?\d+(e\d+)?w/;g.ns="picturefill",function(){g.srcsetSupported="srcset"in c,g.sizesSupported="sizes"in c,g.curSrcSupported="currentSrc"in c}(),g.trim=function(a){return a.trim?a.trim():a.replace(/^\s+|\s+$/g,"")},g.makeUrl=function(){var a=b.createElement("a");return function(b){return a.href=b,a.href}}(),g.restrictsMixedContent=function(){return"https:"===a.location.protocol},g.matchesMedia=function(b){return a.matchMedia&&a.matchMedia(b).matches},g.getDpr=function(){return a.devicePixelRatio||1},g.getWidthFromLength=function(a){var c;if(!a||a.indexOf("%")>-1!=!1||!(parseFloat(a)>0||a.indexOf("calc(")>-1))return!1;a=a.replace("vw","%"),g.lengthEl||(g.lengthEl=b.createElement("div"),g.lengthEl.style.cssText="border:0;display:block;font-size:1em;left:0;margin:0;padding:0;position:absolute;visibility:hidden",g.lengthEl.className="helper-from-picturefill-js"),g.lengthEl.style.width="0px";try{g.lengthEl.style.width=a}catch(d){}return b.body.appendChild(g.lengthEl),c=g.lengthEl.offsetWidth,0>=c&&(c=!1),b.body.removeChild(g.lengthEl),c},g.detectTypeSupport=function(b,c){var d=new a.Image;return d.onerror=function(){g.types[b]=!1,e()},d.onload=function(){g.types[b]=1===d.width,e()},d.src=c,"pending"},g.types=g.types||{},g.initTypeDetects=function(){g.types["image/jpeg"]=!0,g.types["image/gif"]=!0,g.types["image/png"]=!0,g.types["image/svg+xml"]=b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1"),g.types["image/webp"]=g.detectTypeSupport("image/webp","data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=")},g.verifyTypeSupport=function(a){var b=a.getAttribute("type");if(null===b||""===b)return!0;var c=g.types[b];return"string"==typeof c&&"pending"!==c?(g.types[b]=g.detectTypeSupport(b,c),"pending"):"function"==typeof c?(c(),"pending"):c},g.parseSize=function(a){var b=/(\([^)]+\))?\s*(.+)/g.exec(a);return{media:b&&b[1],length:b&&b[2]}},g.findWidthFromSourceSize=function(c){for(var d,e=g.trim(c).split(/\s*,\s*/),f=0,h=e.length;h>f;f++){var i=e[f],j=g.parseSize(i),k=j.length,l=j.media;if(k&&(!l||g.matchesMedia(l))&&(d=g.getWidthFromLength(k)))break}return d||Math.max(a.innerWidth||0,b.documentElement.clientWidth)},g.parseSrcset=function(a){for(var b=[];""!==a;){a=a.replace(/^\s+/g,"");var c,d=a.search(/\s/g),e=null;if(-1!==d){c=a.slice(0,d);var f=c.slice(-1);if((","===f||""===c)&&(c=c.replace(/,+$/,""),e=""),a=a.slice(d+1),null===e){var g=a.indexOf(",");-1!==g?(e=a.slice(0,g),a=a.slice(g+1)):(e=a,a="")}}else c=a,a="";(c||e)&&b.push({url:c,descriptor:e})}return b},g.parseDescriptor=function(a,b){var c,d=b||"100vw",e=a&&a.replace(/(^\s+|\s+$)/g,""),f=g.findWidthFromSourceSize(d);if(e)for(var h=e.split(" "),i=h.length-1;i>=0;i--){var j=h[i],k=j&&j.slice(j.length-1);if("h"!==k&&"w"!==k||g.sizesSupported){if("x"===k){var l=j&&parseFloat(j,10);c=l&&!isNaN(l)?l:1}}else c=parseFloat(parseInt(j,10)/f)}return c||1},g.getCandidatesFromSourceSet=function(a,b){for(var c=g.parseSrcset(a),d=[],e=0,f=c.length;f>e;e++){var h=c[e];d.push({url:h.url,resolution:g.parseDescriptor(h.descriptor,b)})}return d},g.dodgeSrcset=function(a){a.srcset&&(a[g.ns].srcset=a.srcset,a.srcset="",a.setAttribute("data-pfsrcset",a[g.ns].srcset))},g.processSourceSet=function(a){var b=a.getAttribute("srcset"),c=a.getAttribute("sizes"),d=[];return"IMG"===a.nodeName.toUpperCase()&&a[g.ns]&&a[g.ns].srcset&&(b=a[g.ns].srcset),b&&(d=g.getCandidatesFromSourceSet(b,c)),d},g.backfaceVisibilityFix=function(a){var b=a.style||{},c="webkitBackfaceVisibility"in b,d=b.zoom;c&&(b.zoom=".999",c=a.offsetWidth,b.zoom=d)},g.setIntrinsicSize=function(){var c={},d=function(a,b,c){b&&a.setAttribute("width",parseInt(b/c,10))};return function(e,f){var h;e[g.ns]&&!a.pfStopIntrinsicSize&&(void 0===e[g.ns].dims&&(e[g.ns].dims=e.getAttribute("width")||e.getAttribute("height")),e[g.ns].dims||(f.url in c?d(e,c[f.url],f.resolution):(h=b.createElement("img"),h.onload=function(){if(c[f.url]=h.width,!c[f.url])try{b.body.appendChild(h),c[f.url]=h.width||h.offsetWidth,b.body.removeChild(h)}catch(a){}e.src===f.url&&d(e,c[f.url],f.resolution),e=null,h.onload=null,h=null},h.src=f.url)))}}(),g.applyBestCandidate=function(a,b){var c,d,e;a.sort(g.ascendingSort),d=a.length,e=a[d-1];for(var f=0;d>f;f++)if(c=a[f],c.resolution>=g.getDpr()){e=c;break}e&&(e.url=g.makeUrl(e.url),b.src!==e.url&&(g.restrictsMixedContent()&&"http:"===e.url.substr(0,"http:".length).toLowerCase()?void 0!==window.console&&console.warn("Blocked mixed content image "+e.url):(b.src=e.url,g.curSrcSupported||(b.currentSrc=b.src),g.backfaceVisibilityFix(b))),g.setIntrinsicSize(b,e))},g.ascendingSort=function(a,b){return a.resolution-b.resolution},g.removeVideoShim=function(a){var b=a.getElementsByTagName("video");if(b.length){for(var c=b[0],d=c.getElementsByTagName("source");d.length;)a.insertBefore(d[0],c);c.parentNode.removeChild(c)}},g.getAllElements=function(){for(var a=[],c=b.getElementsByTagName("img"),d=0,e=c.length;e>d;d++){var f=c[d];("PICTURE"===f.parentNode.nodeName.toUpperCase()||null!==f.getAttribute("srcset")||f[g.ns]&&null!==f[g.ns].srcset)&&a.push(f)}return a},g.getMatch=function(a,b){for(var c,d=b.childNodes,e=0,f=d.length;f>e;e++){var h=d[e];if(1===h.nodeType){if(h===a)return c;if("SOURCE"===h.nodeName.toUpperCase()){null!==h.getAttribute("src")&&void 0!==typeof console&&console.warn("The `src` attribute is invalid on `picture` `source` element; instead, use `srcset`.");var i=h.getAttribute("media");if(h.getAttribute("srcset")&&(!i||g.matchesMedia(i))){var j=g.verifyTypeSupport(h);if(j===!0){c=h;break}if("pending"===j)return!1}}}}return c},f(),e._=g,d(e)}(window,window.document,new window.Image); +/*! Picturefill - v3.0.1 - 2015-09-30 + * http://scottjehl.github.io/picturefill + * Copyright (c) 2015 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT + */ +!function(a){var b=navigator.userAgent;a.HTMLPictureElement&&/ecko/.test(b)&&b.match(/rv\:(\d+)/)&&RegExp.$1<41&&addEventListener("resize",function(){var b,c=document.createElement("source"),d=function(a){var b,d,e=a.parentNode;"PICTURE"===e.nodeName.toUpperCase()?(b=c.cloneNode(),e.insertBefore(b,e.firstElementChild),setTimeout(function(){e.removeChild(b)})):(!a._pfLastSize||a.offsetWidth>a._pfLastSize)&&(a._pfLastSize=a.offsetWidth,d=a.sizes,a.sizes+=",100vw",setTimeout(function(){a.sizes=d}))},e=function(){var a,b=document.querySelectorAll("picture > img, img[srcset][sizes]");for(a=0;a2.7?h=c+1:(f=b-c,e=Math.pow(a-.6,1.5),g=f*e,d&&(g+=.1*e),h=a+g):h=c>1?Math.sqrt(a*b):a,h>c}function h(a){var b,c=s.getSet(a),d=!1;"pending"!==c&&(d=r,c&&(b=s.setRes(c),s.applySetCandidate(b,a))),a[s.ns].evaled=d}function i(a,b){return a.res-b.res}function j(a,b,c){var d;return!c&&b&&(c=a[s.ns].sets,c=c&&c[c.length-1]),d=k(b,c),d&&(b=s.makeUrl(b),a[s.ns].curSrc=b,a[s.ns].curCan=d,d.res||_(d,d.set.sizes)),d}function k(a,b){var c,d,e;if(a&&b)for(e=s.parseSet(b),a=s.makeUrl(a),c=0;cc;c++)e=g[c],e[s.ns]=!0,f=e.getAttribute("srcset"),f&&b.push({srcset:f,media:e.getAttribute("media"),type:e.getAttribute("type"),sizes:e.getAttribute("sizes")})}function m(a,b){function c(b){var c,d=b.exec(a.substring(m));return d?(c=d[0],m+=c.length,c):void 0}function e(){var a,c,d,e,f,i,j,k,l,m=!1,o={};for(e=0;el?m=!0:c=l):W.test(j)&&"h"===i?((d||c)&&(m=!0),0===k?m=!0:d=k):m=!0;m||(o.url=g,a&&(o.w=a),c&&(o.d=c),d&&(o.h=d),d||c||a||(o.d=1),1===o.d&&(b.has1x=!0),o.set=b,n.push(o))}function f(){for(c(S),i="",j="in descriptor";;){if(k=a.charAt(m),"in descriptor"===j)if(d(k))i&&(h.push(i),i="",j="after descriptor");else{if(","===k)return m+=1,i&&h.push(i),void e();if("("===k)i+=k,j="in parens";else{if(""===k)return i&&h.push(i),void e();i+=k}}else if("in parens"===j)if(")"===k)i+=k,j="in descriptor";else{if(""===k)return h.push(i),void e();i+=k}else if("after descriptor"===j)if(d(k));else{if(""===k)return void e();j="in descriptor",m-=1}m+=1}}for(var g,h,i,j,k,l=a.length,m=0,n=[];;){if(c(T),m>=l)return n;g=c(U),h=[],","===g.slice(-1)?(g=g.replace(V,""),e()):f()}}function n(a){function b(a){function b(){f&&(g.push(f),f="")}function c(){g[0]&&(h.push(g),g=[])}for(var e,f="",g=[],h=[],i=0,j=0,k=!1;;){if(e=a.charAt(j),""===e)return b(),c(),h;if(k){if("*"===e&&"/"===a[j+1]){k=!1,j+=2,b();continue}j+=1}else{if(d(e)){if(a.charAt(j-1)&&d(a.charAt(j-1))||!f){j+=1;continue}if(0===i){b(),j+=1;continue}e=" "}else if("("===e)i+=1;else if(")"===e)i-=1;else{if(","===e){b(),c(),j+=1;continue}if("/"===e&&"*"===a.charAt(j+1)){k=!0,j+=2;continue}}f+=e,j+=1}}}function c(a){return k.test(a)&&parseFloat(a)>=0?!0:l.test(a)?!0:"0"===a||"-0"===a||"+0"===a?!0:!1}var e,f,g,h,i,j,k=/^(?:[+-]?[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?(?:ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmin|vmax|vw)$/i,l=/^calc\((?:[0-9a-z \.\+\-\*\/\(\)]+)\)$/i;for(f=b(a),g=f.length,e=0;g>e;e++)if(h=f[e],i=h[h.length-1],c(i)){if(j=i,h.pop(),0===h.length)return j;if(h=h.join(" "),s.matchesMedia(h))return j}return"100vw"}b.createElement("picture");var o,p,q,r,s={},t=function(){},u=b.createElement("img"),v=u.getAttribute,w=u.setAttribute,x=u.removeAttribute,y=b.documentElement,z={},A={algorithm:""},B="data-pfsrc",C=B+"set",D=navigator.userAgent,E=/rident/.test(D)||/ecko/.test(D)&&D.match(/rv\:(\d+)/)&&RegExp.$1>35,F="currentSrc",G=/\s+\+?\d+(e\d+)?w/,H=/(\([^)]+\))?\s*(.+)/,I=a.picturefillCFG,J="position:absolute;left:0;visibility:hidden;display:block;padding:0;border:none;font-size:1em;width:1em;overflow:hidden;clip:rect(0px, 0px, 0px, 0px)",K="font-size:100%!important;",L=!0,M={},N={},O=a.devicePixelRatio,P={px:1,"in":96},Q=b.createElement("a"),R=!1,S=/^[ \t\n\r\u000c]+/,T=/^[, \t\n\r\u000c]+/,U=/^[^ \t\n\r\u000c]+/,V=/[,]+$/,W=/^\d+$/,X=/^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/,Y=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,d||!1):a.attachEvent&&a.attachEvent("on"+b,c)},Z=function(a){var b={};return function(c){return c in b||(b[c]=a(c)),b[c]}},$=function(){var a=/^([\d\.]+)(em|vw|px)$/,b=function(){for(var a=arguments,b=0,c=a[0];++b in a;)c=c.replace(a[b],a[++b]);return c},c=Z(function(a){return"return "+b((a||"").toLowerCase(),/\band\b/g,"&&",/,/g,"||",/min-([a-z-\s]+):/g,"e.$1>=",/max-([a-z-\s]+):/g,"e.$1<=",/calc([^)]+)/g,"($1)",/(\d+[\.]*[\d]*)([a-z]+)/g,"($1 * e.$2)",/^(?!(e.[a-z]|[0-9\.&=|><\+\-\*\(\)\/])).*/gi,"")+";"});return function(b,d){var e;if(!(b in M))if(M[b]=!1,d&&(e=b.match(a)))M[b]=e[1]*P[e[2]];else try{M[b]=new Function("e",c(b))(P)}catch(f){}return M[b]}}(),_=function(a,b){return a.w?(a.cWidth=s.calcListLength(b||"100vw"),a.res=a.w/a.cWidth):a.res=a.d,a},aa=function(a){var c,d,e,f=a||{};if(f.elements&&1===f.elements.nodeType&&("IMG"===f.elements.nodeName.toUpperCase()?f.elements=[f.elements]:(f.context=f.elements,f.elements=null)),c=f.elements||s.qsa(f.context||b,f.reevaluate||f.reselect?s.sel:s.selShort),e=c.length){for(s.setupRun(f),R=!0,d=0;e>d;d++)s.fillImg(c[d],f);s.teardownRun(f)}};o=a.console&&console.warn?function(a){console.warn(a)}:t,F in u||(F="src"),z["image/jpeg"]=!0,z["image/gif"]=!0,z["image/png"]=!0,z["image/svg+xml"]=b.implementation.hasFeature("http://wwwindow.w3.org/TR/SVG11/feature#Image","1.1"),s.ns=("pf"+(new Date).getTime()).substr(0,9),s.supSrcset="srcset"in u,s.supSizes="sizes"in u,s.supPicture=!!a.HTMLPictureElement,s.supSrcset&&s.supPicture&&!s.supSizes&&!function(a){u.srcset="data:,a",a.src="data:,a",s.supSrcset=u.complete===a.complete,s.supPicture=s.supSrcset&&s.supPicture}(b.createElement("img")),s.selShort="picture>img,img[srcset]",s.sel=s.selShort,s.cfg=A,s.supSrcset&&(s.sel+=",img["+C+"]"),s.DPR=O||1,s.u=P,s.types=z,q=s.supSrcset&&!s.supSizes,s.setSize=t,s.makeUrl=Z(function(a){return Q.href=a,Q.href}),s.qsa=function(a,b){return a.querySelectorAll(b)},s.matchesMedia=function(){return a.matchMedia&&(matchMedia("(min-width: 0.1em)")||{}).matches?s.matchesMedia=function(a){return!a||matchMedia(a).matches}:s.matchesMedia=s.mMQ,s.matchesMedia.apply(this,arguments)},s.mMQ=function(a){return a?$(a):!0},s.calcLength=function(a){var b=$(a,!0)||!1;return 0>b&&(b=!1),b},s.supportsType=function(a){return a?z[a]:!0},s.parseSize=Z(function(a){var b=(a||"").match(H);return{media:b&&b[1],length:b&&b[2]}}),s.parseSet=function(a){return a.cands||(a.cands=m(a.srcset,a)),a.cands},s.getEmValue=function(){var a;if(!p&&(a=b.body)){var c=b.createElement("div"),d=y.style.cssText,e=a.style.cssText;c.style.cssText=J,y.style.cssText=K,a.style.cssText=K,a.appendChild(c),p=c.offsetWidth,a.removeChild(c),p=parseFloat(p,10),y.style.cssText=d,a.style.cssText=e}return p||16},s.calcListLength=function(a){if(!(a in N)||A.uT){var b=s.calcLength(n(a));N[a]=b?b:P.width}return N[a]},s.setRes=function(a){var b;if(a){b=s.parseSet(a);for(var c=0,d=b.length;d>c;c++)_(b[c],a.sizes)}return b},s.setRes.res=_,s.applySetCandidate=function(a,b){if(a.length){var c,d,e,f,h,k,l,m,n,o=b[s.ns],p=s.DPR;if(k=o.curSrc||b[F],l=o.curCan||j(b,k,a[0].set),l&&l.set===a[0].set&&(n=E&&!b.complete&&l.res-.1>p,n||(l.cached=!0,l.res>=p&&(h=l))),!h)for(a.sort(i),f=a.length,h=a[f-1],d=0;f>d;d++)if(c=a[d],c.res>=p){e=d-1,h=a[e]&&(n||k!==s.makeUrl(c.url))&&g(a[e].res,c.res,p,a[e].cached)?a[e]:c;break}h&&(m=s.makeUrl(h.url),o.curSrc=m,o.curCan=h,m!==k&&s.setSrc(b,h),s.setSize(b))}},s.setSrc=function(a,b){var c;a.src=b.url,"image/svg+xml"===b.set.type&&(c=a.style.width,a.style.width=a.offsetWidth+1+"px",a.offsetWidth+1&&(a.style.width=c))},s.getSet=function(a){var b,c,d,e=!1,f=a[s.ns].sets;for(b=0;bf?c=setTimeout(e,b-f):(c=null,a())};return function(){d=new Date,c||(c=setTimeout(e,b))}},h=y.clientHeight,i=function(){L=Math.max(a.innerWidth||0,y.clientWidth)!==P.width||y.clientHeight!==h,h=y.clientHeight,L&&s.fillImgs()};Y(a,"resize",g(i,99)),Y(b,"readystatechange",e)}(),s.picturefill=aa,s.fillImgs=aa,s.teardownRun=t,aa._=s,a.picturefillCFG={pf:s,push:function(a){var b=a.shift();"function"==typeof s[b]?s[b].apply(s,a):(A[b]=a[0],R&&s.fillImgs({reselect:!0}))}};for(;I&&I.length;)a.picturefillCFG.push(I.shift());a.picturefill=aa,"object"==typeof module&&"object"==typeof module.exports?module.exports=aa:"function"==typeof define&&define.amd&&define("picturefill",function(){return aa}),s.supPicture||(z["image/webp"]=e("image/webp","data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA=="))}(window,document); \ No newline at end of file diff --git a/readme.md b/readme.md index c91a780..732cd46 100644 --- a/readme.md +++ b/readme.md @@ -38,97 +38,71 @@ add_action( 'after_setup_theme', 'custom_theme_setup' ); --- +### Function Reference -#### tevkori_get_sizes( $id, $size, $args ) +#### wp_get_attachment_image_sizes( $size, $image_meta = null, $attachment_id = 0, $image_src = null ) -Returns a valid source size value for use in a 'sizes' attribute. The parameters include the ID of the image, the default size of the image, and an array or string containing of size information. The ID parameter is required. [Link](https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images/blob/master/wp-tevko-responsive-images.php#L28) +Create 'sizes' attribute value for an image. -***Usage Example*** +**Return:** (string|bool) A valid source size value for use in a 'sizes' attribute or false. -``` - -``` -By default, the sizes attribute will be declared as 100% of the viewport width when the viewport width is smaller than the width of the image, or to the width of the image itself when the viewport is larger than the image. In other words, this: +##### Parameters -`(max-width: {{image-width}}) 100vw, {{image-width}}` +**$size** (array|string) +Image size. Accepts any valid image size name ('thumbnail', 'medium', etc.), or an array of width and height values in pixels (in that order). -You can override those defaults by passing your own size values as set of arrays to the `$args` parameter. +**$image_meta** (array) (Optional) The image meta data as returned by 'wp_get_attachment_metadata()'. -*Example:* +**$attachment_id** (int) +(Optional) Image attachment ID. Either `$image_meta` or `$attachment_id` is needed when using the image size name as argument for `$size`. -``` -$args = array( - 'sizes' => array( - array( - 'size_value' => '10em', - 'mq_value' => '60em', - 'mq_name' => 'min-width' - ), - array( - 'size_value' => '20em', - 'mq_value' => '30em', - 'mq_name' => 'min-width' - ), - array( - 'size_value' => 'calc(100vm - 30px)' - ), - ) -); - -$sizes = tevkori_get_sizes( $id, 'medium', $args ); -``` +**$image_src** (string) +(Optional) The URL to the image file. -Which would output a sizes value of: -`(min-width: 60em) 10em, (min-width: 30em) 20em, calc(100vm - 30px)` +##### Usage Example ---- - -#### tevkori_get_sizes_string( $id, $size, $args) +``` + +``` -Returns A full 'sizes' attribute. The parameters include the ID of the image, the default size of the image, and an array or string containing of size information. The ID parameter is required. +By default, the sizes attribute will be declared as 100% of the viewport width when the viewport width is smaller than the width of the image, or to the width of the image itself when the viewport is larger than the image. In other words, this: -***Usage Example*** +`(max-width: {{image-width}}) 100vw, {{image-width}}` -``` - > -``` +You can override those defaults by adding a filter to `wp_get_attachment_image_sizes`. --- -#### tevkori_get_srcset_array( $id, $size ) -Returns an array of image source candidates for use in a 'srcset' attribute. The parameters include the ID of the image, the default size of the image, and An array of of srcset values. The ID parameter is required. [Link](https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images/blob/master/wp-tevko-responsive-images.php#L132) +#### wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $image_meta = null ) -***Usage Example*** +Retrieves the value for an image attachment's 'srcset' attribute. -``` -$sources = tevkori_get_srcset_array( 11, 'medium' ); +**Return:** (string|bool) A 'srcset' value string or false. -// Optionally remove a specific source from the srcset list. -foreach( $sources as $key => $source ) { - if ( strpos( $source, '300w' ) ) { - unset( $s[$key] ); - } -} +##### Parameters - -``` +**$attachment_id** (int) +Image attachment ID. ---- +**$size** (array|string) +Image size. Accepts any valid image size, or an array of width and height values in pixels (in that order). Default 'medium'. -#### tevkori_get_srcset_string( $id, $size ) +**$image_meta** (array) +(Optional) The image meta data as returned by 'wp_get_attachment_metadata()'. -Returns A full 'srcset' attribute. The parameters include the ID of the image and its default size. The ID parameter is required. [Link](https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images/blob/master/wp-tevko-responsive-images.php#L196) -***Usage Example*** +##### Usage Example ``` - > + ``` -**Dependencies** +--- + +### Dependencies -The only external dependency included in this plugin is [Picturefill](http://scottjehl.github.io/picturefill/) - v2.3.0. If you would like to remove Picturefill (see notes about [browser support](http://scottjehl.github.io/picturefill/#support)), add the following to your functions.php file: +The only external dependency included in this plugin is [Picturefill](http://scottjehl.github.io/picturefill/) - v3.0.1. If you would like to remove Picturefill (see notes about [browser support](http://scottjehl.github.io/picturefill/#support)), add the following to your functions.php file: function mytheme_dequeue_scripts() { wp_dequeue_script('picturefill'); @@ -140,18 +114,30 @@ We use a hook because if you attempt to dequeue a script before it's enqueued, w ## Version -2.5.2 +3.0.0 ## Changelog +**3.0.0** + +- Deprecates all core functions that will be merged into WordPress core in 4.4. +- Adds compatibility shims for sites using the plugin's internal functions and hooks. +- Adds a new display filter callback which can be use as general utility function for adding srcset and sizes attributes. +- Fixes a bug when `wp_get_attachment_metadata()` failed to return an array. +- Update our tests to be compatible with WordPress 4.4 +- Upgrade to Picturefill 3.0.1 +- Clean up inline docs. + +**2.5.2** + - Numerous performance and usability improvements -- Pass height and width to `tevkori_get_sizes() +- Pass height and width to `tevkori_get_sizes()` - Improved regex in display filter - Avoid calling `wp_get_attachment_image_src()` in srcset functions - Improved coding standards - Removed second regular expression in content filter - Improved cache warning function -- Change default `$size` value for all function to 'medium' +- Change default `$size` value for all functions to 'medium' **2.5.1** @@ -163,14 +149,14 @@ We use a hook because if you attempt to dequeue a script before it's enqueued, w - Responsify all post images by adding `srcset` and `sizes` through a display filter. - Improve method used to build paths in `tevkori_get_srcset_array()` -- Adds linthub config files +- Added Linthub config files - Returns single source arrays in `tevkori_get_srcset_array()` - Add tests for PHP7 to our Travis matrix - Add test coverage for `tevkori_filter_attachment_image_attributes()` **2.4.0** -- Added filter for tevkori_get_sizes, with tests +- Added filter for `tevkori_get_sizes`, with tests - Added Composer support - Compare aspect ratio in relative values, not absolute values - Cleanup of code style and comments added @@ -185,7 +171,7 @@ We use a hook because if you attempt to dequeue a script before it's enqueued, w - Added 'sudo: false' to travis.ci to use new TravisCI infrastructure - Removing the srcset and sizes attributes if there is only one source present for the image - Use edited image hash to filter out originals from edited images -- Make output of tevkori_get_srcset_array filterable +- Make output of `tevkori_get_srcset_array` filterable **2.3.1** @@ -200,14 +186,14 @@ We use a hook because if you attempt to dequeue a script before it's enqueued, w - Added advanced image compression option (available by adding hook to functions.php) - Duplicate entires now filtered out from srcset array - Upgrade Picturefill to 2.3.1 -- Refactoring plugin JS, including a switch to ajax for updating the srcset value when the image is changed in the editor -- Now using wp_get_attachment_image_attributes filter for post thumbnails +- Refactoring plugin JavaScript, including a switch to ajax for updating the srcset value when the image is changed in the editor +- Now using `wp_get_attachment_image_attributes` filter for post thumbnails - Readme and other general code typo fixes - Gallery images will now contain a srcset attribute **2.2.1** -- JS patch for WordPress +- JavaScript patch for WordPress **2.2.0** @@ -215,18 +201,17 @@ We use a hook because if you attempt to dequeue a script before it's enqueued, w - Updated to Picturefill v2.3.0 - Extensive documentation included in readme - Integrated testing with Travis CLI -- Check if wp.media exists before running JS +- Check if wp.media exists before running JavaScript - Account for rounding variance when matching ascpect ratios **2.1.1** -- Adding in wp-tevko-responsive-images.js after file not found to be in wordpress repository -- Adjusts the aspect ratio check in tevkori_get_srcset_array() to account for rounding variance +- Adding in wp-tevko-responsive-images.js after file not found to be in WordPress repository +- Adjusts the aspect ratio check in `tevkori_get_srcset_array()` to account for rounding variance **2.1.0** -- **This version introduces a breaking change** - there are now two functions. One returns an array of srcset values, and the other returns a string with the ``srcset=".."`` html needed to generate the responsive image. To retrieve the srcset array, use ``tevkori_get_srcset_array( $id, $size )`` - +- **This version introduces a breaking change**: There are now two functions. One returns an array of srcset values, and the other returns a string with the `srcset=".."` html needed to generate the responsive image. To retrieve the srcset array, use `tevkori_get_srcset_array( $id, $size )` - When the image size is changed in the post editor, the srcset values will adjust to match the change. **2.0.2** @@ -234,10 +219,12 @@ We use a hook because if you attempt to dequeue a script before it's enqueued, w - A bugfix correcting a divide by zero error. Some users may have seen this after upgrading to 2.0.1 **2.0.1** + - Only outputs the default WordPress sizes, giving theme developers the option to extend as needed - Added support for featured images **2.0.0** + - Uses [Picturefill 2.2.0 (Beta)](http://scottjehl.github.io/picturefill/) - Scripts are output to footer - Image sizes adjusted diff --git a/readme.txt b/readme.txt index b247914..2211bcb 100644 --- a/readme.txt +++ b/readme.txt @@ -1,10 +1,10 @@ === RICG Responsive Images === -Contributors: tevko, wilto, joemcgill, jaspermdegroot, chriscoyier, Michael McGinnis, ryelle, drrobotnik, nacin , georgestephanis, helen, wordpressdotorg, Bocoup +Contributors: tevko, wilto, joemcgill, jaspermdegroot, chriscoyier, Michael McGinnis, ryelle, drrobotnik, nacin, georgestephanis, helen, wordpressdotorg, Bocoup Donate link: https://app.etapestry.com/hosted/BoweryResidentsCommittee/OnlineDonation.html Tags: Responsive, Images, Responsive Images, SRCSET, Picturefill -Requires at least: 4.1 +Requires at least: 4.0 Tested up to: 4.3 -Stable tag: 2.5.2 +Stable tag: 3.0.0 License: GPLv2 License URI: http://www.gnu.org/licenses/gpl-2.0.txt @@ -16,6 +16,9 @@ Bringing automatic default responsive images to WordPress. This plugin works by including all available image sizes for each image upload. Whenever WordPress outputs the image through the media uploader, or whenever a featured image is generated, those sizes will be included in the image tag via the srcset attribute. +**Notice** +As of version 2.5.0, the plugin adds `srcset` and `sizes` attributes to images on the front end instead of adding them to the image markup saved in posts. + **Full documentation and contributor guidelines can be found on [Github](https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images)** == Installation == @@ -26,32 +29,40 @@ This plugin works by including all available image sizes for each image upload. == Changelog == += 3.0.0 = +* Deprecates all core functions that will be merged into WordPress core in 4.4. +* Adds compatibility shims for sites using the plugin's internal functions and hooks. +* Adds a new display filter callback which can be use as general utility function for adding srcset and sizes attributes. +* Fixes a bug when `wp_get_attachment_metadata()` failed to return an array. +* Update our tests to be compatible with WordPress 4.4 +* Upgrade to Picturefill 3.0.1 +* Clean up inline docs. + = 2.5.2 = * Numerous performance and usability improvements -* Pass height and width to `tevkori_get_sizes() +* Pass height and width to `tevkori_get_sizes()` * Improved regex in display filter * Avoid calling `wp_get_attachment_image_src()` in srcset functions * Improved coding standards * Removed second regular expression in content filter * Improved cache warning function -* Change default `$size` value for all function to 'medium' +* Change default `$size` value for all functions to 'medium' = 2.5.1 = * Query all images in single request before replacing * Minor fix to prevent a potential undefined variable notice * Remove third fallback query from the display filter - = 2.5.0 = * Responsify all post images by adding `srcset` and `sizes` through a display filter. * Improve method used to build paths in `tevkori_get_srcset_array()` -* Adds linthub config files +* Added Linthub config files * Returns single source arrays in `tevkori_get_srcset_array()` * Add tests for PHP7 to our Travis matrix * Add test coverage for `tevkori_filter_attachment_image_attributes()` = 2.4.0 = -* Added filter for tevkori_get_sizes, with tests +* Added filter for `tevkori_get_sizes`, with tests * Added Composer support * Compare aspect ratio in relative values, not absolute values * Cleanup of code style and comments added @@ -66,7 +77,7 @@ This plugin works by including all available image sizes for each image upload. * Added 'sudo: false' to travis.ci to use new TravisCI infrastructure * Removing the srcset and sizes attributes if there is only one source present for the image * Use edited image hash to filter out originals from edited images -* Make output of tevkori_get_srcset_array filterable +* Make output of `tevkori_get_srcset_array` filterable = 2.3.1 = * First char no longer stripped from file name if there's no slash @@ -79,28 +90,28 @@ This plugin works by including all available image sizes for each image upload. * Added advanced image compression option (available by adding hook to functions.php) * Duplicate entires now filtered out from srcset array * Upgrade Picturefill to 2.3.1 -* Refactoring plugin JS, including a switch to ajax for updating the srcset value when the image is changed in the editor -* Now using wp_get_attachment_image_attributes filter for post thumbnails +* Refactoring plugin JavaScript, including a switch to ajax for updating the srcset value when the image is changed in the editor +* Now using `wp_get_attachment_image_attributes` filter for post thumbnails * Readme and other general code typo fixes * Gallery images will now contain a srcset attribute = 2.2.1 = -* Patch fixing missing javascript error +* Patch fixing missing JavaScript error = 2.2.0 = * The mandatory sizes attribute is now included on all images * Updated to Picturefill v2.3.0 * Extensive documentation included in readme * Integrated testing with Travis CLI -* Check if wp.media exists before running JS +* Check if wp.media exists before running JavaScript * Account for rounding variance when matching ascpect ratios = 2.1.1 = * Adding in wp-tevko-responsive-images.js after file not found to be in WordPress repository -* Adjusts the aspect ratio check in tevkori_get_srcset_array() to account for rounding variance +* Adjusts the aspect ratio check in `tevkori_get_srcset_array()` to account for rounding variance = 2.1.0 = -* **This version introduces a breaking change** - there are now two functions. One returns an array of srcset values, and the other returns a string with the `srcset=".."` html needed to generate the responsive image. To retrieve the srcset array, us `tevkori_get_srcset_array( $id, $size )` +* **This version introduces a breaking change**: There are now two functions. One returns an array of srcset values, and the other returns a string with the `srcset=".."` html needed to generate the responsive image. To retrieve the srcset array, us `tevkori_get_srcset_array( $id, $size )` * When the image size is changed in the post editor, the srcset values will adjust to match the change. = 2.0.2 = diff --git a/tests/test-suite.php b/tests/test-suite.php index b756f86..1a8f86b 100644 --- a/tests/test-suite.php +++ b/tests/test-suite.php @@ -1,110 +1,94 @@ remove_added_uploads(); - parent::tearDown(); + protected static $large_id; + + protected static $test_file_name; + + public static function setUpBeforeClass() { + self::$test_file_name = dirname(__FILE__) . '/data/test-large.png'; + self::$large_id = self::create_upload_object( self::$test_file_name ); } - /** - * Helper function that creates an attachment in the DB. - * Copied from Tests_Post_Attachments Class in the WP Core test suite. - */ - private function _make_attachment( $upload, $parent_post_id = 0 ) { + public static function tearDownAfterClass() { + wp_delete_attachment( self::$large_id ); + } + public static function create_upload_object( $filename, $parent = 0 ) { + $contents = file_get_contents($filename); + $upload = wp_upload_bits(basename($filename), null, $contents); $type = ''; - if ( !empty($upload['type']) ) { + + if ( ! empty($upload['type'] ) ) { $type = $upload['type']; } else { $mime = wp_check_filetype( $upload['file'] ); - if ($mime) + if ( $mime ) $type = $mime['type']; } $attachment = array( - 'post_title' => basename( $upload['file'] ), - 'post_content' => '', - 'post_type' => 'attachment', - 'post_parent' => $parent_post_id, + 'post_title' => basename( $upload['file'] ), + 'post_content' => '', + 'post_type' => 'attachment', + 'post_parent' => $parent, 'post_mime_type' => $type, - 'guid' => $upload[ 'url' ], + 'guid' => $upload[ 'url' ], ); - // Save the data - $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $parent_post_id ); + // Save the data. + $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $parent ); wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); - return $this->ids[] = $id; - - } - - /** - * Helper function to create an attachment from a file - * - * @uses _make_attachment - * - * @param string Optional. A path to a file. Default: DIR_TESTDATA.'/images/canola.JPG'. - * @return int|bool An attachment ID or false. - */ - private function _test_img( $file = null ) { - - $filename = $file ? $file : ( dirname(__FILE__) . '/data/test-large.png' ); - $contents = file_get_contents($filename); - - $upload = wp_upload_bits(basename($filename), null, $contents); - $this->assertTrue( empty($upload['error']) ); - - $id = $this->_make_attachment($upload); - return $id; } /* OUR TESTS */ + /** + * @expectedDeprecated tevkori_get_sizes + */ function test_tevkori_get_sizes() { - // make an image - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; global $content_width; - // test sizes against the default WP sizes - $intermediates = array('thumbnail', 'medium', 'large'); + // Test sizes against the default WP sizes. + $intermediates = array( 'thumbnail', 'medium', 'large' ); foreach( $intermediates as $int ) { $width = get_option( $int . '_size_w' ); - // the sizes width gets constrained to $content_width by default - if ( $content_width > 0 ) { - $width = ( $width > $content_width ) ? $content_width : $width; - } - $expected = '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px'; $sizes = tevkori_get_sizes( $id, $int ); - $this->assertSame($expected, $sizes); + $this->assertSame( $expected, $sizes ); } } + /** + * @expectedDeprecated tevkori_get_sizes + */ function test_tevkori_get_sizes_with_args() { - // make an image - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; $args = array( 'sizes' => array( array( - 'size_value' => '10em', - 'mq_value' => '60em', - 'mq_name' => 'min-width' + 'size_value' => '10em', + 'mq_value' => '60em', + 'mq_name' => 'min-width' ), array( - 'size_value' => '20em', - 'mq_value' => '30em', - 'mq_name' => 'min-width' + 'size_value' => '20em', + 'mq_value' => '30em', + 'mq_name' => 'min-width' ), array( - 'size_value' => 'calc(100vm - 30px)' + 'size_value' => 'calc(100vm - 30px)' ), ) ); @@ -112,19 +96,22 @@ function test_tevkori_get_sizes_with_args() { $expected = '(min-width: 60em) 10em, (min-width: 30em) 20em, calc(100vm - 30px)'; $sizes = tevkori_get_sizes( $id, 'medium', $args ); - $this->assertSame($expected, $sizes); + $this->assertSame( $expected, $sizes ); } + /** + * @expectedDeprecated tevkori_get_sizes + */ function test_filter_tevkori_get_sizes_string() { // Add our test filter. add_filter( 'tevkori_image_sizes_args', array( $this, '_test_tevkori_image_sizes_args' ) ); // Set up our test. - $id = $this->_test_img(); + $id = self::$large_id; $sizes = tevkori_get_sizes($id, 'medium'); // Evaluate that the sizes returned is what we expected. - $this->assertSame( $sizes, '100vm'); + $this->assertSame( $sizes, '100vm' ); remove_filter( 'tevkori_image_sizes_args', array( $this, '_test_tevkori_image_sizes_args' ) ); } @@ -137,12 +124,15 @@ function _test_tevkori_image_sizes_args( $args ) { return $args; } + /** + * @expectedDeprecated tevkori_get_srcset_array + */ function test_filter_tevkori_srcset_array() { - // Add test filter + // Add test filter. add_filter( 'tevkori_srcset_array', array( $this, '_test_tevkori_srcset_array' ) ); // Set up our test. - $id = $this->_test_img(); + $id = self::$large_id; $sizes = tevkori_get_srcset_array($id, 'medium'); // Evaluate that the sizes returned is what we expected. @@ -150,91 +140,111 @@ function test_filter_tevkori_srcset_array() { $this->assertTrue( $width <= 500 ); } - // Remove test filter + // Remove test filter. remove_filter( 'tevkori_srcset_array', array( $this, '_test_tevkori_srcset_array' ) ); } /** * A test filter for tevkori_get_srcset_array() that removes any sources - * that are larger that 500px wide. + * that are larger than 500px wide. */ function _test_tevkori_srcset_array( $array ) { foreach ( $array as $size => $file ) { if ( $size > 500 ) { - unset( $array[$size] ); + unset( $array[ $size ] ); } } return $array; } + /** + * @expectedDeprecated tevkori_get_sizes + * @expectedDeprecated tevkori_get_sizes_string + */ function test_tevkori_get_sizes_string() { - // make an image - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; - $sizes = tevkori_get_sizes($id, 'medium'); + $sizes = tevkori_get_sizes( $id, 'medium' ); $sizes_string = tevkori_get_sizes_string( $id, 'medium' ); $expected = 'sizes="' . $sizes . '"'; - $this->assertSame( $expected, $sizes_string); + $this->assertSame( $expected, $sizes_string ); } + /** + * @expectedDeprecated tevkori_get_srcset_array + */ function test_tevkori_get_srcset_array() { // make an image - $id = $this->_test_img(); + $id = self::$large_id; $sizes = tevkori_get_srcset_array( $id, 'medium' ); $year_month = date('Y/m'); $image = wp_get_attachment_metadata( $id ); - $expected = array( - $image['sizes']['medium']['width'] => 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' - . $image['sizes']['medium']['file'] . ' ' . $image['sizes']['medium']['width'] . 'w', - $image['sizes']['large']['width'] => 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' - . $image['sizes']['large']['file'] . ' ' . $image['sizes']['large']['width'] . 'w', - $image['width'] => 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w' - ); + foreach( $image['sizes'] as $name => $size ) { + // Whitelist the sizes that should be included so we pick up 'medium_large' in 4.4. + if ( in_array( $name, array( 'medium', 'medium_large', 'large' ) ) ) { + $expected[$size['width']] = 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' . $size['file'] . ' ' . $size['width'] . 'w'; + } + } + + // Add the full size width at the end. + $expected[$image['width']] = 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; $this->assertSame( $expected, $sizes ); } + /** + * @expectedDeprecated tevkori_get_srcset_array + */ function test_tevkori_get_srcset_array_random_size_name() { - // make an image - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; $sizes = tevkori_get_srcset_array( $id, 'foo' ); $year_month = date('Y/m'); $image = wp_get_attachment_metadata( $id ); - $expected = array( - $image['sizes']['medium']['width'] => 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' - . $image['sizes']['medium']['file'] . ' ' . $image['sizes']['medium']['width'] . 'w', - $image['sizes']['large']['width'] => 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' - . $image['sizes']['large']['file'] . ' ' . $image['sizes']['large']['width'] . 'w', - $image['width'] => 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w' - ); + foreach( $image['sizes'] as $name => $size ) { + // Whitelist the sizes that should be included so we pick up 'medium_large' in 4.4. + if ( in_array( $name, array( 'medium', 'medium_large', 'large' ) ) ) { + $expected[$size['width']] = 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' . $size['file'] . ' ' . $size['width'] . 'w'; + } + } + + // Add the full size width at the end. + $expected[$image['width']] = 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; $this->assertSame( $expected, $sizes ); } + /** + * @expectedDeprecated tevkori_get_srcset_array + */ function test_tevkori_get_srcset_array_no_date_upoads() { - // Save the current setting for uploads folders + // Save the current setting for uploads folders. $uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' ); - // Disable date organized uploads + // Disable date organized uploads. update_option( 'uploads_use_yearmonth_folders', 0 ); - // make an image - $id = $this->_test_img(); + // Make an image. + $id = self::create_upload_object( self::$test_file_name ); $sizes = tevkori_get_srcset_array( $id, 'medium' ); - $image = wp_get_attachment_metadata( $id ); - $expected = array( - $image['sizes']['medium']['width'] => 'http://example.org/wp-content/uploads/' . $image['sizes']['medium']['file'] . ' ' . $image['sizes']['medium']['width'] . 'w', - $image['sizes']['large']['width'] => 'http://example.org/wp-content/uploads/' . $image['sizes']['large']['file'] . ' ' . $image['sizes']['large']['width'] . 'w', - $image['width'] => 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w' - ); + foreach( $image['sizes'] as $name => $size ) { + // Whitelist the sizes that should be included so we pick up 'medium_large' in 4.4. + if ( in_array( $name, array( 'medium', 'medium_large', 'large' ) ) ) { + $expected[$size['width']] = 'http://example.org/wp-content/uploads/' . $size['file'] . ' ' . $size['width'] . 'w'; + } + } + + // Add the full size width at the end. + $expected[$image['width']] = 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; $this->assertSame( $expected, $sizes ); @@ -242,11 +252,17 @@ function test_tevkori_get_srcset_array_no_date_upoads() { update_option( 'uploads_use_yearmonth_folders', $uploads_use_yearmonth_folders ); } + /** + * @expectedDeprecated tevkori_get_srcset + * @expectedDeprecated tevkori_get_srcset_array + */ function test_tevkori_get_srcset_single_srcset() { - // make an image - $id = $this->_test_img(); - // In our tests, thumbnails would only return a single srcset candidate, - // in which case we don't bother returning a srcset array. + // Make an image. + $id = self::$large_id; + /* + * In our tests, thumbnails would only return a single srcset candidate, + * in which case we don't bother returning a srcset array. + */ $sizes = tevkori_get_srcset( $id, 'thumbnail' ); $this->assertTrue( 1 === count( tevkori_get_srcset_array( $id, 'thumbnail' ) ) ); @@ -256,13 +272,16 @@ function test_tevkori_get_srcset_single_srcset() { /** * Test for filtering out leftover sizes after an image is edited. * @group 155 + * @expectedDeprecated tevkori_get_srcset_array */ function test_tevkori_get_srcset_array_with_edits() { // Make an image. - $id = $this->_test_img(); + $id = self::$large_id; - // For this test we're going to mock metadata changes from an edit. - // Start by getting the attachment metadata. + /* + * For this test we're going to mock metadata changes from an edit. + * Start by getting the attachment metadata. + */ $meta = wp_get_attachment_metadata( $id ); // Mimick hash generation method used in wp_save_image(). @@ -288,53 +307,67 @@ function test_tevkori_get_srcset_array_with_edits() { } } + /** + * @expectedDeprecated tevkori_get_srcset_array + */ function test_tevkori_get_srcset_array_false() { - // make an image - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; $sizes = tevkori_get_srcset_array( 99999, 'foo' ); - // For canola.jpg we should return + // For canola.jpg we should return. $this->assertFalse( $sizes ); } + /** + * @expectedDeprecated tevkori_get_srcset_array + */ function test_tevkori_get_srcset_array_no_width() { // Filter image_downsize() output. - add_filter( 'wp_generate_attachment_metadata', array( $this, '_test_tevkori_get_srcset_array_no_width_filter' ) ); + add_filter( 'image_downsize', array( $this, '_filter_image_downsize' ), 10, 3 ); - // Make our attachement. - $id = $this->_test_img(); + // Make our attachment. + $id = self::create_upload_object( self::$test_file_name ); $srcset = tevkori_get_srcset_array( $id, 'medium' ); // The srcset should be false $this->assertFalse( $srcset ); - // Remove filter. - remove_filter( 'wp_generate_attachment_metadata', array( $this, '_test_tevkori_get_srcset_array_no_width_filter' ) ); + remove_filter( 'image_downsize', array( $this, '_filter_image_downsize' ) ); } /** * Helper funtion to filter image_downsize and return zero values for width and height. */ - public function _test_tevkori_get_srcset_array_no_width_filter( $meta ) { - $meta['sizes']['medium']['width'] = 0; - $meta['sizes']['medium']['height'] = 0; - return $meta; + public function _filter_image_downsize( $out, $id, $size ) { + $img_url = wp_get_attachment_url($id); + return array( $img_url, 0, 0 ); } + /** + * @expectedDeprecated tevkori_get_srcset_string + */ function test_tevkori_get_srcset_string() { - // make an image - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; $sizes = tevkori_get_srcset_string( $id, 'full-size' ); + $sizes = tevkori_get_srcset_string( $id, 'full' ); $image = wp_get_attachment_metadata( $id ); $year_month = date('Y/m'); - $expected = 'srcset="'; - $expected .= 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' - . $image['sizes']['medium']['file'] . ' ' . $image['sizes']['medium']['width'] . 'w, '; - $expected .='http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' - . $image['sizes']['large']['file'] . ' ' . $image['sizes']['large']['width'] . 'w, '; - $expected .= 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w"'; + $srcset = ''; + + foreach( $image['sizes'] as $name => $size ) { + // Whitelist the sizes that should be included so we pick up 'medium_large' in 4.4. + if ( in_array( $name, array( 'medium', 'medium_large', 'large' ) ) ) { + $srcset .= 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' . $size['file'] . ' ' . $size['width'] . 'w, '; + } + } + // Add the full size width at the end. + $srcset .= 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; + + $expected = sprintf( 'srcset="%s"', $srcset ); $this->assertSame( $expected, $sizes ); } @@ -343,13 +376,13 @@ function test_tevkori_get_srcset_string() { * @group 159 */ function test_tevkori_filter_attachment_image_attributes() { - // Make image. - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; // Get attachment post data. $attachment = get_post( $id ); $image = wp_get_attachment_image_src( $id, 'medium' ); - list($src, $width, $height) = $image; + list( $src, $width, $height ) = $image; // Create dummy attributes array. $attr = array( @@ -370,13 +403,13 @@ function test_tevkori_filter_attachment_image_attributes() { * @group 159 */ function test_tevkori_filter_attachment_image_attributes_thumbnails() { - // Make image. - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; // Get attachment post data. $attachment = get_post( $id ); $image = wp_get_attachment_image_src( $id, 'thumbnail' ); - list($src, $width, $height) = $image; + list( $src, $width, $height ) = $image; // Create dummy attributes array. $attr = array( @@ -395,10 +428,13 @@ function test_tevkori_filter_attachment_image_attributes_thumbnails() { /** * @group 170 + * @expectedDeprecated tevkori_get_srcset_string + * @expectedDeprecated tevkori_get_sizes_string + * @expectedDeprecated tevkori_filter_content_images */ function test_tevkori_filter_content_images() { - // Make image. - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; $srcset = tevkori_get_srcset_string( $id, 'medium' ); $sizes = tevkori_get_sizes_string( $id, 'medium' ); @@ -408,9 +444,9 @@ function test_tevkori_filter_content_images() { $img_no_size = str_replace( 'size-', '', $img ); $img_no_size_id = str_replace( 'wp-image-', 'id-', $img_no_size ); - // Manually add srcset and sizes to the markup from get_image_tag(); - $respimg = preg_replace('|]+) />|', '', $img); - $respimg_no_size = preg_replace('|]+) />|', '', $img_no_size); + // Manually add srcset and sizes to the markup from get_image_tag(). + $respimg = preg_replace('|]+) />|', '', $img ); + $respimg_no_size = preg_replace('|]+) />|', '', $img_no_size ); $content = '

Welcome to WordPress! This post contains important information. After you read it, you can make it private to hide it from visitors but still have the information handy for future reference.

First things first:

@@ -448,10 +484,11 @@ function test_tevkori_filter_content_images() { /** * @group 170 + * @expectedDeprecated tevkori_filter_content_images */ function test_tevkori_filter_content_images_with_preexisting_srcset() { - // Make image. - $id = $this->_test_img(); + // Make an image. + $id = self::$large_id; // Generate HTML and add a dummy srcset attribute. $image_html = get_image_tag( $id, '', '', '', 'medium' ); diff --git a/wp-tevko-core-functions.php b/wp-tevko-core-functions.php new file mode 100644 index 0000000..687cc72 --- /dev/null +++ b/wp-tevko-core-functions.php @@ -0,0 +1,453 @@ + $image_meta['width'], + 'height' => $image_meta['height'], + 'file' => $image_basename, + ); + + // Uploads are (or have been) in year/month sub-directories. + if ( $image_basename !== $image_meta['file'] ) { + $dirname = dirname( $image_meta['file'] ); + + if ( $dirname !== '.' ) { + $image_baseurl = trailingslashit( $image_baseurl ) . $dirname; + } + } + + $image_baseurl = trailingslashit( $image_baseurl ); + + // Calculate the image aspect ratio. + $image_ratio = $image_height / $image_width; + + /* + * Images that have been edited in WordPress after being uploaded will + * contain a unique hash. Look for that hash and use it later to filter + * out images that are leftovers from previous versions. + */ + $image_edited = preg_match( '/-e[0-9]{13}/', wp_basename( $image_src ), $image_edit_hash ); + + /** + * Filter the maximum image width to be included in a 'srcset' attribute. + * + * @since 4.4.0 + * + * @param int $max_width The maximum image width to be included in the 'srcset'. Default '1600'. + * @param array $size_array Array of width and height values in pixels (in that order). + */ + $max_srcset_image_width = apply_filters( 'max_srcset_image_width', 1600, $size_array ); + + // Array to hold URL candidates. + $sources = array(); + + /* + * Loop through available images. Only use images that are resized + * versions of the same edit. + */ + foreach ( $image_sizes as $image ) { + + // Filter out images that are from previous edits. + if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) { + continue; + } + + // Filter out images that are wider than '$max_srcset_image_width'. + if ( $max_srcset_image_width && $image['width'] > $max_srcset_image_width ) { + continue; + } + + // Calculate the new image ratio. + if ( $image['width'] ) { + $image_ratio_compare = $image['height'] / $image['width']; + } else { + $image_ratio_compare = 0; + } + + // If the new ratio differs by less than 0.01, use it. + if ( abs( $image_ratio - $image_ratio_compare ) < 0.01 ) { + // Add the URL, descriptor, and value to the sources array to be returned. + $sources[ $image['width'] ] = array( + 'url' => $image_baseurl . $image['file'], + 'descriptor' => 'w', + 'value' => $image['width'], + ); + } + } + + /** + * Filter the output of 'wp_calculate_image_srcset()'. + * + * @since 3.0.0 + * + * @param array $sources An array of sources to include in the 'srcset'. Each source + * consists of an array containing the URL and the descriptor + * type and value (default: the image width): + * + * image width => array( + * 'url' => string, + * 'descriptor' => string ('w' or 'x'), + * 'value' => integer (width or pixel density) + * }, + * + * @param int $attachment_id Image attachment ID. + * @param array $size_array Array of width and height values in pixels (in that order). + * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. + + */ + $sources = apply_filters( 'wp_calculate_image_srcset', $sources, $attachment_id, $size_array, $image_meta ); + + // Only return a 'srcset' value if there is more than one source. + if ( count( $sources ) < 2 ) { + return false; + } + + $srcset = ''; + + foreach ( $sources as $source ) { + $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', '; + } + + return rtrim( $srcset, ', ' ); +} + +/** + * Create 'sizes' attribute value for an image. + * + * @since 3.0.0 + * + * @param array|string $size Image size. Accepts any valid image size name ('thumbnail', 'medium', etc.), + * or an array of width and height values in pixels (in that order). + * @param array $image_meta Optional. The image meta data as returned by 'wp_get_attachment_metadata()'. + * @param int $attachment_id Optional. Image attachment ID. Either `$image_meta` or `$attachment_id` is needed + * when using the image size name as argument for `$size`. + * @param string $image_src Optional. The URL to the image file. + * + * @return string|bool A valid source size value for use in a 'sizes' attribute or false. + */ +function wp_get_attachment_image_sizes( $size, $image_meta = null, $attachment_id = 0, $image_src = null ) { + $width = 0; + + if ( is_array( $size ) ) { + $width = absint( $size[0] ); + } elseif ( is_string( $size ) ) { + if ( ! $image_meta && $attachment_id ) { + $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true ); + } + + if ( is_array( $image_meta ) ) { + $size_array = _wp_get_image_size_from_meta( $size, $image_meta ); + if ( $size_array ) { + $width = absint( $size_array[0] ); + } + } + } + + if ( ! $width ) { + return false; + } + + // Setup the default 'sizes' attribute. + $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $width ); + + /** + * Filter the output of 'wp_get_attachment_image_sizes()'. + * + * @since 3.0.0 + * + * @param string $sizes A source size value for use in a 'sizes' attribute. + * @param array|string $size Image size. Image size name, or an array of width and height + * values in pixels (in that order). + * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. + * @param int $attachment_id Image attachment ID of the original image. + * @param string $image_src Optional. The URL to the image file. + */ + return apply_filters( 'wp_get_attachment_image_sizes', $sizes, $size, $image_meta, $attachment_id, $image_src ); +} + +/** + * Filters 'img' elements in post content to add 'srcset' and 'sizes' attributes. + * + * @since 3.0.0 + * + * @see 'wp_image_add_srcset_and_sizes()' + * + * @param string $content The raw post content to be filtered. + * @return string Converted content with 'srcset' and 'sizes' attributes added to images. + */ +function wp_make_content_images_responsive( $content ) { + $images = tevkori_get_media_embedded_in_content( $content, 'img' ); + + $selected_images = $attachment_ids = array(); + + foreach( $images as $image ) { + if ( false === strpos( $image, ' srcset="' ) && preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) && + ( $attachment_id = absint( $class_id[1] ) ) ) { + + /* + * If exactly the same image tag is used more than once, overwrite it. + * All identical tags will be replaced later with 'str_replace()'. + */ + $selected_images[ $image ] = $attachment_id; + // Overwrite the ID when the same image is included more than once. + $attachment_ids[ $attachment_id ] = true; + } + } + + if ( count( $attachment_ids ) > 1 ) { + /* + * Warm object cache for use with 'get_post_meta()'. + * + * To avoid making a database call for each image, a single query + * warms the object cache with the meta information for all images. + */ + update_meta_cache( 'post', array_keys( $attachment_ids ) ); + } + + foreach ( $selected_images as $image => $attachment_id ) { + $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true ); + $content = str_replace( $image, wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ), $content ); + } + + return $content; +} +add_filter( 'the_content', 'wp_make_content_images_responsive', 5, 1 ); + +/** + * Check the content blob for an audio, video, object, embed, or iframe tags. + * This is a copy of `get_media_embedded_in_content()` in WP 4.4 in order to provide + * back compatibility to older versions of WordPress. + * + * @since 3.0.0 + * + * @param string $content A string which might contain media data. + * @param array $types An array of media types: 'audio', 'video', 'object', 'embed', or 'iframe'. + * @return array A list of found HTML media embeds. + */ +function tevkori_get_media_embedded_in_content( $content, $types = null ) { + $html = array(); + + /** + * Filter the embedded media types that are allowed to be returned from the content blob. + * + * @param array $allowed_media_types An array of allowed media types. Default media types are + * 'audio', 'video', 'object', 'embed', 'iframe', and 'img'. + */ + $allowed_media_types = apply_filters( 'media_embedded_in_content_allowed_types', array( 'audio', 'video', 'object', 'embed', 'iframe', 'img' ) ); + + if ( ! empty( $types ) ) { + if ( ! is_array( $types ) ) { + $types = array( $types ); + } + + $allowed_media_types = array_intersect( $allowed_media_types, $types ); + } + + $tags = implode( '|', $allowed_media_types ); + + if ( preg_match_all( '#<(?P' . $tags . ')[^<]*?(?:>[\s\S]*?<\/(?P=tag)>|\s*\/>)#', $content, $matches ) ) { + foreach ( $matches[0] as $match ) { + $html[] = $match; + } + } + + return $html; +} + +/** + * Adds 'srcset' and 'sizes' attributes to an existing 'img' element. + * + * @since 3.0.0 + * + * @see 'wp_get_attachment_image_srcset()' + * @see 'wp_get_attachment_image_sizes()' + * + * @param string $image An HTML 'img' element to be filtered. + * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. + * @param int $attachment_id Image attachment ID. + * @return string Converted 'img' element with 'srcset' and 'sizes' attributes added. + */ +function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) { + // Ensure the image meta exists. + if ( empty( $image_meta['sizes'] ) ) { + return $image; + } + + $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : ''; + list( $src ) = explode( '?', $src ); + + // Return early if we couldn't get the image source. + if ( ! $src ) { + return $image; + } + + // Bail early if an image has been inserted and later edited. + if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash ) && + strpos( wp_basename( $src ), $img_edit_hash[0] ) === false ) { + + return $image; + } + + $width = preg_match( '/ width="([0-9]+)"/', $image, $match_width ) ? (int) $match_width[1] : 0; + $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0; + + if ( ! $width || ! $height ) { + /* + * If attempts to parse the size value failed, attempt to use the image meta data to match + * the image file name from 'src' against the available sizes for an attachment. + */ + $image_filename = wp_basename( $src ); + + if ( $image_filename === wp_basename( $image_meta['file'] ) ) { + $width = (int) $image_meta['width']; + $height = (int) $image_meta['height']; + } else { + foreach( $image_meta['sizes'] as $image_size_data ) { + if ( $image_filename === $image_size_data['file'] ) { + $width = (int) $image_size_data['width']; + $height = (int) $image_size_data['height']; + break; + } + } + } + } + + if ( ! $width || ! $height ) { + return $image; + } + + $size_array = array( $width, $height ); + $srcset = wp_calculate_image_srcset( $src, $size_array, $image_meta, $attachment_id ); + + if ( $srcset ) { + $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta, $attachment_id, $src ); + } + + if ( $srcset && $sizes ) { + // Format the 'srcset' and 'sizes' string and escape attributes. + $srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes ) ); + + // Add 'srcset' and 'sizes' attributes to the image markup. + $image = preg_replace( '/]+?)[\/ ]*>/', '', $image ); + } + + return $image; +} + +// Add the filter to add 'srcset' and 'sizes' attributes to post thumbnails and gallery images. +add_filter( 'wp_get_attachment_image_attributes', 'tevkori_filter_attachment_image_attributes', 0, 3 ); diff --git a/wp-tevko-deprecated-functions.php b/wp-tevko-deprecated-functions.php new file mode 100644 index 0000000..c1de576 --- /dev/null +++ b/wp-tevko-deprecated-functions.php @@ -0,0 +1,283 @@ + array( + array( + 'size_value' => '100vw', + 'mq_value' => $img_width, + 'mq_name' => 'max-width' + ), + array( + 'size_value' => $img_width + ), + ) + ); + + $args = wp_parse_args( $args, $defaults ); + + /** + * Filter arguments used to create the 'sizes' attribute value. + * + * @since 2.4.0 + * @deprecated 3.0.0 Use 'wp_get_attachment_image_sizes' + * @see 'wp_get_attachment_image_sizes' + * + * @param array $args An array of arguments used to create a 'sizes' attribute. + * @param int $id Post ID of the original image. + * @param array|string $size Image size. Image size or an array of width and height + * values in pixels (in that order). + */ + $args = apply_filters( 'tevkori_image_sizes_args', $args, $id, $size ); + + // If sizes is passed as a string, just use the string. + if ( is_string( $args['sizes'] ) ) { + $size_list = $args['sizes']; + + // Otherwise, breakdown the array and build a sizes string. + } elseif ( is_array( $args['sizes'] ) ) { + + $size_list = ''; + + foreach ( $args['sizes'] as $size ) { + + // Use 100vw as the size value unless something else is specified. + $size_value = ( $size['size_value'] ) ? $size['size_value'] : '100vw'; + + // If a media length is specified, build the media query. + if ( ! empty( $size['mq_value'] ) ) { + + $media_length = $size['mq_value']; + + // Use max-width as the media condition unless min-width is specified. + $media_condition = ( ! empty( $size['mq_name'] ) ) ? $size['mq_name'] : 'max-width'; + + // If a media length was set, create the media query. + $media_query = '(' . $media_condition . ": " . $media_length . ') '; + + } else { + + // If no media length was set, '$media_query' is blank. + $media_query = ''; + } + + // Add to the source size list string. + $size_list .= $media_query . $size_value . ', '; + } + + // Remove the trailing comma and space from the end of the string. + $size_list = substr( $size_list, 0, -2 ); + } + + // If '$size_list' is defined set the string, otherwise set false. + return ( $size_list ) ? $size_list : false; + } else { + return wp_get_attachment_image_sizes( $size, $image_meta = null, $id ); + } +} + +/** + * Returns a 'sizes' attribute. + * + * @since 2.2.0 + * @deprecated 3.0.0 Use 'wp_get_attachment_image_sizes()' + * @see 'wp_get_attachment_image_sizes()' + * + * @param int $id Image attachment ID. + * @param array|string $size Image size. Accepts any valid image size, or an array of width and height + * values in pixels (in that order). Default 'medium'. + * @param array $args { + * Optional. Arguments to retrieve posts. + * + * @type array|string $sizes An array or string containing of size information. + * @type int $width A single width value used in the default 'sizes' string. + * } + * @return string|bool A valid source size list as a 'sizes' attribute or false. + */ +function tevkori_get_sizes_string( $id, $size = 'medium', $args = null ) { + _deprecated_function( __FUNCTION__, '3.0.0', 'wp_get_attachment_image_sizes()' ); + + if ( $args || has_filter( 'tevkori_image_sizes_args' ) ) { + $sizes = tevkori_get_sizes( $id, $size, $args ); + } else { + $sizes = wp_get_attachment_image_sizes( $size, $image_meta = null, $id ); + } + + return $sizes ? 'sizes="' . esc_attr( $sizes ) . '"' : false; +} + +/** + * Filter to add 'srcset' and 'sizes' attributes to images in the post content. + * + * @since 2.5.0 + * @deprecated 3.0.0 Use 'wp_make_content_images_responsive()' + * @see 'wp_make_content_images_responsive()' + * + * @param string $content The raw post content to be filtered. + * @return string Converted content with 'srcset' and 'sizes' added to images. + */ +function tevkori_filter_content_images( $content ) { + _deprecated_function( __FUNCTION__, '3.0.0', 'wp_make_content_images_responsive()' ); + return wp_make_content_images_responsive( $content ); +} + +/** + * Filter to add 'srcset' and 'sizes' attributes to post thumbnails and gallery images. + * + * @since 2.3.0 + * @see 'wp_get_attachment_image_attributes' + * + * @return array Attributes for image. + */ +function tevkori_filter_attachment_image_attributes( $attr, $attachment, $size ) { + // Set 'srcset' and 'sizes' if not already present and both were returned. + if ( empty( $attr['srcset'] ) ) { + $srcset = wp_get_attachment_image_srcset( $attachment->ID, $size ); + $sizes = wp_get_attachment_image_sizes( $size, $image_meta = null, $attachment->ID ); + + if ( $srcset && $sizes ) { + $attr['srcset'] = $srcset; + + if ( empty( $attr['sizes'] ) ) { + $attr['sizes'] = $sizes; + } + } + } + + return $attr; +} diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index e0bf345..993c56e 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -6,9 +6,9 @@ * * @wordpress-plugin * Plugin Name: RICG Responsive Images - * Plugin URI: http://www.smashingmagazine.com/2015/02/24/ricg-responsive-images-for-wordpress/ - * Description: Bringing automatic default responsive images to wordpress - * Version: 2.5.2 + * Plugin URI: https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images + * Description: Bringing automatic default responsive images to WordPress + * Version: 3.0.0 * Author: The RICG * Author URI: http://responsiveimages.org/ * License: GPL-2.0+ @@ -18,7 +18,10 @@ // Don't load the plugin directly. defined( 'ABSPATH' ) or die( "No script kiddies please!" ); -// List includes. +/* + * Include the advanced image compression files. + * See readme.md for more information. + */ if ( class_exists( 'Imagick' ) ) { require_once( plugin_dir_path( __FILE__ ) . 'class-respimg.php' ); require_once( plugin_dir_path( __FILE__ ) . 'class-wp-image-editor-respimg.php' ); @@ -40,431 +43,36 @@ function tevkori_wp_image_editors( $editors ) { add_filter( 'wp_image_editors', 'tevkori_wp_image_editors' ); } -/** - * Enqueue bundled version of the Picturefill library. - */ -function tevkori_get_picturefill() { - wp_enqueue_script( 'picturefill', plugins_url( 'js/picturefill.min.js', __FILE__ ), array(), '2.3.1', true ); -} -add_action( 'wp_enqueue_scripts', 'tevkori_get_picturefill' ); - -/** - * Return a source size attribute for an image from an array of values. - * - * @since 2.2.0 - * - * @param int $id Image attachment ID. - * @param string $size Optional. Name of image size. Default value: 'medium'. - * @param array $args { - * Optional. Arguments to retrieve posts. - * - * @type array|string $sizes An array or string containing of size information. - * @type int $width A single width value used in the default `sizes` string. - * } - * @return string|bool A valid source size value for use in a 'sizes' attribute or false. - */ -function tevkori_get_sizes( $id, $size = 'medium', $args = null ) { - - // Try to get the image width from `$args` before calling `image_downsize()`. - if ( is_array( $args ) && ! empty( $args['width'] ) ) { - $img_width = (int) $args['width']; - } elseif ( $img = image_downsize( $id, $size ) ) { - $img_width = $img[1]; - } - - // Bail early if ``$image_width` isn't set. - if ( ! $img_width ) { - return false; - } - - // Set the image width in pixels. - $img_width = $img_width . 'px'; - - // Set up our default values. - $defaults = array( - 'sizes' => array( - array( - 'size_value' => '100vw', - 'mq_value' => $img_width, - 'mq_name' => 'max-width' - ), - array( - 'size_value' => $img_width - ), - ) - ); - - $args = wp_parse_args( $args, $defaults ); - - /** - * Filter arguments used to create 'sizes' attribute. - * - * @since 2.4.0 - * - * @param array $args An array of arguments used to create a 'sizes' attribute. - * @param int $id Post ID of the original image. - * @param string $size Name of the image size being used. - */ - $args = apply_filters( 'tevkori_image_sizes_args', $args, $id, $size ); - - // If sizes is passed as a string, just use the string. - if ( is_string( $args['sizes'] ) ) { - $size_list = $args['sizes']; - - // Otherwise, breakdown the array and build a sizes string. - } elseif ( is_array( $args['sizes'] ) ) { - - $size_list = ''; - - foreach ( $args['sizes'] as $size ) { - - // Use 100vw as the size value unless something else is specified. - $size_value = ( $size['size_value'] ) ? $size['size_value'] : '100vw'; - - // If a media length is specified, build the media query. - if ( ! empty( $size['mq_value'] ) ) { - - $media_length = $size['mq_value']; - - // Use max-width as the media condition unless min-width is specified. - $media_condition = ( ! empty( $size['mq_name'] ) ) ? $size['mq_name'] : 'max-width'; - - // If a media_length was set, create the media query. - $media_query = '(' . $media_condition . ": " . $media_length . ') '; - - } else { - - // If not meda length was set, $media_query is blank. - $media_query = ''; - } - - // Add to the source size list string. - $size_list .= $media_query . $size_value . ', '; - } - - // Remove the trailing comma and space from the end of the string. - $size_list = substr( $size_list, 0, -2 ); - } - - // If $size_list is defined set the string, otherwise set false. - return ( $size_list ) ? $size_list : false; -} - -/** - * Return a source size list for an image from an array of values. - * - * @since 2.2.0 - * - * @param int $id Image attachment ID. - * @param string $size Optional. Name of image size. Default value: 'medium'. - * @param array $args { - * Optional. Arguments to retrieve posts. - * - * @type array|string $sizes An array or string containing of size information. - * @type int $width A single width value used in the default `sizes` string. - * } - * @return string|bool A valid source size list as a 'sizes' attribute or false. - */ -function tevkori_get_sizes_string( $id, $size = 'medium', $args = null ) { - $sizes = tevkori_get_sizes( $id, $size, $args ); - - return $sizes ? 'sizes="' . $sizes . '"' : false; -} - -/** - * Get an array of image sources candidates for use in a 'srcset' attribute. - * - * @param int $id Image attachment ID. - * @param string $size Optional. Name of image size. Default value: 'medium'. - * @return array|bool An array of of srcset values or false. - */ -function tevkori_get_srcset_array( $id, $size = 'medium' ) { - $arr = array(); - - // Get the intermediate size. - $image = image_get_intermediate_size( $id, $size ); - // Get the post meta. - $img_meta = wp_get_attachment_metadata( $id ); - - // Extract the height and width from the intermediate or the full size. - $img_width = ( $image ) ? $image['width'] : $img_meta['width']; - $img_height = ( $image ) ? $image['height'] : $img_meta['height']; - - // Bail early if the width isn't greater that zero. - if ( ! $img_width > 0 ) { - return false; - } - - // Use the url from the intermediate size or build the url from the metadata. - if ( ! empty( $image['url'] ) ) { - $img_url = $image['url']; - } else { - $uploads_dir = wp_upload_dir(); - $img_file = ( $image ) ? path_join( dirname( $img_meta['file'] ) , $image['file'] ) : $img_meta['file']; - $img_url = $uploads_dir['baseurl'] . '/' . $img_file; - } - - $img_sizes = $img_meta['sizes']; - - // Add full size to the img_sizes array. - $img_sizes['full'] = array( - 'width' => $img_meta['width'], - 'height' => $img_meta['height'], - 'file' => wp_basename( $img_meta['file'] ) - ); - - // Calculate the image aspect ratio. - $img_ratio = $img_height / $img_width; - - /* - * Images that have been edited in WordPress after being uploaded will - * contain a unique hash. Look for that hash and use it later to filter - * out images that are leftovers from previous versions. - */ - $img_edited = preg_match( '/-e[0-9]{13}/', $img_url, $img_edit_hash ); - - /* - * Loop through available images and only use images that are resized - * versions of the same rendition. - */ - foreach ( $img_sizes as $img ) { - - // Filter out images that are leftovers from previous renditions. - if ( $img_edited && ! strpos( $img['file'], $img_edit_hash[0] ) ) { - continue; - } - - // Calculate the new image ratio. - $img_ratio_compare = $img['height'] / $img['width']; - - // If the new ratio differs by less than 0.01, use it. - if ( abs( $img_ratio - $img_ratio_compare ) < 0.01 ) { - $arr[ $img['width'] ] = path_join( dirname( $img_url ), $img['file'] ) . ' ' . $img['width'] .'w'; - } - } - - /** - * Filter the output of tevkori_get_srcset_array(). - * - * @since 2.4.0 - * - * @param array $arr An array of image sources. - * @param int $id Attachment ID for image. - * @param array|string $size Size of image, either array or string. - */ - return apply_filters( 'tevkori_srcset_array', $arr, $id, $size ); -} +// Load the deprecated core functions. +require_once( plugin_dir_path( __FILE__ ) . 'wp-tevko-deprecated-functions.php' ); -/** - * Get the value for the 'srcset' attribute. - * - * @since 2.3.0 - * - * @param int $id Image attachment ID. - * @param string $size Optional. Name of image size. Default value: 'medium'. - * @return string|bool A 'srcset' value string or false. +/* + * Load copies of our core functions if the plugin is installed on a version of WordPress + * previous to 4.4, when the functions were added to core. */ -function tevkori_get_srcset( $id, $size = 'medium' ) { - $srcset_array = tevkori_get_srcset_array( $id, $size ); - - if ( count( $srcset_array ) <= 1 ) { - return false; - } - - return implode( ', ', $srcset_array ); +if ( ! function_exists( 'wp_get_attachment_image_srcset' ) ) { + require_once( plugin_dir_path( __FILE__ ) . 'wp-tevko-core-functions.php' ); } -/** - * Create a 'srcset' attribute. - * - * @since 2.1.0 - * - * @param int $id Image attachment ID. - * @param string $size Optional. Name of image size. Default value: 'medium'. - * @return string|bool A full 'srcset' string or false. - */ -function tevkori_get_srcset_string( $id, $size = 'medium' ) { - $srcset_value = tevkori_get_srcset( $id, $size ); - - if ( empty( $srcset_value ) ) { - return false; - } - - return 'srcset="' . $srcset_value . '"'; +// Enqueue bundled version of the Picturefill library. +function tevkori_get_picturefill() { + wp_enqueue_script( 'picturefill', plugins_url( 'js/picturefill.min.js', __FILE__ ), array(), '3.0.1', true ); } +add_action( 'wp_enqueue_scripts', 'tevkori_get_picturefill' ); /** - * Filters images in post content to add 'srcset' and 'sizes'. + * Back compatability shim for 'data-sizes' attributes in content. * - * @since 2.5.0 + * Prior to version 2.5 a 'srcset' and 'data-sizes' attribute were added to the image + * while inserting the image in the content. We replace the 'data-sizes' attribute by + * a 'sizes' attribute. * - * @param string $content The raw post content to be filtered. - * @return string Converted content with 'srcset' and 'sizes' added to images. - */ -function tevkori_filter_content_images( $content ) { - - // Only match images in our uploads directory. - $uploads_dir = wp_upload_dir(); - $path_to_upload_dir = $uploads_dir['baseurl']; - - // Pattern for matching all images with a `src` from the uploads directory. - $pattern = '|]+' . preg_quote( $path_to_upload_dir ) . '[^>]+)>|i'; - preg_match_all( $pattern, $content, $matches ); - - $images = $matches[0]; - $ids = array(); - - foreach( $images as $image ) { - if ( preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) ) { - (int) $id = $class_id[1]; - if ( $id ) { - $ids[] = $id; - } - } - } - - if ( 0 < count( $ids ) ) { - /* - * Warm object caches for use with wp_get_attachment_metadata. - * - * To avoid making a database call for each image, a single query - * warms the object cache with the meta information for all images. - **/ - _prime_post_caches( $ids, false, true ); - } - - foreach( $matches[0] as $k => $image ) { - $match = array( $image, $matches[1][$k] ); - $needle = $image; - $replacement = _tevkori_filter_content_images_callback( $match ); - if ( false === $replacement ) { - continue; - } - $content = str_replace( $image, $replacement, $content ); - } - - return $content; -} -add_filter( 'the_content', 'tevkori_filter_content_images', 5, 1 ); - -/** - * Private preg_replace callback used in tevkori_filter_content_images() + * @since 3.0.0 * - * @access private - * @since 2.5.0 + * @param string $content The content to filter; + * @return string The filtered content with `data-sizes` repaced by `sizes` attributes. */ -function _tevkori_filter_content_images_callback( $image ) { - if ( empty( $image ) ) { - return false; - } - - list( $image_html, $atts ) = $image; - - // Bail early if a 'srcset' attribute already exists. - if ( false !== strpos( $atts, 'srcset=' ) ) { - - /* - * Backward compatibility. - * - * Prior to version 2.5 a 'srcset' and 'data-sizes' attribute - * were added to the image while inserting the image in the content. - * We replace the 'data-sizes' attribute by a 'sizes' attribute. - */ - $image_html = str_replace( ' data-sizes="', ' sizes="', $image_html ); - - return $image_html; - } - - // Grab ID and size info from core classes. - $id = preg_match( '/wp-image-([0-9]+)/i', $atts, $class_id ) ? (int) $class_id[1] : false; - $size = preg_match( '/size-([^\s|"]+)/i', $atts, $class_size ) ? $class_size[1] : false; - $width = preg_match( '/ width="([0-9]+)"/', $atts, $atts_width ) ? (int) $atts_width[1] : false; - $height = preg_match( '/ height="([0-9]+)"/', $atts, $atts_height ) ? (int) $atts_height[1] : false; - - if ( $id && false === $size ) { - $size = array( - $width, - $height - ); - } - - /* - * If attempts to parse the size value failed, attempt to use the image - * metadata to match the 'src' angainst the available sizes for an attachment. - */ - if ( ! $size && ! empty( $id ) && $meta = wp_get_attachment_metadata( $id ) ) { - - preg_match( '/src="([^"]+)"/', $atts, $url ); - - // Sanity check the 'src' value and bail early it doesn't exist. - if ( ! $url[1] ) { - return $image_html; - } - - $image_filename = basename( $url[1] ); - - /* - * First, see if the file is the full size image. If not, we loop through - * the intermediate sizes until we find a match. - */ - if ( $image_filename === basename( $meta['file'] ) ) { - $size = 'full'; - } else { - foreach( $meta['sizes'] as $image_size => $image_size_data ) { - if ( $image_filename === $image_size_data['file'] ) { - $size = $image_size; - break; - } - } - } - - } - - // If we have an ID and size, try for 'srcset' and 'sizes' and update the markup. - if ( $id && $size && $srcset = tevkori_get_srcset_string( $id, $size ) ) { - - // Pass height and width to `tevkori_get_sizes_string()`. - $args = array( - 'width' => $width, - 'height' => $height, - ); - - $sizes = tevkori_get_sizes_string( $id, $size, $args ); - - // Strip trailing slashes and whitespaces from the `$atts` string. - $atts = trim( rtrim( $atts, '/' ) ); - - $image_html = ""; - }; - - return $image_html; -} - -/** - * Filter to add 'srcset' and 'sizes' attributes to post thumbnails and gallery images. - * - * @see wp_get_attachment_image_attributes - * @return array Attributes for image. - */ -function tevkori_filter_attachment_image_attributes( $attr, $attachment, $size ) { - if ( ! isset( $attr['srcset'] ) ) { - $srcset = tevkori_get_srcset( $attachment->ID, $size ); - - // Set the 'srcset' attribute if one was returned. - if ( $srcset ) { - $attr['srcset'] = $srcset; - - if ( ! isset( $attr['sizes'] ) ) { - $sizes = tevkori_get_sizes( $attachment->ID, $size ); - - // Set the 'sizes' attribute if sizes were returned. - if ( $sizes ) { - $attr['sizes'] = $sizes; - } - } - } - } - - return $attr; +function tevkori_replace_data_sizes( $content ) { + return str_replace( ' data-sizes="', ' sizes="', $content ); } -add_filter( 'wp_get_attachment_image_attributes', 'tevkori_filter_attachment_image_attributes', 0, 3 ); +add_filter( 'the_content', 'tevkori_replace_data_sizes' );