diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..330a936 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,62 @@ +sudo: false + +language: php + +notifications: + email: + on_success: never + on_failure: change + +branches: + only: + - master + +cache: + directories: + - vendor + - $HOME/.composer/cache + +matrix: + include: + - php: 7.1 + env: WP_VERSION=latest + - php: 7.0 + env: WP_VERSION=latest + - php: 5.6 + env: WP_VERSION=3.2 + - php: 5.6 + env: WP_VERSION=latest + - php: 5.6 + env: WP_VERSION=trunk + - php: 5.6 + env: WP_TRAVISCI=phpcs + - php: 5.3 + env: WP_VERSION=latest + +before_script: + - export PATH="$HOME/.composer/vendor/bin:$PATH" + - | + if [[ ! -z "$WP_VERSION" ]] ; then + bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION + if [[ ${TRAVIS_PHP_VERSION:0:2} == "5." ]]; then + composer global require "phpunit/phpunit=4.8.*" + else + composer global require "phpunit/phpunit=5.7.*" + fi + fi + - | + if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then + composer global require wp-coding-standards/wpcs + phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs + fi + +script: + - | + if [[ ! -z "$WP_VERSION" ]] ; then + phpunit + WP_MULTISITE=1 phpunit + fi + - | + if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then + phpcs --standard=phpcs.ruleset.xml $(find . -name '*.php') + fi diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh new file mode 100755 index 0000000..73bb4c7 --- /dev/null +++ b/bin/install-wp-tests.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash + +if [ $# -lt 3 ]; then + echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" + exit 1 +fi + +DB_NAME=$1 +DB_USER=$2 +DB_PASS=$3 +DB_HOST=${4-localhost} +WP_VERSION=${5-latest} +SKIP_DB_CREATE=${6-false} + +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 == '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 + 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 + + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php +} + +install_test_suite() { + # portable in-place argument for both GNU sed and Mac OSX sed + if [[ $(uname -s) == 'Darwin' ]]; then + local ioption='-i .bak' + else + local ioption='-i' + fi + + # 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 + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data + fi + + 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 + # remove all forward slashes in the end + WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") + 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() { + + if [ ${SKIP_DB_CREATE} = "true" ]; then + return 0 + fi + + # parse DB_HOST for port or socket references + local PARTS=(${DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]}; + local DB_SOCK_OR_PORT=${PARTS[1]}; + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ] ; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ] ; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ] ; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA +} + +install_wp +install_test_suite +install_db diff --git a/js/lazy-load.js b/js/lazy-load.js index b7457ca..66885fb 100644 --- a/js/lazy-load.js +++ b/js/lazy-load.js @@ -11,13 +11,15 @@ $( '[data-carousel-extra]' ).each( function() { $( this ).find( 'img[data-lazy-src]' ).each( function() { lazy_load_image( this ); - } ); + } ); } ); } function lazy_load_image( img ) { var $img = jQuery( img ), - src = $img.attr( 'data-lazy-src' ); + src = $img.attr( 'data-lazy-src' ), + srcset = $img.attr( 'data-lazy-srcset' ), + sizes = $img.attr( 'data-lazy-sizes' ); if ( ! src || 'undefined' === typeof( src ) ) return; @@ -25,9 +27,17 @@ $img.unbind( 'scrollin' ) // remove event binding .hide() .removeAttr( 'data-lazy-src' ) + .removeAttr( 'data-lazy-srcset' ) + .removeAttr( 'data-lazy-sizes' ) .attr( 'data-lazy-loaded', 'true' ); img.src = src; + if ( srcset ) { + img.srcset = srcset; + } + if ( sizes ) { + img.sizes = sizes; + } $img.fadeIn(); } })(jQuery); diff --git a/lazy-load.php b/lazy-load.php index b80398b..16baf5e 100644 --- a/lazy-load.php +++ b/lazy-load.php @@ -6,7 +6,7 @@ * Text Domain: lazy-load * * Code by the WordPress.com VIP team, TechCrunch 2011 Redesign team, and Jake Goldman (10up LLC). - * Uses jQuery.sonar by Dave Artz (AOL): http://www.artzstudio.com/files/jquery-boston-2010/jquery.sonar/ + * Uses jQuery.sonar by Dave Artz (AOL): http://www.artzstudio.com/files/jquery-boston-2010/jquery.sonar/ * * License: GPL2 */ @@ -65,27 +65,51 @@ static function process_image( $matches ) { $placeholder_image = apply_filters( 'lazyload_images_placeholder_image', self::get_url( 'images/1x1.trans.gif' ) ); $old_attributes_str = $matches[2]; - $old_attributes = wp_kses_hair( $old_attributes_str, wp_allowed_protocols() ); + $old_attributes_kses_hair = wp_kses_hair( $old_attributes_str, wp_allowed_protocols() ); - if ( empty( $old_attributes['src'] ) ) { + if ( empty( $old_attributes_kses_hair['src'] ) ) { return $matches[0]; } - $image_src = $old_attributes['src']['value']; - - // Remove src and lazy-src since we manually add them + $old_attributes = self::flatten_kses_hair_data( $old_attributes_kses_hair ); $new_attributes = $old_attributes; - unset( $new_attributes['src'], $new_attributes['data-lazy-src'] ); + + // Set placeholder and lazy-src + $new_attributes['src'] = self::get_placeholder_image(); + $new_attributes['data-lazy-src'] = $old_attributes['src']; + + // Handle `srcset` + if ( ! empty( $new_attributes['srcset'] ) ) { + $new_attributes['data-lazy-srcset'] = $old_attributes['srcset']; + unset( $new_attributes['srcset'] ); + } + + // Handle `sizes` + if ( ! empty( $new_attributes['sizes'] ) ) { + $new_attributes['data-lazy-sizes'] = $old_attributes['sizes']; + unset( $new_attributes['sizes'] ); + } $new_attributes_str = self::build_attributes_string( $new_attributes ); - return sprintf( '', esc_url( $placeholder_image ), esc_url( $image_src ), $new_attributes_str, $matches[0] ); + return sprintf( '', $new_attributes_str, $matches[0] ); + } + + private static function get_placeholder_image() { + return apply_filters( 'lazyload_images_placeholder_image', self::get_url( 'images/1x1.trans.gif' ) ); + } + + private static function flatten_kses_hair_data( $attributes ) { + $flattened_attributes = array(); + foreach ( $attributes as $name => $attribute ) { + $flattened_attributes[ $name ] = $attribute['value']; + } + return $flattened_attributes; } private static function build_attributes_string( $attributes ) { $string = array(); - foreach ( $attributes as $name => $attribute ) { - $value = $attribute['value']; + foreach ( $attributes as $name => $value ) { if ( '' === $value ) { $string[] = sprintf( '%s', $name ); } else { diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..44f0fdb --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + ./tests/ + + + diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..e312884 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,25 @@ + array( + array( + '', + 'img', + ' id="img"', + ), + '', + ), + + 'img_simple' => array( + array( + '', + 'img', + ' src="image.jpg"', + ), + '', + ), + + 'img_with_other_attributes' => array( + array( + 'Alt!', + 'img', + ' src="image.jpg" alt="Alt!"', + ), + 'Alt!', + ), + + 'img_with_srcset' => array( + array( + '', + 'img', + ' src="image.jpg" srcset="medium.jpg 1000w, large.jpg 2000w"', + + ), + '', + ), + + 'img_with_sizes' => array( + array( + '', + 'img', + ' src="image.jpg" sizes="(min-width: 36em) 33.3vw, 100vw"', + + ), + '', + ), + ); + } + + /** + * @dataProvider get_test_data + */ + function test_process_image( $image_parts, $expected_html ) { + $actual_html = LazyLoad_Images::process_image( $image_parts ); + + $this->assertEquals( $expected_html, $actual_html ); + } +}