HEX
Server: nginx/1.28.3
System: Linux VM-0-7-opencloudos 6.6.117-45.oc9.x86_64 #1 SMP Thu Dec 4 10:26:39 CST 2025 x86_64
User: www (1000)
PHP: 8.2.28
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: /www/wwwroot/www.213n.cn/wp-content/plugins/wpturbo/module/lazyload.php
<?php

/**
 * Class WPTurbo_Lazyload
 * @author quaniz
 * Author URI: https://www.wbolt.com
 */

class WPTurbo_Lazyload extends WPTurbo_Base
{

    public function __construct()
    {
        add_action('wp', [$this, 'lazy_load_init']);
    }


    public static function cnf($key = null, $default = null)
    {
        return WPTurbo_Optimize::cnf($key, $default);
    }

    public function lazy_load_init()
    {

        if (is_admin() || is_embed() || is_feed() || is_customize_preview()) {
            return;
        }
        if (wp_doing_ajax() || wp_doing_cron()) {
            return;
        }
        if ((defined('REST_REQUEST') && REST_REQUEST) || (function_exists('wp_is_json_request') && wp_is_json_request())) {
            return;
        }

        $cnf = self::cnf();
        //lazy load image or iframes
        $is_lazy = $cnf['lazy_load_image'] || $cnf['lazy_frame_video'];
        if (!$is_lazy) {
            return;
        }
        if ($cnf['lazy_fix_image']) {
            add_filter('wpturbo_theme_output', [$this, 'image_dimensions'], 100);
        }
        add_action('wp_enqueue_scripts', [$this, 'enqueue_lazy_load']);
        //add_filter('template_redirect', [$this, 'lazy_load'], 2);
        add_filter('wpturbo_theme_output', [$this, 'lazy_load_buffer'], 110);
        add_action('wp_footer', [$this, 'print_lazy_load_js'], 10000);
        add_action('wp_head', [$this, 'print_lazy_load_css'], 10000);
        add_filter('wp_lazy_loading_enabled', '__return_false');
    }

    public function enqueue_lazy_load()
    {
        wp_register_script('wpturbo-lazy-load-js', plugins_url('assets/js/lazyload.min.js', WPTURBO_BASE), array(), WPTURBO_VERSION, true);
        wp_enqueue_script('wpturbo-lazy-load-js');
    }

    public function lazy_load()
    {
        //ob_start([$this,'lazy_load_buffer']);
    }

    public function lazy_load_buffer($html)
    {

        $cnf = self::cnf();

        //get clean html to use for buffer search
        $buffer = $this->lazy_load_clean_html($html);

        if ($cnf['lazy_load_image']) {
            $html = $this->lazy_load_images($html, $buffer);
            $html = $this->lazy_load_pictures($html, $buffer);
            $html = $this->lazy_load_background_images($html, $buffer);
        }

        if ($cnf['lazy_frame_video']) {
            $html = $this->lazy_load_iframes($html, $buffer);
            $html = $this->lazy_load_videos($html, $buffer);
        }
        return $html;
    }

    //remove unecessary bits from html for buffer searh
    public function lazy_load_clean_html($html)
    {
        //remove existing script tags
        $html = preg_replace('#<script\b(?:[^>]*)>(?:.+)?</script>#Umsi', '', $html);
        //remove existing noscript tags
        $html = preg_replace('#<noscript>(?:.+)</noscript>#Umsi', '', $html);
        return $html;
    }

    //lazy load img tags
    public function lazy_load_images($html, $buffer)
    {

        //match all img tags
        preg_match_all('#<img([^>]+?)/?>#is', $buffer, $images, PREG_SET_ORDER);

        if (empty($images)) {
            return $html;
        }

        $cnf = self::cnf();

        //return $images[3][0];


        $lazy_image_count = 0;
        $lazy_skip_image_num = $cnf['lazy_skip_image_num'] ?? 0;

        //remove any duplicate images
        $images = array_unique($images, SORT_REGULAR);

        //loop through images
        foreach ($images as $image) {

            $lazy_image_count++;

            if ($lazy_image_count <= $lazy_skip_image_num) {
                continue;
            }

            //prepare lazy load image
            $lazy_image = $this->lazy_load_image($image);

            //replace image in html
            $html = str_replace($image[0], $lazy_image, $html);
        }

        return $html;
    }

    //prep img tag for lazy loading
    public function lazy_load_image($image)
    {

        //if there are no attributes, return original match
        if (empty($image[1])) {
            return $image[0];
        }

        //get image attributes array
        $image_atts = $this->lazyload_get_atts_array($image[1]);
        if (empty($image_atts['src'])) {
            return $image[0];
        }
        //强制lazy load
        $force_rule = $this->get_lazyload_forced_atts();
        if (!$this->lazyload_excluded($image[1], $force_rule)) {
            if (!empty($image_atts['class']) && strpos($image_atts['class'], 'do-not-lazyload') !== false) {
                return $image[0];
            }
            $exclude_rule = $this->get_lazyload_exclude_rule();
            if ($this->lazyload_excluded($image[1], $exclude_rule)) {
                return $image[0];
            }
        }

        //add lazyload class
        $image_atts['class'] = (isset($image_atts['class']) ? $image_atts['class'] . ' ' : '') . 'wpturbo-lazy';

        //migrate src
        $image_atts['data-src'] = $image_atts['src'];

        //add placeholder src
        $width = !empty($image_atts['width']) ? $image_atts['width'] : 0;
        $height = !empty($image_atts['height']) ? $image_atts['height'] : 0;
        $image_atts['src'] = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20" . $width . "%20" . $height . "'%3E%3C/svg%3E";

        //migrate srcset
        if (!empty($image_atts['srcset'])) {
            $image_atts['data-srcset'] = $image_atts['srcset'];
            unset($image_atts['srcset']);
        }

        //migrate sizes
        if (!empty($image_atts['sizes'])) {
            $image_atts['data-sizes'] = $image_atts['sizes'];
            unset($image_atts['sizes']);
        }

        //build lazy image attributes string
        $lazy_image_atts_string = $this->lazyload_get_atts_string($image_atts);

        //replace attributes
        $html = sprintf('<img %1$s />', $lazy_image_atts_string);

        //original noscript image
        $html .= "<noscript>" . $image[0] . "</noscript>";

        return $html;
    }

    public function lazyload_get_atts_array($atts_string)
    {

        if (!empty($atts_string)) {
            $atts_array = array_map(
                function (array $attribute) {
                    return $attribute['value'];
                },
                wp_kses_hair($atts_string, wp_allowed_protocols())
            );

            return $atts_array;
        }

        return false;
    }

    //check for excluded attributes in attributes string
    public function lazyload_excluded($string, $excluded)
    {
        if (!is_array($excluded)) {
            (array) $excluded;
        }

        if (empty($excluded)) {
            return false;
        }

        foreach ($excluded as $exclude) {
            if (strpos($string, $exclude) !== false) {
                return true;
            }
        }

        return false;
    }

    //get forced attributes
    public function get_lazyload_forced_atts()
    {
        static $rules = [];
        if ($rules) {
            return $rules;
        }
        $rules =  apply_filters('wpturo_lazyload_forced_rules', array());
        return $rules;
    }

    //get excluded attributes
    public function get_lazyload_exclude_rule()
    {
        static $rules = [];
        if ($rules) {
            return $rules;
        }
        //base exclusions
        $base_rules = array(
            'data-wpturbo-preload',
            'gform_ajax_frame'
        );

        $cnf = self::cnf();
        //get exclusions added from settings
        if (!empty($cnf['lazy_exclude']) && is_array($cnf['lazy_exclude'])) {
            $rules = array_unique(array_merge($base_rules, $cnf['lazy_exclude']));
        }

        $rules =  apply_filters('wpturo_lazyload_excluded_rules', $rules);
        return $rules;
    }


    public function lazyload_get_atts_string($atts_array)
    {

        if (!empty($atts_array)) {
            $assigned_atts_array = array_map(
                function ($name, $value) {
                    if ($value === '') {
                        return $name;
                    }
                    return sprintf('%s="%s"', $name, esc_attr($value));
                },
                array_keys($atts_array),
                $atts_array
            );
            $atts_string = implode(' ', $assigned_atts_array);

            return $atts_string;
        }

        return false;
    }


    //lazy load picture tags for webp
    public function lazy_load_pictures($html, $buffer)
    {

        //match all picture tags
        if (!preg_match_all('#<picture(.*)?>(.*)<\/picture>#isU', $buffer, $pictures, PREG_SET_ORDER)) {
            return $html;
        }

        $force_rule = $this->get_lazyload_forced_atts();
        $exclude_rule = $this->get_lazyload_exclude_rule();

        foreach ($pictures as $picture) {

            //get picture tag attributes
            $picture_atts = $this->lazyload_get_atts_array($picture[1]);
            if (!$this->lazyload_excluded($picture[1], $force_rule)) {
                //skip if no-lazy class is found
                if (!empty($picture_atts['class']) && strpos($picture_atts['class'], 'do-not-lazyload') !== false) {
                    continue;
                }

                //skip if exluded attribute was found
                if ($this->lazyload_excluded($picture[0], $exclude_rule)) {
                    continue;
                }
            }


            //match all source tags inside the picture
            if (!preg_match_all('#<source(\s.+)>#isU', $picture[2], $sources, PREG_SET_ORDER)) {
                continue;
            }

            //remove any duplicate sources
            $sources = array_unique($sources, SORT_REGULAR);

            foreach ($sources as $source) {

                //skip if exluded attribute was found
                if ($this->lazyload_excluded($source[1], $exclude_rule)) {
                    continue;
                }

                //create placeholder src
                $width = !empty($picture_atts['width']) ? $picture_atts['width'] : 0;
                $height = !empty($picture_atts['height']) ? $picture_atts['height'] : 0;
                $placeholder = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20" . $width . "%20" . $height . "'%3E%3C/svg%3E";

                //migrate srcet
                $new_source = preg_replace('/([\s"\'])srcset/i', '${1}srcset="' . $placeholder . '" data-srcset', $source[0]);

                //migrate sizes
                $new_source = preg_replace('/([\s"\'])sizes/i', '${1}data-sizes', $new_source);

                //replace source in html
                $html = str_replace($source[0], $new_source, $html);
            }


            //match img tag inside the picture
            if (!preg_match('#<img([^>]+?)\/?>#isU', $picture[0], $image)) {
                continue;
            }

            //get lazy load image
            $lazy_image = $this->lazy_load_image($image);

            //replace image in html
            $html = str_replace($image[0], $lazy_image, $html);
        }

        return $html;
    }

    //lazy load background images
    public function lazy_load_background_images($html, $buffer)
    {

        //match all elements with inline styles
        //preg_match_all('#<(?<tag>div|figure|section|span|li)(\s+[^>]+[\'"\s]?style\s*=\s*[\'"].*?background-image.*?[\'"][^>]*)>#is', $buffer, $elements, PREG_SET_ORDER); //alternate to possibly filter some out that don't have background images???
        if (!preg_match_all('#<(?<tag>div|figure|section|span|li)(\s+[^>]*[\'"\s]?style\s*=\s*[\'"].*?[\'"][^>]*)>#is', $buffer, $elements, PREG_SET_ORDER)) {
            return $html;
        }

        $force_rule = $this->get_lazyload_forced_atts();
        $exclude_rule = $this->get_lazyload_exclude_rule();

        foreach ($elements as $element) {

            //get element tag attributes
            $element_atts = $this->lazyload_get_atts_array($element[2]);

            //dont check excluded if forced attribute was found
            if (!$this->lazyload_excluded($element[2], $force_rule)) {

                //skip if no-lazy class is found
                if (!empty($element_atts['class']) && strpos($element_atts['class'], 'no-lazy') !== false) {
                    continue;
                }

                //skip if exluded attribute was found
                if ($this->lazyload_excluded($element[2], $exclude_rule)) {
                    continue;
                }
            }

            //skip if no style attribute
            if (!isset($element_atts['style'])) {
                continue;
            }

            //match background-image in style string
            if (!preg_match('#background(-image)?\s*:\s*(\s*url\s*\((?<url>[^)]+)\))\s*;?#is', $element_atts['style'], $url)) {
                continue;
            }

            $url['url'] = trim($url['url'], '\'" ');

            //add lazyload class
            $element_atts['class'] = (!empty($element_atts['class']) ? $element_atts['class'] . ' ' : '') . 'wpturbo-lazy';

            //remove background image url from inline style attribute
            $element_atts['style'] = str_replace($url[0], '', $element_atts['style']);

            //migrate src
            $element_atts['data-bg'] = 'url(' . esc_attr($url['url']) . ')';

            //build lazy element attributes string
            $lazy_element_atts_string = $this->lazyload_get_atts_string($element_atts);

            //build lazy element
            $lazy_element = sprintf('<' . $element['tag'] . ' %1$s >', $lazy_element_atts_string);

            //replace element with placeholder
            $html = str_replace($element[0], $lazy_element, $html);

            unset($lazy_element);
        }

        return $html;
    }



    //lazy load iframes
    public function lazy_load_iframes($html, $buffer)
    {

        //match all iframes
        if (!preg_match_all('#<iframe(\s.+)>.*</iframe>#iUs', $buffer, $iframes, PREG_SET_ORDER)) {
            return $html;
        }

        $cnf = self::cnf();

        //remove any duplicates
        $iframes = array_unique($iframes, SORT_REGULAR);

        $force_rule = $this->get_lazyload_forced_atts();
        $exclude_rule = $this->get_lazyload_exclude_rule();

        foreach ($iframes as $iframe) {

            //get iframe attributes array
            $iframe_atts = $this->lazyload_get_atts_array($iframe[1]);

            //dont check excluded if forced attribute was found
            if (!$this->lazyload_excluded($iframe[1], $force_rule)) {

                //skip if exluded attribute was found
                if ($this->lazyload_excluded($iframe[1], $exclude_rule)) {
                    continue;
                }

                //skip if no-lazy class is found
                if (!empty($iframe_atts['class']) && strpos($iframe_atts['class'], 'do-not-lazyload') !== false) {
                    continue;
                }
            }

            //skip if no src is found
            if (empty($iframe_atts['src'])) {
                continue;
            }

            $iframe['src'] = trim($iframe_atts['src']);

            //try rendering youtube preview placeholder if we need to
            if (!empty($cnf['youtube_preview_thumbnails'])) {
                $iframe_lazyload = $this->lazy_load_youtube_iframe($iframe);
            }

            //default iframe placeholder
            if (empty($iframe_lazyload)) {

                $iframe_atts['class'] = (!empty($iframe_atts['class']) ? $iframe_atts['class'] . ' ' : '') . 'wpturbo-lazy';

                //migrate src
                $iframe_atts['data-src'] = $iframe_atts['src'];
                unset($iframe_atts['src']);

                //build lazy iframe attributes string
                $lazy_iframe_atts_string = $this->lazyload_get_atts_string($iframe_atts);

                //replace iframe attributes string
                $iframe_lazyload = str_replace($iframe[1], ' ' . $lazy_iframe_atts_string, $iframe[0]);

                //add noscript original iframe
                $iframe_lazyload .= '<noscript>' . $iframe[0] . '</noscript>';
            }

            //replace iframe with placeholder
            $html = str_replace($iframe[0], $iframe_lazyload, $html);

            unset($iframe_lazyload);
        }

        return $html;
    }

    //prep youtube iframe for lazy loading
    public function lazy_load_youtube_iframe($iframe)
    {

        if (!$iframe) {
            return false;
        }

        //attempt to get the id based on url
        $result = preg_match('#^(?:https?:)?(?://)?(?:www\.)?(?:youtu\.be|youtube\.com|youtube-nocookie\.com)/(?:embed/|v/|watch/?\?v=)?([\w-]{11})#iU', $iframe['src'], $matches);

        //return false if there is no usable id
        if (!$result || $matches[1] === 'videoseries') {
            return false;
        }

        $youtube_id = $matches[1];

        //parse iframe src url
        $query = wp_parse_url(htmlspecialchars_decode($iframe['src']), PHP_URL_QUERY);

        //clean up the url
        $parsed_url = wp_parse_url($iframe['src'], -1);
        $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '//';
        $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
        $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
        $youtube_url = $scheme . $host . $path;

        //thumbnail resolutions
        $resolutions = array(
            'default'       => array(
                'width'  => 120,
                'height' => 90,
            ),
            'mqdefault'     => array(
                'width'  => 320,
                'height' => 180,
            ),
            'hqdefault'     => array(
                'width'  => 480,
                'height' => 360,
            ),
            'sddefault'     => array(
                'width'  => 640,
                'height' => 480,
            ),
            'maxresdefault' => array(
                'width'  => 1280,
                'height' => 720,
            )
        );

        //filter set resolution
        $resolution = apply_filters('wpturbo_lazyload_youtube_thumbnail_resolution', 'hqdefault');

        //finished youtube lazy output
        $youtube_lazyload = '<div class="wpturbo-lazy-youtube" data-src="' . esc_attr($youtube_url) . '" data-id="' . esc_attr($youtube_id) . '" data-query="' . esc_attr($query) . '" onclick="wpturboLazyLoadYouTube(this);">';
        $youtube_lazyload .= '<div>';
        $youtube_lazyload .= '<img class="wpturbo-lazy" src="data:image/svg+xml,%3Csvg%20xmlns=\'http://www.w3.org/2000/svg\'%20viewBox=\'0%200%20' . $resolutions[$resolution]['width'] . '%20' . $resolutions[$resolution]['height'] . '%3E%3C/svg%3E" data-src="https://i.ytimg.com/vi/' . esc_attr($youtube_id) . '/' . $resolution . '.jpg" alt="YouTube Video" width="' . $resolutions[$resolution]['width'] . '" height="' . $resolutions[$resolution]['height'] . '" data-pin-nopin="true">';
        $youtube_lazyload .= '<div class="play"></div>';
        $youtube_lazyload .= '</div>';
        $youtube_lazyload .= '</div>';
        $youtube_lazyload .= '<noscript>' . $iframe[0] . '</noscript>';

        return $youtube_lazyload;
    }

    //lazy load videos
    public function lazy_load_videos($html, $buffer)
    {

        //match all videos
        if (!preg_match_all('#<video(\s.+)>.*</video>#iUs', $buffer, $videos, PREG_SET_ORDER)) {
            return $html;
        }
        //get plugin options
        $cnf = self::cnf();

        //remove any duplicates
        $videos = array_unique($videos, SORT_REGULAR);

        $force_rule = $this->get_lazyload_forced_atts();
        $exclude_rule = $this->get_lazyload_exclude_rule();
        foreach ($videos as $video) {

            //get video attributes array
            $video_atts = $this->lazyload_get_atts_array($video[1]);

            //dont check excluded if forced attribute was found
            if (!$this->lazyload_excluded($video[1], $force_rule)) {

                //skip if exluded attribute was found
                if ($this->lazyload_excluded($video[1], $exclude_rule)) {
                    continue;
                }

                //skip if no-lazy class is found
                if (!empty($video_atts['class']) && strpos($video_atts['class'], 'do-not-lazyload') !== false) {
                    continue;
                }
            }

            //skip if no src is found
            if (empty($video_atts['src'])) {
                continue;
            }

            //add lazyload class
            $video_atts['class'] = (!empty($video_atts['class']) ? $video_atts['class'] . ' ' : '') . 'wpturob-lazy';

            //migrate src
            $video_atts['data-src'] = $video_atts['src'];
            unset($video_atts['src']);

            //build lazy video attributes string
            $lazy_video_atts_string = $this->lazyload_get_atts_string($video_atts);

            //replace video attributes string
            $video_lazyload  = str_replace($video[1], ' ' . $lazy_video_atts_string, $video[0]);

            //add noscript original video
            $video_lazyload .= '<noscript>' . $video[0] . '</noscript>';

            //replace video with placeholder
            $html = str_replace($video[0], $video_lazyload, $html);

            unset($video_lazyload);
        }

        return $html;
    }


    //initialize lazy load instance
    public function print_lazy_load_js()
    {

        $cnf = self::cnf();

        $viewpoint = apply_filters('wpturbo_lazyload_viewpoint', ($cnf['lazy_viewpoint'] ? $cnf['lazy_viewpoint'] : '0px'));

        $inline_js = '<script>';

        $inline_js .= 'document.addEventListener("DOMContentLoaded",function(){';

        //initialize lazy loader
        $inline_js .= 'var lazyLoadInstance=new LazyLoad({elements_selector:"img[data-src],.wpturbo-lazy",thresholds:"' . $viewpoint . ' 0px",callback_loaded:function(element){if(element.tagName==="IFRAME"){if(element.classList.contains("loaded")){if(typeof window.jQuery!="undefined"){if(jQuery.fn.fitVids){jQuery(element).parent().fitVids()}}}}}});';

        //dom monitoring
        if ($cnf['lazy_new_dom']) {
            $inline_js .= 'var target=document.querySelector("body");var observer=new MutationObserver(function(mutations){lazyLoadInstance.update()});var config={childList:!0,subtree:!0};observer.observe(target,config);';
        }

        $inline_js .= '});';

        //youtube thumbnails
        if ($cnf['lazy_frame_video'] && $cnf['youtube_preview_thumbnails']) {
            $inline_js .= 'function wpturboLazyLoadYouTube(e){var t=document.createElement("iframe"),r="ID?";r+=0===e.dataset.query.length?"":e.dataset.query+"&",r+="autoplay=1",t.setAttribute("src",r.replace("ID",e.dataset.src)),t.setAttribute("frameborder","0"),t.setAttribute("allowfullscreen","1"),t.setAttribute("allow","accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"),e.replaceChild(t,e.firstChild)}';
        }
        $inline_js .= '</script>';

        echo $inline_js;
    }

    //print lazy load styles
    public function print_lazy_load_css()
    {

        $cnf = self::cnf();

        //print noscript styles
        echo '<noscript><style>.wpturbo-lazy[data-src]{display:none !important;}</style></noscript>';

        $inline_styles = '';

        //youtube thumbnails
        if ($cnf['lazy_frame_video'] && $cnf['youtube_preview_thumbnails']) {
            $cover = plugins_url('assets/youtube.svg', WPTURBO_BASE);
            $inline_styles .= '.wpturbo-lazy-youtube{position:relative;width:100%;max-width:100%;height:0;padding-bottom:56.23%;overflow:hidden}.wpturbo-lazy-youtube img{position:absolute;top:0;right:0;bottom:0;left:0;display:block;width:100%;max-width:100%;height:auto;margin:auto;border:none;cursor:pointer;transition:.5s all;-webkit-transition:.5s all;-moz-transition:.5s all}.wpturbo-lazy-youtube img:hover{-webkit-filter:brightness(75%)}.wpturbo-lazy-youtube .play{position:absolute;top:50%;left:50%;width:68px;height:48px;margin-left:-34px;margin-top:-24px;background:url(' . $cover . ') no-repeat;background-position:center;background-size:cover;pointer-events:none}.wpturbo-lazy-youtube iframe{position:absolute;top:0;left:0;width:100%;height:100%;z-index:99}';
            if (current_theme_supports('responsive-embeds')) {
                $inline_styles .= '.wp-has-aspect-ratio .wp-block-embed__wrapper{position:relative;}.wp-has-aspect-ratio .wpturbo-lazy-youtube{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;padding-bottom:0}';
            }
        }

        //fade in effect
        if ($cnf['lazy_image_fade_in']) {
            $inline_styles .= '.wpturbo-lazy:not(picture),.wpturbo-lazy img{opacity:0}.wpturbo-lazy.loaded,.wpturbo-lazy img.loaded,.wpturbo-lazy[data-was-processed=true]{opacity:1;transition:opacity ' . apply_filters('wpturbo_lazyload_image_fade_in_speed', 500) . 'ms}';
        }

        //print styles
        if ($inline_styles) {
            echo '<style>' . $inline_styles . '</style>';
        }
    }


    //fix images missing dimensions
    public function image_dimensions($html)
    {
        //match all img tags without width or height attributes
        if (!preg_match_all('#<img((?:[^>](?!(height|width)=[\'\"](?:\S+)[\'\"]))*+)>#is', $html, $images, PREG_SET_ORDER)) {
            return $html;
        }

        //remove any duplicate images
        $images = array_unique($images, SORT_REGULAR);

        //loop through images
        foreach ($images as $image) {

            //get image attributes array
            $image_atts = $this->lazyload_get_atts_array($image[1]);

            if (empty($image_atts['src'])) {
                continue;
            }

            //get image dimensions
            $dimensions = $this->get_dimensions_from_url($image_atts['src']);
            if (empty($dimensions)) {
                continue;
            }

            //remove any existing dimension attributes
            $new_image = preg_replace('/(height|width)=[\'"](?:\S+)*[\'"]/i', '', $image[0]);

            //add dimension attributes to img tag
            $new_image = preg_replace('/<\s*img/i', '<img width="' . $dimensions['width'] . '" height="' . $dimensions['height'] . '"', $new_image);
            //replace original img tag in html
            if (!empty($new_image)) {
                $html = str_replace($image[0], $new_image, $html);
            }
        }

        return $html;
    }

    public function get_dimensions_from_url($url)
    {
        //grab dimensions from file name if available
        if (preg_match('/(?:.+)-([0-9]+)x([0-9]+)\.(jpg|jpeg|png|gif|svg)$/', $url, $matches)) {
            return array('width' => $matches[1], 'height' => $matches[2]);
        }

        //get image path
        $image_path = ABSPATH . wp_parse_url($url)['path'];

        if (file_exists($image_path)) {

            //get dimensions from file
            $sizes = getimagesize($image_path);

            if (!empty($sizes)) {
                return array('width' => $sizes[0], 'height' => $sizes[1]);
            }
        }

        return false;
    }
}