parser = $parser;
parent::__construct();
}
/**
* Initialize module actions.
*
* @since 3.2.0
*/
public function init() {
// Only run on front end and if lazy loading is enabled.
if ( is_admin() || ! $this->settings->get( 'lazy_load' ) ) {
return;
}
$this->options = $this->settings->get_setting( WP_SMUSH_PREFIX . 'lazy_load' );
// Enabled without settings? Don't think so... Exit.
if ( ! $this->options ) {
return;
}
// Skip AMP pages.
if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
return;
}
// Load js file that is required in public facing pages.
add_action( 'wp_head', array( $this, 'add_inline_styles' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
// Allow lazy load attributes in img tag.
add_filter( 'wp_kses_allowed_html', array( $this, 'add_lazy_load_attributes' ) );
$this->parser->enable( 'lazy_load' );
add_filter( 'wp_smush_should_skip_parse', array( $this, 'maybe_skip_parse' ), 10 );
// Filter images.
if ( ! isset( $this->options['output']['content'] ) || ! $this->options['output']['content'] ) {
add_filter( 'the_content', array( $this, 'exclude_from_lazy_loading' ), 100 );
}
if ( ! isset( $this->options['output']['thumbnails'] ) || ! $this->options['output']['thumbnails'] ) {
add_filter( 'post_thumbnail_html', array( $this, 'exclude_from_lazy_loading' ), 100 );
}
if ( ! isset( $this->options['output']['gravatars'] ) || ! $this->options['output']['gravatars'] ) {
add_filter( 'get_avatar', array( $this, 'exclude_from_lazy_loading' ), 100 );
}
if ( ! isset( $this->options['output']['widgets'] ) || ! $this->options['output']['widgets'] ) {
add_action( 'dynamic_sidebar_before', array( $this, 'filter_sidebar_content_start' ), 0 );
add_action( 'dynamic_sidebar_after', array( $this, 'filter_sidebar_content_end' ), 1000 );
}
}
/**
* Add inline styles at the top of the page for pre-loaders and effects.
*
* @since 3.2.0
*/
public function add_inline_styles() {
if ( ! $this->options['animation']['selected'] ) {
return;
}
// Spinner.
if ( 'spinner' === $this->options['animation']['selected'] ) {
$loader = WP_SMUSH_URL . 'app/assets/images/smush-lazyloader-' . $this->options['animation']['spinner']['selected'] . '.gif';
if ( isset( $this->options['animation']['spinner']['selected'] ) && 5 < (int) $this->options['animation']['spinner']['selected'] ) {
$loader = wp_get_attachment_image_src( $this->options['animation']['spinner']['selected'], 'full' );
$loader = $loader[0];
}
$background = 'rgba(255, 255, 255, 0)';
} else {
// Placeholder.
$loader = WP_SMUSH_URL . 'app/assets/images/smush-placeholder.png';
$background = '#FAFAFA';
if ( isset( $this->options['animation']['placeholder']['selected'] ) && 2 === (int) $this->options['animation']['placeholder']['selected'] ) {
$background = '#333333';
}
if ( isset( $this->options['animation']['placeholder']['selected'] ) && 2 < (int) $this->options['animation']['placeholder']['selected'] ) {
$loader = wp_get_attachment_image_src( $this->options['animation']['placeholder']['selected'], 'full' );
$loader = $loader[0];
if ( isset( $this->options['animation']['placeholder']['color'] ) ) {
$background = $this->options['animation']['placeholder']['color'];
}
}
}
// Fade in.
$fadein = isset( $this->options['animation']['fadein']['duration'] ) ? $this->options['animation']['fadein']['duration'] : 0;
$delay = isset( $this->options['animation']['fadein']['delay'] ) ? $this->options['animation']['fadein']['delay'] : 0;
?>
options['footer'] ) ? $this->options['footer'] : true;
wp_enqueue_script(
'smush-lazy-load',
WP_SMUSH_URL . 'app/assets/js/smush-lazy-load.min.js',
array(),
WP_SMUSH_VERSION,
$in_footer
);
$custom = "window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.lazyClass = 'lazyload';
window.lazySizesConfig.loadingClass = 'lazyloading';
window.lazySizesConfig.loadedClass = 'lazyloaded';
lazySizesConfig.loadMode = 1;"; // Page is optimized for fast onload event.
wp_add_inline_script( 'smush-lazy-load', $custom, 'before' );
wp_add_inline_script( 'smush-lazy-load', 'lazySizes.init();' );
}
/**
* Make sure WordPress does not filter out img elements with lazy load attributes.
*
* @since 3.2.0
*
* @param array $allowedposttags Allowed post tags.
*
* @return mixed
*/
public function add_lazy_load_attributes( $allowedposttags ) {
if ( ! isset( $allowedposttags['img'] ) ) {
return $allowedposttags;
}
$smush_attributes = array(
'data-src' => true,
'data-srcset' => true,
);
$img_attributes = array_merge( $allowedposttags['img'], $smush_attributes );
$allowedposttags['img'] = $img_attributes;
return $allowedposttags;
}
/**
* Check if we need to skip parsing of this page.
*
* @since 3.2.2
* @param bool $skip Skip parsing.
*
* @return bool
*/
public function maybe_skip_parse( $skip ) {
// Don't lazy load for feeds, previews.
if ( is_feed() || is_preview() ) {
$skip = true;
}
if ( ! $this->is_allowed_post_type() || $this->is_exluded_uri() ) {
$skip = true;
}
return $skip;
}
/**
* Parse image for Lazy load.
*
* @since 3.2.2
*
* @param string $src Image URL.
* @param string $image Image tag (
).
*
* @return string
*/
public function parse_image( $src, $image ) {
/**
* Filter to skip a single image from lazy load.
*
* @since 3.3.0 Added $image param.
*
* @param bool $skip Should skip? Default: false.
* @param string $src Image url.
* @param string $image Image.
*/
if ( apply_filters( 'smush_skip_image_from_lazy_load', false, $src, $image ) ) {
return $image;
}
// Avoid conflicts if attributes are set (another plugin, for example).
if ( false !== strpos( $image, 'data-src' ) ) {
return $image;
}
/**
* Check if some image formats are excluded.
*/
if ( in_array( false, $this->options['format'], true ) ) {
$ext = strtolower( pathinfo( $src, PATHINFO_EXTENSION ) );
$ext = 'jpg' === $ext ? 'jpeg' : $ext;
if ( isset( $this->options['format'][ $ext ] ) && ! $this->options['format'][ $ext ] ) {
return $image;
}
}
if ( $this->has_excluded_class_or_id( $image ) ) {
return $image;
}
$new_image = $image;
$src = Helpers\Parser::get_attribute( $new_image, 'src' );
Helpers\Parser::remove_attribute( $new_image, 'src' );
Helpers\Parser::add_attribute( $new_image, 'data-src', $src );
// Change srcset to data-srcset attribute.
$new_image = preg_replace( '/
/i', '
', $new_image );
// Add .lazyload class.
$class = Helpers\Parser::get_attribute( $new_image, 'class' );
if ( $class ) {
$class .= ' lazyload';
} else {
$class = 'lazyload';
}
Helpers\Parser::remove_attribute( $new_image, 'class' );
Helpers\Parser::add_attribute( $new_image, 'class', apply_filters( 'wp_smush_lazy_load_classes', $class ) );
Helpers\Parser::add_attribute( $new_image, 'src', 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' );
// Use noscript element in HTML to load elements normally when JavaScript is disabled in browser.
$new_image .= '';
return $new_image;
}
/**
* Get images from content and add exclusion class.
*
* @since 3.2.2
*
* @param string $content Page/block content.
*
* @return string
*/
public function exclude_from_lazy_loading( $content ) {
$images = Helpers\Parser::get_images_from_content( $content );
if ( empty( $images ) ) {
return $content;
}
foreach ( $images[0] as $key => $image ) {
$new_image = $image;
// Add .no-lazyload class.
$class = Helpers\Parser::get_attribute( $new_image, 'class' );
if ( $class ) {
Helpers\Parser::remove_attribute( $new_image, 'class' );
$class .= ' no-lazyload';
} else {
$class = 'no-lazyload';
}
Helpers\Parser::add_attribute( $new_image, 'class', $class );
$content = str_replace( $image, $new_image, $content );
}
return $content;
}
/**
* Check if this is part of the allowed post type.
*
* @since 3.2.0
*
* @return bool
*/
private function is_allowed_post_type() {
// If not settings are set, probably, all are disabled.
if ( ! is_array( $this->options['include'] ) ) {
return false;
}
// Static home page is selected (is_home() is false, is_front_page() is true).
if ( is_front_page() ) {
return isset( $this->options['include']['frontpage'] ) && $this->options['include']['frontpage'];
}
// Latest posts selected as homepage (both is_home() and is_front_page() will return true).
if ( is_home() ) {
return isset( $this->options['include']['home'] ) && $this->options['include']['home'];
}
if ( is_page() && isset( $this->options['include']['page'] ) && $this->options['include']['page'] ) {
return true;
} elseif ( is_single() && isset( $this->options['include']['single'] ) && $this->options['include']['single'] ) {
return true;
} elseif ( is_category() && isset( $this->options['include']['category'] ) && ! $this->options['include']['category'] ) {
return false; // Show false, because a category is also an archive.
} elseif ( is_tag() && isset( $this->options['include']['tag'] ) && ! $this->options['include']['tag'] ) {
return false;
} elseif ( is_archive() && isset( $this->options['include']['archive'] ) && $this->options['include']['archive'] ) {
return true;
}
return false;
}
/**
* Check if the page has been added to Post, Pages & URLs filter in lazy loading settings.
*
* @since 3.2.0
*
* @return bool
*/
private function is_exluded_uri() {
// No exclusion rules defined.
if ( ! isset( $this->options['exclude-pages'] ) || empty( $this->options['exclude-pages'] ) ) {
return false;
}
$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
// Remove empty values.
$uri_pattern = array_filter( $this->options['exclude-pages'] );
$uri_pattern = implode( '|', $uri_pattern );
if ( preg_match( "#{$uri_pattern}#i", $request_uri ) ) {
return true;
}
return false;
}
/**
* Check if the image has a defined class or ID.
*
* @since 3.2.0
*
* @param string $image Image.
*
* @return bool
*/
private function has_excluded_class_or_id( $image ) {
$image_classes = Helpers\Parser::get_attribute( $image, 'class' );
$image_classes = explode( ' ', $image_classes );
$image_id = '#' . Helpers\Parser::get_attribute( $image, 'id' );
if ( in_array( $image_id, $this->options['exclude-classes'], true ) ) {
return true;
}
foreach ( $image_classes as $class ) {
// Skip Revolution Slider images.
if ( 'rev-slidebg' === $class ) {
return true;
}
// Internal class to skip images.
if ( 'no-lazyload' === $class ) {
return true;
}
if ( in_array( ".{$class}", $this->options['exclude-classes'], true ) ) {
return true;
}
}
return false;
}
/**
* Buffer sidebar content.
*
* @since 3.2.0
*/
public function filter_sidebar_content_start() {
ob_start();
}
/**
* Process buffered content.
*
* @since 3.2.0
*/
public function filter_sidebar_content_end() {
$content = ob_get_clean();
echo $this->exclude_from_lazy_loading( $content );
unset( $content );
}
}