setup(); } return $instance; } /** * Setup routine */ public function setup() { $this->settings = \PoweredCache\Utils\get_settings(); if ( $this->settings['enable_lazy_load'] ) { add_action( 'wp', [ $this, 'init' ], 9999 ); // run this as late as possible add_action( 'powered_cache_lazy_load_compat', [ $this, 'compat' ] ); add_action( 'powered_cache_lazy_load_run_filter', [ $this, 'maybe_disable_through_meta' ] ); } /** * Filter to disable native lazyload support. * * @hook powered_cache_disable_native_lazyload * * @param {boolean} $status true to disable native lazy load. * * @return {boolean} New value. * @since 2.0 */ if ( apply_filters( 'powered_cache_disable_native_lazyload', $this->settings['disable_wp_lazy_load'] ) ) { add_filter( 'wp_lazy_loading_enabled', '__return_false' ); } } /** * Initialize the setup */ public function init() { /* We do not touch the feeds */ if ( is_admin() || is_feed() || is_preview() ) { return; } /** * Fires before filtering lazy-load hooks. * * @hook powered_cache_lazy_load_compat * * @since 1.0 */ do_action( 'powered_cache_lazy_load_compat' ); /** * Filters lazy-load status. * * @hook powered_cache_lazy_load_enabled * * @param {boolean} $enable true to enable * * @return {boolean} New value * @since 1.0 */ $enabled = apply_filters( 'powered_cache_lazy_load_enabled', true ); if ( $enabled ) { add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); $this->setup_filtering(); } } /** * Enqueue scripts */ public function enqueue_scripts() { wp_enqueue_script( 'PCLL', POWERED_CACHE_URL . 'dist/js/lazyload.js', null, POWERED_CACHE_VERSION, true ); /** * Filters screen threshold value. * * @hook powered_cache_lazy_load_threshold * * @param {int} $threshold Screen display threshold. * * @return {int} New value * @since 1.0 */ $threshold = apply_filters( 'powered_cache_lazy_load_threshold', 200 ); if ( 200 !== (int) $threshold ) { wp_localize_script( 'PCLL', 'PCLL_options', array( 'threshold' => $threshold ) ); } } /** * Set up filtering for certain content */ protected function setup_filtering() { /** * Filters whether apply or not apply lazyload for images. * * @hook powered_cache_lazy_load_images * * @param {boolean} true to lazyload for images * * @return {boolean} New value. * @since 1.0 */ if ( true === apply_filters( 'powered_cache_lazy_load_images', $this->settings['lazy_load_images'] ) ) { add_filter( 'powered_cache_lazy_load_filter', array( __CLASS__, 'filter_images' ) ); } /** * Filters whether apply or not apply lazyload for iframes. * * @hook powered_cache_lazy_load_iframes * * @param {boolean} true to apply lazyload for iframes * * @return {boolean} New value. * @since 1.0 */ if ( true === apply_filters( 'powered_cache_lazy_load_iframes', $this->settings['lazy_load_iframes'] ) ) { add_filter( 'powered_cache_lazy_load_filter', array( __CLASS__, 'filter_iframes' ) ); } /** * Filters whether apply or not apply lazyload for post_content. * * @hook powered_cache_lazy_load_post_content * * @param {boolean} true to apply lazyload for post_content * * @return {boolean} New value. * @since 1.0 */ if ( true === apply_filters( 'powered_cache_lazy_load_post_content', $this->settings['lazy_load_post_content'] ) ) { add_filter( 'the_content', array( __CLASS__, 'filter' ), 200 ); } /** * Filters whether apply or not apply lazyload for widgets. * * @hook powered_cache_lazy_load_widget_text * * @param {boolean} true to apply lazyload for widgets * * @return {boolean} New value. * @since 1.0 */ if ( true === apply_filters( 'powered_cache_lazy_load_widget_text', $this->settings['lazy_load_widgets'] ) ) { add_filter( 'widget_text', array( __CLASS__, 'filter' ), 200 ); } /** * Filters whether apply or not apply lazyload for thumbnails. * * @hook powered_cache_lazy_load_post_thumbnail * * @param {boolean} true to apply lazyload for thumbnails * * @return {boolean} New value. * @since 1.0 */ if ( true === apply_filters( 'powered_cache_lazy_load_post_thumbnail', $this->settings['lazy_load_post_thumbnail'] ) ) { add_filter( 'post_thumbnail_html', array( __CLASS__, 'filter' ), 200 ); } /** * Filters whether apply or not apply lazyload for avatars. * * @hook powered_cache_lazy_load_avatar * * @param {boolean} true to apply lazyload for avatars * * @return {boolean} New value. * @since 1.0 */ if ( true === apply_filters( 'powered_cache_lazy_load_avatar', $this->settings['lazy_load_avatars'] ) ) { add_filter( 'get_avatar', array( __CLASS__, 'filter' ), 200 ); } add_filter( 'powered_cache_lazy_load_html', array( __CLASS__, 'filter' ) ); } /** * Filter HTML content. Replace supported content with placeholders. * * @param string $content The HTML string to filter * * @return string The filtered HTML string */ public static function filter( $content ) { // Last chance to bail out before running the filter /** * Filters lazy-load run filter. * * @hook powered_cache_lazy_load_run_filter * * @param {boolean} $run_filter true to enable * * @return {boolean} New value * @since 1.0 */ $run_filter = apply_filters( 'powered_cache_lazy_load_run_filter', true ); if ( ! $run_filter ) { return $content; } /** * Filters the content * * @hook powered_cache_lazy_load_filter * * @param string $content The HTML string to filter * * @since 1.0 */ $content = apply_filters( 'powered_cache_lazy_load_filter', $content ); return $content; } /** * Replace images with placeholders in the content * * @param string $content The HTML to do the filtering on * * @return string The HTML with the images replaced */ public static function filter_images( $content ) { /** * Filters the content * * @hook powered_cache_lazy_load_filter * * @param string $content The HTML string to filter * * @since 1.0 */ $placeholder_url = apply_filters( 'powered_cache_lazy_load_placeholder_url', 'data:image/gif;base64,R0lGODdhAQABAPAAAP///wAAACwAAAAAAQABAEACAkQBADs=' ); $match_content = self::get_content_haystack( $content ); $matches = array(); preg_match_all( '//is', $match_content, $matches ); $search = array(); $replace = array(); foreach ( $matches[0] as $img_html ) { // don't to the replacement if the image is a data-uri if ( ! preg_match( "/src=['\"]data:image/is", $img_html ) ) { $placeholder_url_used = $placeholder_url; // replace the src and add the data-src attribute $replace_html = preg_replace( '/'; array_push( $search, $img_html ); array_push( $replace, $replace_html ); } } $content = str_replace( $search, $replace, $content ); return $content; } /** * Replace iframes with placeholders in the content * * @param string $content The HTML to do the filtering on * * @return string The HTML with the iframes replaced */ public static function filter_iframes( $content ) { /** * Filters the lazyload placeholder URL. * * @hook powered_cache_lazy_load_placeholder_url * * @param string $image default placeholder url. Base64 string as default. * * @since 1.0 */ $placeholder_url = apply_filters( 'powered_cache_lazy_load_placeholder_url', 'data:image/gif;base64,R0lGODdhAQABAPAAAP///wAAACwAAAAAAQABAEACAkQBADs=' ); $match_content = self::get_content_haystack( $content ); $matches = array(); preg_match_all( '||si', $match_content, $matches ); $search = array(); $replace = array(); foreach ( $matches[0] as $iframe_html ) { // Don't mess with the Gravity Forms ajax iframe if ( strpos( $iframe_html, 'gform_ajax_frame' ) ) { continue; } $replace_html = ''; $replace_html .= ''; array_push( $search, $iframe_html ); array_push( $replace, $replace_html ); } $content = str_replace( $search, $replace, $content ); return $content; } /** * Remove elements we don’t want to filter from the HTML string * We’re reducing the haystack by removing the hay we know we don’t want to look for needles in * * @param string $content The HTML string * * @return string The HTML string without the unwanted elements */ protected static function get_content_haystack( $content ) { $content = self::remove_noscript( $content ); $content = self::remove_skip_classes_elements( $content ); return $content; } /** * Remove