h%20d%3D%22M28.494%205.858a3.994%203.994%200%20115.648%205.648%203.994%203.994%200%2001-5.648-5.648z%22%20fill%3D%22%23323232%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background-repeat: no-repeat;
background-position: 50% 50%;
}';
const IFRAME_PLACEHOLDER_STYLE = '';
const IFRAME_TEMP_COMMENT = '/** optmliframelazyloadplaceholder */';
const SVG_PLACEHOLDER = 'data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%20#width#%20#height#%22%20width%3D%22#width#%22%20height%3D%22#height#%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22#width#%22%20height%3D%22#height#%22%20fill%3D%22#fill#%22%2F%3E%3C%2Fsvg%3E';
/**
* If frame lazyload is present on page.
*
* @var bool Whether or not at least one iframe has been lazyloaded.
*/
private static $found_iframe = false;
/**
* Cached object instance.
*
* @var Optml_Lazyload_Replacer
*/
protected static $instance = null;
/**
* Holds the number of images to skip from lazyload.
*
* @var integer The number image tags to skip.
*/
private static $skip_lazyload_images = null;
/**
* Holds classes for listening to lazyload on background.
*
* @var array Lazyload background classes.
*/
private static $lazyload_background_classes = null;
/**
* Selectors used for background lazyload.
*
* @var array Lazyload background CSS selectors.
*/
private static $background_lazyload_selectors = null;
/**
* Holds flags which remove noscript tag bundle causing issues on render, i.e slider plugins.
*
* @var array Noscript flags.
*/
private static $ignore_no_script_flags = null;
/**
* Holds possible iframe lazyload flags where we should ignore our lazyload.
*
* @var array
*/
protected static $iframe_lazyload_flags = null;
/**
* Holds classes responsabile for watching lazyload behaviour.
*
* @var array Lazyload classes.
*/
private static $lazyload_watcher_classes = null;
/**
* Should we use the generic placeholder?
*
* @var bool Lazyload placeholder flag.
*/
private static $is_lazyload_placeholder = false;
/**
* Class instance method.
*
* @codeCoverageIgnore
* @static
* @return Optml_Lazyload_Replacer
* @since 1.0.0
* @access public
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
add_action( 'optml_replacer_setup', [ self::$instance, 'init' ] );
}
return self::$instance;
}
/**
* Return lazyload selectors for background images.
*
* @return array Lazyload selectors.
*/
public static function get_background_lazyload_selectors() {
if ( null !== self::$background_lazyload_selectors ) {
return self::$background_lazyload_selectors;
}
if ( self::instance()->settings->get( 'bg_replacer' ) === 'disabled' ) {
self::$background_lazyload_selectors = [];
return self::$background_lazyload_selectors;
}
$default_watchers = [
'.elementor-section[data-settings*="background_background"]',
'.elementor-section > .elementor-background-overlay',
'[class*="wp-block-cover"][style*="background-image"]',
'[class*="wp-block-group"][style*="background-image"]',
];
$saved_watchers = self::instance()->settings->get_watchers();
$saved_watchers = str_replace( [ "\n", "\r" ], ',', $saved_watchers );
$saved_watchers = explode( ',', $saved_watchers );
$all_watchers = array_merge( $default_watchers, $saved_watchers );
$all_watchers = apply_filters( 'optml_lazyload_bg_selectors', $all_watchers );
$all_watchers = array_filter(
$all_watchers,
function ( $value ) {
return ! empty( $value ) && strlen( $value ) >= 2;
}
);
self::$background_lazyload_selectors = $all_watchers;
return self::$background_lazyload_selectors;
}
/**
* Returns background classes for lazyload.
*
* @return array
*/
public static function get_lazyload_bg_classes() {
if ( null !== self::$lazyload_background_classes ) {
return self::$lazyload_background_classes;
}
self::$lazyload_background_classes = apply_filters( 'optml_lazyload_bg_classes', [] );
return self::$lazyload_background_classes;
}
/**
* Returns the number of images to skip from lazyload.
*
* @return integer
*/
public static function get_skip_lazyload_limit() {
if ( self::$skip_lazyload_images !== null ) {
return self::$skip_lazyload_images;
}
self::$skip_lazyload_images = apply_filters( 'optml_lazyload_images_skip', self::instance()->settings->get( 'skip_lazyload_images' ) );
return self::$skip_lazyload_images;
}
/**
* Returns classes for lazyload additional watch.
*
* @return array
*/
public static function get_watcher_lz_classes() {
if ( null !== self::$lazyload_watcher_classes ) {
return self::$lazyload_watcher_classes;
}
self::$lazyload_watcher_classes = apply_filters( 'optml_watcher_lz_classes', [] );
return self::$lazyload_watcher_classes;
}
/**
* The initialize method.
*/
public function init() {
parent::init();
if ( ! $this->settings->use_lazyload() ) {
return;
}
$filters = $this->settings->get_filters();
if ( ! Optml_Filters::should_do_page( $filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_URL ], $filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_URL_MATCH ] ) ) {
return;
}
self::$is_lazyload_placeholder = self::$instance->settings->get( 'lazyload_placeholder' ) === 'enabled';
add_filter( 'optml_tag_replace', [ $this, 'lazyload_tag_replace' ], 2, 6 );
add_filter( 'optml_video_replace', [ $this, 'lazyload_video_replace' ], 2, 1 );
}
/**
* Check if there are lazyloaded iframes.
*
* @return bool Whether an iframe was lazyloaded on the page or not.
*/
public static function found_iframe() {
return self::$found_iframe;
}
/**
* Check if the img tag should be lazyloaded early.
*
* @param string $image_tag The image tag.
* @return bool Whether the image tag should be lazyloaded early or not.
*/
public function should_lazyload_early( $image_tag ) {
$flags = apply_filters( 'optml_lazyload_early_flags', [] );
foreach ( $flags as $flag ) {
if ( strpos( $image_tag, $flag ) !== false ) {
return true;
}
}
return false;
}
/**
* Replaces the tags with lazyload tags.
*
* @param string $new_tag The new tag.
* @param string $original_url The original URL.
* @param string $new_url The optimized URL.
* @param array $optml_args Options passed for URL optimization.
* @param bool $is_slashed If the url needs slashes.
* @param string $full_tag Full tag, wrapper included.
*
* @return string
*/
public function lazyload_tag_replace( $new_tag, $original_url, $new_url, $optml_args, $is_slashed = false, $full_tag = '' ) {
if ( ! $this->can_lazyload_for( $original_url, $full_tag ) ) {
return Optml_Tag_Replacer::instance()->regular_tag_replace( $new_tag, $original_url, $new_url, $optml_args, $is_slashed );
}
if ( self::instance()->settings->get( 'native_lazyload' ) === 'enabled' ) {
if ( strpos( $new_tag, 'loading=' ) === false ) {
$new_tag = preg_replace( '/is_valid_mimetype_from_url( $original_url, [ 'gif' => true, 'svg' => true ] );
if ( ! self::$is_lazyload_placeholder && ! $should_ignore_rescale ) {
$optml_args['quality'] = 'eco';
$optml_args['resize'] = [];
$low_url = apply_filters( 'optml_content_url', $original_url, $optml_args );
$low_url = $is_slashed ? addcslashes( $low_url, '/' ) : $low_url;
} else {
$low_url = $this->get_svg_for(
isset( $optml_args['width'] ) ? $optml_args['width'] : '100%',
isset( $optml_args['height'] ) ? $optml_args['height'] : '100%',
( $should_ignore_rescale ? null : $original_url )
);
}
$opt_format = '';
if ( $this->should_add_data_tag( $full_tag ) ) {
$opt_format = ' data-opt-src="%s" ';
$custom_class = '';
if ( $should_ignore_rescale ) {
$custom_class .= 'optimole-lazy-only ';
}
if ( $this->should_lazyload_early( $full_tag ) ) {
$custom_class .= 'optimole-load-early';
}
if ( ! empty( $custom_class ) ) {
if ( strpos( $new_tag, 'class=' ) === false ) {
$opt_format .= ' class="' . $custom_class . '" ';
} else {
$new_tag = str_replace(
( $is_slashed ? 'class=\"' : 'class="' ),
( $is_slashed ? 'class=\"' . $custom_class . ' ' : 'class="' . $custom_class . ' ' ),
$new_tag
);
}
}
$opt_format = $is_slashed ? addslashes( $opt_format ) : $opt_format;
}
$new_url = $is_slashed ? addcslashes( $new_url, '/' ) : $new_url;
$opt_src = sprintf( $opt_format, $new_url );
$no_script_tag = str_replace(
$original_url,
$new_url,
$new_tag
);
$new_tag = preg_replace(
[
'/((?:\s|\'|"){1,}src(?>=|"|\'|\s|\\\\)*)' . preg_quote( $original_url, '/' ) . '/m',
'/should_add_noscript( $new_tag ) ) {
return $new_tag;
}
return $new_tag . '';
}
/**
* Replaces video embeds with lazyload embeds.
*
* @param string $content Html page content.
*
* @return string
*/
public function lazyload_video_replace( $content ) {
$video_tags = [];
$iframes = [];
$videos = [];
preg_match_all( '#(?:)?#is', $content, $iframes );
preg_match_all( '#(?:)?#is', $content, $videos );
$video_tags = array_merge( $iframes[0], $videos[0] );
$search = [];
$replace = [];
foreach ( $video_tags as $video_tag ) {
if ( ! $this->should_lazyload_iframe( $video_tag ) ) {
continue;
}
if ( preg_match( "/ data-opt-src=['\"]/is", $video_tag ) ) {
continue;
}
$should_add_noscript = true;
$original_tag = $video_tag;
// replace the src and add the data-opt-src attribute
if ( strpos( $video_tag, 'iframe' ) !== false ) {
$video_tag = preg_replace( '/iframe(.*?)src=/is', 'iframe$1 src="about:blank" data-opt-src=', $video_tag );
} elseif ( strpos( $video_tag, 'video' ) !== false ) {
if ( strpos( $video_tag, 'source' ) !== false ) {
if ( strpos( $video_tag, 'preload' ) === false ) {
$video_tag = preg_replace( '/video(.*?)>/is', 'video$1 preload="metadata">', $video_tag, 1 );
$should_add_noscript = false;
} else {
continue;
}
} else {
$video_tag = preg_replace( '/video(.*?)src=/is', 'video$1 data-opt-src=', $video_tag );
}
}
if ( $this->should_add_noscript( $video_tag ) && $should_add_noscript ) {
$video_tag .= '';
}
array_push( $search, $original_tag );
array_push( $replace, $video_tag );
self::$found_iframe = true;
}
$search = array_unique( $search );
$replace = array_unique( $replace );
$content = str_replace( $search, $replace, $content );
return $content;
}
/**
* Check if the lazyload is allowed for this url.
*
* @param string $url Url.
* @param string $tag Html tag.
*
* @return bool We can lazyload?
*/
public function can_lazyload_for( $url, $tag = '' ) {
foreach ( self::possible_lazyload_flags() as $banned_string ) {
if ( strpos( $tag, $banned_string ) !== false ) {
return false;
}
}
if ( false === Optml_Filters::should_do_image( $url, self::$filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_FILENAME ] ) ) {
return false;
}
$url = strtok( $url, '?' );
$type = wp_check_filetype(
basename( $url ),
Optml_Config::$image_extensions
);
if ( empty( $type['ext'] ) ) {
return false;
}
if ( false === Optml_Filters::should_do_extension( self::$filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_EXT ], $type['ext'] ) ) {
return false;
}
if ( defined( 'OPTML_DISABLE_PNG_LAZYLOAD' ) && OPTML_DISABLE_PNG_LAZYLOAD ) {
return $type['ext'] !== 'png';
}
if ( Optml_Tag_Replacer::$lazyload_skipped_images < self::get_skip_lazyload_limit() ) {
return false;
}
return true;
}
/**
* Check if we should add the data-opt-tag.
*
* @param string $tag Html tag.
*
* @return bool Should add?
*/
public function should_add_data_tag( $tag ) {
foreach ( self::possible_data_ignore_flags() as $banned_string ) {
if ( strpos( $tag, $banned_string ) !== false ) {
return false;
}
}
return true;
}
/**
* Get SVG markup with specific width/height.
*
* @param int|string $width Markup Width.
* @param int|string $height Markup Height.
* @param string|null $url Original URL.
*
* @return string SVG code.
*/
public function get_svg_for( $width, $height, $url = null ) {
if ( $url !== null && ! is_numeric( $width ) ) {
$url = strtok( $url, '?' );
$key = crc32( $url );
$sizes = wp_cache_get( $key, 'optml_sources' );
if ( $sizes === false ) {
$filepath = substr( $url, strpos( $url, $this->upload_resource['content_folder'] ) + $this->upload_resource['content_folder_length'] );
$filepath = WP_CONTENT_DIR . $filepath;
if ( is_file( $filepath ) ) {
$sizes = getimagesize( $filepath );
wp_cache_add( $key, [ $sizes[0], $sizes[1] ], 'optml_sources', DAY_IN_SECONDS );
}
}
list( $width, $height ) = $sizes;
}
$width = ! is_numeric( $width ) ? '100%' : $width;
$height = ! is_numeric( $height ) ? '100%' : $height;
$fill = $this->settings->get( 'placeholder_color' );
if ( empty( $fill ) ) {
$fill = 'transparent';
}
return str_replace(
[ '#width#', '#height#', '#fill#' ],
[
$width,
$height,
urlencode( $fill ),
],
self::SVG_PLACEHOLDER
);
}
/**
* Check if we should add the noscript tag.
*
* @param string $tag Html tag.
*
* @return bool Should add?
*/
public function should_add_noscript( $tag ) {
if ( $this->settings->get( 'no_script' ) === 'disabled' ) {
return false;
}
foreach ( self::get_ignore_noscript_flags() as $banned_string ) {
if ( strpos( $tag, $banned_string ) !== false ) {
return false;
}
}
return true;
}
/**
* Check if we should lazyload iframe.
*
* @param string $tag Html tag.
*
* @return bool Should add?
*/
public function should_lazyload_iframe( $tag ) {
foreach ( self::get_iframe_lazyload_flags() as $banned_string ) {
if ( strpos( $tag, $banned_string ) !== false ) {
return false;
}
}
return true;
}
/**
* Returns flags for ignoring noscript tag additional watch.
*
* @return array
*/
public static function get_ignore_noscript_flags() {
if ( null !== self::$ignore_no_script_flags ) {
return self::$ignore_no_script_flags;
}
self::$ignore_no_script_flags = apply_filters( 'optml_ignore_noscript_on', [] );
return self::$ignore_no_script_flags;
}
/**
* Returns possible lazyload flags for iframes.
*
* @return array
*/
public static function get_iframe_lazyload_flags() {
if ( null !== self::$iframe_lazyload_flags ) {
return self::$iframe_lazyload_flags;
}
self::$iframe_lazyload_flags = self::possible_lazyload_flags();
self::$iframe_lazyload_flags = array_merge( self::$iframe_lazyload_flags, apply_filters( 'optml_iframe_lazyload_flags', [ 'gform_ajax_frame', '