close

Plugin Directory

Changeset 3398277


Ignore:
Timestamp:
11/18/2025 06:19:54 PM (5 months ago)
Author:
nosilver4u
Message:

tagging and releasing 4.3.0

Location:
easy-image-optimizer
Files:
10 added
30 edited
1 copied

Legend:

Unmodified
Added
Removed
  • easy-image-optimizer/tags/4.3.0/changelog.txt

    r3350722 r3398277  
     1= 4.3.0 =
     2*Release Date - November 18, 2025*
     3
     4* added: Lazy Load support for background images in external CSS files
     5* added: View CDN bandwidth usage on settings page
     6* changed: Lazy Load checks parent element for skip-lazy class
     7* changed: Lazy Load auto-sizing honors High DPI setting
     8* changed: Easy IO fills in 450px wide image when responsive (srcset) images have a gap
     9* improved: Lazy Load performance when searching for img elements
     10* improved: Lazy Load placeholder generation is faster and works better with Safari
     11* fixed: Lazy Load for iframes breaks WP Remote Users Sync plugin
     12
    113= 4.2.1 =
    214*Release Date - August 26, 2025*
  • easy-image-optimizer/tags/4.3.0/classes/class-base.php

    r3328397 r3398277  
    327327            if ( $this->is_iterable( $potential_logs ) ) {
    328328                foreach ( $potential_logs as $potential_log ) {
    329                     if ( $this->str_ends_with( $potential_log, '.log' ) && false !== strpos( $potential_log, strtolower( __NAMESPACE__ ) . '-debug-' ) && is_file( $this->content_dir . $potential_log ) ) {
     329                    if ( \str_ends_with( $potential_log, '.log' ) && false !== strpos( $potential_log, strtolower( __NAMESPACE__ ) . '-debug-' ) && is_file( $this->content_dir . $potential_log ) ) {
    330330                        return $this->content_dir . $potential_log;
    331331                    }
     
    13491349
    13501350    /**
    1351      * Performs a case-sensitive check indicating if
    1352      * the haystack ends with needle.
    1353      *
    1354      * @param string $haystack The string to search in.
    1355      * @param string $needle   The substring to search for in the `$haystack`.
    1356      * @return bool True if `$haystack` ends with `$needle`, otherwise false.
    1357      */
    1358     public function str_ends_with( $haystack, $needle ) {
    1359         if ( '' === $haystack && '' !== $needle ) {
    1360             return false;
    1361         }
    1362 
    1363         $len = \strlen( $needle );
    1364 
    1365         return 0 === \substr_compare( $haystack, $needle, -$len, $len );
     1351     * Wrapper around size_format to remove the decimal from sizes in bytes.
     1352     *
     1353     * @param int $size A filesize in bytes.
     1354     * @param int $precision Number of places after the decimal separator.
     1355     * @return string Human-readable filesize.
     1356     */
     1357    public function size_format( $size, $precision = 1 ) {
     1358            // Convert it to human readable format.
     1359            $size_str = \size_format( $size, $precision );
     1360            // Remove spaces and extra decimals when measurement is in bytes.
     1361            return \preg_replace( '/\.0+ B ?/', ' B', $size_str );
    13661362    }
    13671363
     
    16331629            return false;
    16341630        }
    1635         if ( 0 === \strpos( $url, '//' ) ) {
     1631        if ( \str_starts_with( $url, '//' ) ) {
    16361632            $url = ( \is_ssl() ? 'https:' : 'http:' ) . $url;
    16371633        }
    1638         if ( false === \strpos( $url, 'http' ) && '/' !== \substr( $url, 0, 1 ) ) {
     1634        if ( ! \str_starts_with( $url, 'http' ) && ! \str_starts_with( $url, '/' ) && ! \str_starts_with( $url, '.' ) ) {
    16391635            $url = ( \is_ssl() ? 'https://' : 'http://' ) . $url;
    16401636        }
  • easy-image-optimizer/tags/4.3.0/classes/class-exactdn.php

    r3350722 r3398277  
    6565     */
    6666    public $full_width = false;
     67
     68    /**
     69     * Indicates the local domain has changed, and Easy IO should be re-initialized.
     70     *
     71     * @access public
     72     * @var bool $domain_mismatch
     73     */
     74    public $domain_mismatch = false;
    6775
    6876    /**
     
    399407        \add_filter( 'autoptimize_filter_cssjs_multidomain', array( $this, 'add_cdn_domain' ) );
    400408
    401         if ( $this->is_as3cf_cname_active() ) {
    402             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_as3cf_cname_active' );
    403             return;
    404         }
     409        \add_action( 'admin_notices', array( $this, 'admin_notices' ) );
    405410
    406411        $upload_url_parts = $this->parse_url( $this->site_url );
     
    414419            $this->set_exactdn_option( 'local_domain', \base64_encode( $this->upload_domain ) );
    415420            $stored_local_domain = $this->upload_domain;
    416         } elseif ( false !== \strpos( $stored_local_domain, '.' ) ) {
     421        } elseif ( \str_contains( $stored_local_domain, '.' ) ) {
    417422            $this->set_exactdn_option( 'local_domain', \base64_encode( $stored_local_domain ) );
    418         } else {
    419             $stored_local_domain = \base64_decode( $stored_local_domain );
    420         }
    421         $this->debug_message( "saved domain is $stored_local_domain" );
     423        }
     424        $this->debug_message( "saved (local) domain is $stored_local_domain" );
    422425
    423426        $this->debug_message( "allowing images from here: $this->upload_domain" );
     
    433436            $this->debug_message( "removing this from urls: $this->remove_path" );
    434437        }
    435         if (
    436             $stored_local_domain !== $this->upload_domain &&
    437             ! $this->allow_image_domain( $stored_local_domain ) &&
    438             \is_admin()
    439         ) {
    440             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_domain_mismatch' );
    441         }
    442438        $this->allowed_domains[] = $this->exactdn_domain;
    443439        $this->allowed_domains   = \apply_filters( 'exactdn_allowed_domains', $this->allowed_domains );
     
    500496     */
    501497    public function cron_setup( $schedule = true ) {
    502         $this->debug_message( '<b>' . __FUNCTION__ . '()</b>' );
     498        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    503499        $event = 'easyio_verification_checkin';
    504500        // Setup scheduled optimization if the user has enabled it, and it isn't already scheduled.
     
    530526
    531527    /**
     528     * Sends the useragent through filters for http requests to the EWWW IO API.
     529     *
     530     * @param string $useragent The current useragent used in http requests.
     531     * @return string The filtered useragent.
     532     */
     533    public function api_useragent( $useragent ) {
     534        return apply_filters( 'exactdn_api_request_useragent', $useragent );
     535    }
     536
     537    /**
    532538     * Use the Site URL to get the zone domain.
    533539     */
     
    538544            global $exactdn_activate_error;
    539545            $exactdn_activate_error = 'as3cf_cname_active';
    540             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_as3cf_cname_active' );
    541546            return false;
    542547        }
     
    548553            $url = \set_url_scheme( $url, 'https' );
    549554        }
    550         \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     555        \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    551556        $result = \wp_remote_post(
    552557            $url,
     
    564569            global $exactdn_activate_error;
    565570            $exactdn_activate_error = $error_message;
    566             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    567571            return false;
    568572        } elseif ( ! empty( $result['body'] ) && \strpos( $result['body'], 'domain' ) !== false ) {
     
    583587                if ( \get_option( 'exactdn_never_been_active' ) ) {
    584588                    $this->set_option( $this->prefix . 'lazy_load', true );
    585                     $this->set_option( 'exactdn_lossy', true );
    586589                    $this->set_option( 'exactdn_all_the_things', true );
    587590                    \delete_option( 'exactdn_never_been_active' );
     
    598601            global $exactdn_activate_error;
    599602            $exactdn_activate_error = $error_message;
    600             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    601603            return false;
    602604        }
     
    649651            $test_url     = \str_replace( $local_domain, $domain, $test_url );
    650652            $this->debug_message( "test url is $test_url" );
    651             \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     653            \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    652654            $test_result = \wp_remote_post(
    653655                $api_url,
     
    665667                $this->debug_message( "exactdn (1) verification request failed: $error_message" );
    666668                $exactdn_activate_error = $error_message;
    667                 \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    668669                return false;
    669             } elseif ( ! empty( $test_result['body'] ) && false === \strpos( $test_result['body'], 'error' ) ) {
     670            } elseif ( ! empty( $test_result['body'] ) && ! \str_contains( $test_result['body'], 'error' ) ) {
    670671                $response = \json_decode( $test_result['body'], true );
    671672                if ( ! empty( $response['success'] ) ) {
     
    677678                        $this->asset_domains = $response['asset_domains'];
    678679                    }
    679                     \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_success' );
    680680                    return true;
    681681                }
     
    689689                    \delete_site_option( $this->prefix . 'exactdn_domain' );
    690690                }
    691                 \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    692691                return false;
    693692            }
     
    695694                $this->debug_message( 'received response code: ' . $test_result['response']['code'] );
    696695            }
    697             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    698696            return false;
    699697        }
    700698
    701699        // Secondary test against the API db.
    702         \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     700        \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    703701        $result = \wp_remote_post(
    704702            $api_url,
     
    715713            $this->debug_message( "exactdn verification request failed: $error_message" );
    716714            $exactdn_activate_error = $error_message;
    717             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    718715            return false;
    719         } elseif ( ! empty( $result['body'] ) && false === \strpos( $result['body'], 'error' ) ) {
     716        } elseif ( ! empty( $result['body'] ) && ! \str_contains( $result['body'], 'error' ) ) {
    720717            $response = \json_decode( $result['body'], true );
    721718            if ( ! empty( $response['success'] ) ) {
     
    730727                $this->debug_message( 'exactdn verification via API succeeded' );
    731728                $this->set_exactdn_option( 'verified', 1, false );
    732                 if ( empty( $last_checkin ) ) {
    733                     \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_success' );
    734                 }
    735729                return true;
    736730            }
     
    744738                \delete_site_option( $this->prefix . 'exactdn_domain' );
    745739            }
    746             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    747740            return false;
    748741        }
     
    750743            $this->debug_message( 'received response code: ' . $result['response']['code'] );
    751744        }
    752         \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    753745        return false;
    754746    }
     
    762754            // Prelim test with a known valid image to ensure http(s) connectivity.
    763755            $sim_url = 'https://optimize.exactdn.com/exactdn/testorig.jpg';
    764             \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     756            \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    765757            $sim_result = \wp_remote_get( $sim_url );
    766758            if ( \is_wp_error( $sim_result ) ) {
     
    900892        }
    901893        return \update_option( $this->prefix . 'exactdn_' . $option_name, $option_value, $autoload );
     894    }
     895
     896    /**
     897     * Check for conditions that need to trigger an admin notice (action).
     898     */
     899    public function admin_notices() {
     900        if ( $this->is_as3cf_cname_active() ) {
     901            \do_action( 'exactdn_as3cf_cname_active' );
     902        }
     903        $stored_local_domain = $this->get_exactdn_option( 'local_domain' );
     904        $stored_local_domain = \base64_decode( $stored_local_domain );
     905        if (
     906            $stored_local_domain !== $this->upload_domain &&
     907            ! $this->allow_image_domain( $stored_local_domain )
     908        ) {
     909            $this->domain_mismatch = true;
     910            \do_action( 'exactdn_domain_mismatch' );
     911        }
    902912    }
    903913
     
    29522962         * @param array|bool $multipliers Array of multipliers to use or false to bypass.
    29532963         */
    2954         $multipliers = \apply_filters( 'exactdn_srcset_multipliers', array( .2, .4, .6, .8, 1 ) );
     2964        $multipliers = \apply_filters( 'exactdn_srcset_multipliers', array( .2, .4, .6, .8, 1, 450 ) );
    29552965
    29562966        if ( empty( $url ) || empty( $multipliers ) ) {
     
    30393049
    30403050                $newwidth = \intval( $base * $multiplier );
    3041                 if ( 1920 === (int) $multiplier ) {
    3042                     $newwidth = 1920;
    3043                     if ( ! $w_descriptor || 1920 >= $reqwidth || 'soft' !== $crop ) {
     3051                if ( $multiplier > 50 ) { // Not a true multiplier, but a hard-coded width.
     3052                    $newwidth = $multiplier;
     3053                    if ( ! $w_descriptor || $multiplier >= $reqwidth || 'soft' !== $crop ) {
    30443054                        $this->debug_message( "skipping $multiplier due to no w descriptor, larger than $reqwidth, or $crop !== soft" );
    30453055                        continue;
     
    31503160         * @param array|bool $multipliers Array of multipliers to use or false to bypass.
    31513161         */
    3152         $multipliers = \apply_filters( 'exactdn_srcset_multipliers', array( .2, .4, .6, .8, 1 ) );
     3162        $multipliers = \apply_filters( 'exactdn_srcset_multipliers', array( .2, .4, .6, .8, 1, 450 ) );
    31533163        /**
    31543164         * Filter the width ExactDN will use to create srcset attribute.
     
    31753185            foreach ( $multipliers as $multiplier ) {
    31763186                $newwidth = \intval( $width * $multiplier );
    3177                 if ( 1920 === (int) $multiplier ) {
     3187                if ( $multiplier > 50 ) { // Not a true multiplier, but a hard-coded width.
    31783188                    if ( $multiplier >= $width ) {
    31793189                        continue;
    31803190                    }
    3181                     $newwidth = 1920;
     3191                    $newwidth = $multiplier;
    31823192                }
    31833193                if ( $newwidth < 50 ) {
     
    35693579        if ( $this->get_option( 'exactdn_hidpi' ) ) {
    35703580            $this->debug_message( 'adding hidpi multipliers' );
    3571             return array( .2, .4, .6, .8, 1, 2, 3, 1920 );
     3581            $multipliers[] = 2;
     3582            $multipliers[] = 3;
     3583            $multipliers[] = 1920;
    35723584        }
    35733585        return $multipliers;
     
    43174329
    43184330        $more_args = array();
    4319         if ( false === \strpos( $image_url, 'strip=all' ) && $this->get_option( $this->prefix . 'metadata_remove' ) ) {
     4331        if ( ! \str_contains( $image_url, 'strip=all' ) && $this->get_option( $this->prefix . 'metadata_remove' ) ) {
    43204332            $more_args['strip'] = 'all';
    43214333        }
     
    43254337        } elseif ( isset( $args['lossy'] ) && false !== \strpos( $image_url, 'lossy=0' ) ) {
    43264338            unset( $args['lossy'] );
    4327         } elseif ( false === \strpos( $image_url, 'lossy=' ) && ! $this->get_option( 'exactdn_lossy' ) ) {
    4328             $more_args['lossy'] = 0;
    4329         } elseif ( false === \strpos( $image_url, 'lossy=' ) && $this->get_option( 'exactdn_lossy' ) ) {
    4330             $more_args['lossy'] = \is_numeric( $this->get_option( 'exactdn_lossy' ) ) ? (int) $this->get_option( 'exactdn_lossy' ) : 1;
    43314339        }
    43324340        if ( false === \strpos( $image_url, 'quality=' ) && ! \is_null( $jpg_quality ) && 82 !== (int) $jpg_quality ) {
     
    43764384
    43774385        if ( isset( $image_url_parts['scheme'] ) && 'https' === $image_url_parts['scheme'] ) {
    4378             if ( \is_array( $args ) && false === \strpos( $image_url, 'ssl=' ) ) {
    4379                 $this->debug_message( 'adding ssl=1' );
    4380                 $args['ssl'] = 1;
    4381             }
    43824386            $this->debug_message( 'setting scheme to https' );
    43834387            $scheme = 'https';
     
    43974401            foreach ( $args as $arg => $value ) {
    43984402                if ( \is_array( $value ) ) {
    4399                     $args[ $arg ] = \implode( ',', $value );
     4403                    $value        = \implode( ',', $value );
     4404                    $args[ $arg ] = $value;
    44004405                }
    44014406            }
     
    45664571            $url = \set_url_scheme( $url, 'https' );
    45674572        }
    4568         \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     4573        \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    45694574        $result = \wp_remote_post(
    45704575            $url,
  • easy-image-optimizer/tags/4.3.0/classes/class-hs-beacon.php

    r3035873 r3398277  
    101101            return;
    102102        }
    103         if ( \strpos( __FILE__, 'plugins/easy' ) ) {
    104             \easyio_notice_beacon();
    105         }
     103        \do_action( strtolower( __NAMESPACE__ ) . '_beacon_notice' );
    106104    }
    107105}
  • easy-image-optimizer/tags/4.3.0/classes/class-lazy-load.php

    r3328397 r3398277  
    9696
    9797    /**
     98     * A list of image tags/sections where lazy loading should not be applied.
     99     *
     100     * @access private
     101     * @var array $forbidden_blocks
     102     */
     103    private $forbidden_blocks = array();
     104
     105    /**
    98106     * Request URI.
    99107     *
     
    101109     */
    102110    public $request_uri = '';
     111
     112    /**
     113     * DOM Document for parsing HTML.
     114     *
     115     * @var \DOMDocument $doc
     116     */
     117    private $doc;
     118
     119    /**
     120     * List of img nodes from the DOMDocument.
     121     *
     122     * @var \DOMNodeList $img_nodes
     123     */
     124    private $img_nodes;
    103125
    104126    /**
     
    198220            \add_action( 'wp_enqueue_scripts', array( $this, 'min_script' ), 1 );
    199221        }
     222
     223        // Allow other plugins to get the background image exclusions via filter.
     224        \add_filter( 'eio_get_lazy_bg_image_exclusions', array( $this, 'get_bgimage_exclusions' ), 10 );
     225
    200226        $this->inline_script_attrs = (array) \apply_filters( 'ewwwio_inline_script_attrs', $this->inline_script_attrs );
    201227        $this->validate_user_exclusions();
     
    354380
    355381    /**
     382     * Check if an img/iframe tag should be excluded because it falls within a forbidden block.
     383     *
     384     * @param string $tag The img or iframe tag to check.
     385     * @param int    $position The position of the tag within the HTML.
     386     * @return bool True if it is within a forbidden block, false otherwise.
     387     */
     388    private function is_in_forbidden_block( $tag, $position ) {
     389        if ( empty( $tag ) || empty( $position ) ) {
     390            return false;
     391        }
     392        if ( $this->is_iterable( $this->forbidden_blocks ) ) {
     393            foreach ( $this->forbidden_blocks as $forbidden_block ) {
     394                if ( empty( $forbidden_block[0] ) || empty( $forbidden_block[1] ) ) {
     395                    continue;
     396                }
     397                $start = $forbidden_block[1];
     398                $end   = $start + \strlen( $forbidden_block[0] );
     399                if ( $position > $start && $position < $end ) {
     400                    $this->debug_message( 'tag is within a forbidden block' );
     401                    return true;
     402                }
     403            }
     404        }
     405    }
     406
     407    /**
    356408     * Search for img elements and rewrite them for Lazy Load with fallback to noscript elements.
    357409     *
     
    391443        }
    392444
     445        $started_time     = \microtime( true );
    393446        $above_the_fold   = (int) \apply_filters( 'eio_lazy_fold', $this->get_option( $this->prefix . 'll_abovethefold' ) );
    394447        $images_processed = 0;
    395448        $replacements     = array();
    396449
    397         // Clean the buffer of incompatible sections.
    398         $search_buffer = \preg_replace( '/<div id="footer_photostream".*?\/div>/s', '', $buffer );
    399         $search_buffer = \preg_replace( '/<(picture|noscript|script).*?\/\1>/s', '', $search_buffer );
    400 
    401         $images = $this->get_images_from_html( $search_buffer, false );
     450        // Find all incompatible sections, and store them with their offsets.
     451        $this->forbidden_blocks = array();
     452        \preg_match_all( '/<div id="footer_photostream".*?\/div>/s', $buffer, $forbidden_blocks, PREG_OFFSET_CAPTURE );
     453        \preg_match_all( '/<(picture|noscript|script).*?\/\1>/s', $buffer, $more_forbidden_blocks, PREG_OFFSET_CAPTURE );
     454        if ( ! empty( $forbidden_blocks[0] ) && ! empty( $more_forbidden_blocks[0] ) ) {
     455            $forbidden_blocks[0] = \array_merge( $forbidden_blocks[0], $more_forbidden_blocks[0] );
     456        } elseif ( ! empty( $more_forbidden_blocks[0] ) ) {
     457            $forbidden_blocks = $more_forbidden_blocks;
     458        }
     459        $this->forbidden_blocks = ! empty( $forbidden_blocks[0] ) ? $forbidden_blocks[0] : array();
     460
     461        $this->doc = new \DOMDocument();
     462        libxml_use_internal_errors( true );
     463        $this->doc->loadHTML( $buffer );
     464        libxml_clear_errors();
     465        $this->img_nodes = $this->doc->getElementsByTagName( 'img' );
     466
     467        $images = $this->get_images_from_html( $buffer, false, true, PREG_OFFSET_CAPTURE );
    402468        if ( ! empty( $images[0] ) && $this->is_iterable( $images[0] ) ) {
    403469            foreach ( $images[0] as $index => $image ) {
    404                 $file = $images['img_url'][ $index ];
     470                $file = $images['img_url'][ $index ][0];
    405471                $this->debug_message( "parsing an image: $file" );
    406                 if ( $this->validate_image_tag( $image ) ) {
     472                if ( empty( $image[0] ) || empty( $image[1] ) ) {
     473                    $this->debug_message( 'missing image tag or position' );
     474                    continue;
     475                }
     476                $image_tag = $image[0];
     477                $position  = $image[1];
     478                if ( $this->is_in_forbidden_block( $image_tag, $position ) ) {
     479                    continue;
     480                }
     481                if ( $this->validate_image_tag( $image_tag ) ) {
    407482                    $this->debug_message( 'found a valid image tag' );
    408                     $this->debug_message( "original image tag: $image" );
    409                     $orig_img = $image;
    410                     $ns_img   = $image;
    411                     $image    = $this->parse_img_tag( $image, $file );
     483                    $this->debug_message( "original image tag: $image_tag" );
     484                    $orig_img  = $image_tag;
     485                    $ns_img    = $image_tag;
     486                    $image_tag = $this->parse_img_tag( $image_tag, $file );
    412487                    $this->set_attribute( $ns_img, 'data-eio', 'l', true );
    413488                    $noscript = '<noscript>' . $ns_img . '</noscript>';
    414                     $position = \strpos( $buffer, $orig_img );
    415                     if ( $position && $orig_img !== $image ) {
     489                    if ( $position && $orig_img !== $image_tag ) {
     490                        $this->debug_message( "lazified image at position $position" );
    416491                        $replacements[ $position ] = array(
    417492                            'orig' => $orig_img,
    418                             'lazy' => $image . $noscript,
     493                            'lazy' => $image_tag . $noscript,
    419494                        );
    420495                    }
    421                     /* $buffer   = str_replace( $orig_img, $image . $noscript, $buffer ); */
    422496                }
    423497            } // End foreach().
    424498        } // End if().
     499
    425500        $element_types = \apply_filters( 'eio_allowed_background_image_elements', array( 'div', 'li', 'span', 'section', 'a' ) );
    426501        foreach ( $element_types as $element_type ) {
     
    435510            }
    436511        }
     512
    437513        if ( \in_array( 'picture', $this->user_element_exclusions, true ) ) {
    438514            $pictures = '';
    439515        } else {
    440516            // Images listed as picture/source elements. Mostly for NextGEN, but should work anywhere.
    441             $pictures = $this->get_picture_tags_from_html( $buffer );
     517            $pictures = $this->get_picture_tags_from_html( $buffer, PREG_OFFSET_CAPTURE );
    442518        }
    443519        if ( $this->is_iterable( $pictures ) ) {
    444520            foreach ( $pictures as $index => $picture ) {
    445                 if ( ! $this->validate_image_tag( $picture ) ) {
    446                     continue;
    447                 }
    448                 $pimages = $this->get_images_from_html( $picture, false );
     521                if ( empty( $picture[0] ) || empty( $picture[1] ) ) {
     522                    $this->debug_message( 'missing picture tag or position' );
     523                    continue;
     524                }
     525                $picture_tag = $picture[0];
     526                $position    = $picture[1];
     527                if ( ! $this->validate_image_tag( $picture_tag ) ) {
     528                    continue;
     529                }
     530                $pimages = $this->get_images_from_html( $picture_tag, false );
    449531                if ( ! empty( $pimages[0] ) && $this->is_iterable( $pimages[0] ) && ! empty( $pimages[0][0] ) ) {
    450532                    $image = $pimages[0][0];
     
    458540                        $image    = $this->parse_img_tag( $image, $file );
    459541                        $this->set_attribute( $ns_img, 'data-eio', 'l', true );
    460                         $noscript = '<noscript>' . $ns_img . '</noscript>';
    461                         $picture  = \str_replace( $orig_img, $image, $picture ) . $noscript;
     542                        $noscript    = '<noscript>' . $ns_img . '</noscript>';
     543                        $picture_tag = \str_replace( $orig_img, $image, $picture_tag ) . $noscript;
    462544                    }
    463545                } else {
    464546                    continue;
    465547                }
    466                 $sources = $this->get_elements_from_html( $picture, 'source' );
     548                $sources = $this->get_elements_from_html( $picture_tag, 'source' );
    467549                if ( $this->is_iterable( $sources ) ) {
    468550                    foreach ( $sources as $source ) {
     
    477559                            $this->set_attribute( $lazy_source, 'data-srcset', $srcset );
    478560                            $this->remove_attribute( $lazy_source, 'srcset' );
    479                             $picture = \str_replace( $source, $lazy_source, $picture );
     561                            $picture_tag = \str_replace( $source, $lazy_source, $picture_tag );
    480562                        }
    481563                    }
    482                     $position = \strpos( $buffer, $pictures[ $index ] );
    483                     if ( $position && $picture !== $pictures[ $index ] ) {
     564                    if ( $position && $picture_tag !== $pictures[ $index ][0] ) {
    484565                        $this->debug_message( 'lazified sources for picture element' );
    485566                        $replacements[ $position ] = array(
    486                             'orig' => $pictures[ $index ],
    487                             'lazy' => $picture,
     567                            'orig' => $pictures[ $index ][0],
     568                            'lazy' => $picture_tag,
    488569                        );
    489                         /* $buffer = str_replace( $pictures[ $index ], $picture, $buffer ); */
    490                     }
    491                 }
    492             }
    493         }
     570                    }
     571                }
     572            }
     573        }
     574
    494575        // Iframe elements, looking for stuff like YouTube embeds.
    495576        if ( \in_array( 'iframe', $this->user_element_exclusions, true ) ) {
    496577            $frames = '';
    497578        } else {
    498             $frames = $this->get_elements_from_html( $search_buffer, 'iframe' );
     579            $frames = $this->get_elements_from_html( $buffer, 'iframe', PREG_OFFSET_CAPTURE );
    499580        }
    500581        if ( $this->is_iterable( $frames ) ) {
    501             foreach ( $frames as $index => $frame ) {
     582            foreach ( $frames as $index => $frame_data ) {
     583                if ( empty( $frame_data[0] ) || empty( $frame_data[1] ) ) {
     584                    $this->debug_message( 'missing iframe tag or position' );
     585                    continue;
     586                }
    502587                $this->debug_message( 'parsing an iframe element' );
     588                $frame    = $frame_data[0];
     589                $position = $frame_data[1];
     590                if ( $this->is_in_forbidden_block( $frame, $position ) ) {
     591                    continue;
     592                }
    503593                $url = $this->get_attribute( $frame, 'src' );
    504594                if ( $url && 0 === \strpos( $url, 'http' ) && $this->validate_iframe_tag( $frame ) ) {
     
    507597                    $this->remove_attribute( $frame, 'src' );
    508598                    $this->set_attribute( $frame, 'class', \trim( $this->get_attribute( $frame, 'class' ) . ' lazyload' ), true );
    509                     if ( $frame !== $frames[ $index ] ) {
    510                         $buffer = \str_replace( $frames[ $index ], $frame, $buffer );
    511                     }
    512                 }
    513             }
    514         }
     599                    if ( $frame !== $frames[ $index ][0] ) {
     600                        $buffer = \str_replace( $frames[ $index ][0], $frame, $buffer );
     601                    }
     602                }
     603            }
     604        }
     605
    515606        if ( $this->is_iterable( $replacements ) ) {
    516607            \ksort( $replacements );
     
    534625            }
    535626        }
    536         $this->debug_message( 'all done parsing page for lazy' );
     627
     628        $elapsed_time = \microtime( true ) - $started_time;
     629        $this->debug_message( "all done parsing page for lazy in $elapsed_time seconds" );
    537630        return $buffer;
    538631    }
     
    796889            return $replacements;
    797890        }
    798         $elements = $this->get_elements_from_html( \preg_replace( '/<(noscript|script).*?\/\1>/s', '', $buffer ), $tag_type );
     891        $elements = $this->get_elements_from_html( $buffer, $tag_type, PREG_OFFSET_CAPTURE );
    799892        if ( $this->is_iterable( $elements ) ) {
    800             foreach ( $elements as $index => $element ) {
     893            foreach ( $elements as $index => $element_data ) {
     894                if ( empty( $element_data[0] ) || empty( $element_data[1] ) ) {
     895                    $this->debug_message( 'missing element or position' );
     896                    continue;
     897                }
     898                $element  = $element_data[0];
     899                $position = $element_data[1];
     900                if ( $this->is_in_forbidden_block( $element, $position ) ) {
     901                    continue;
     902                }
    801903                $this->debug_message( "parsing a $tag_type" );
    802                 if ( false === \strpos( $element, 'background:' ) && false === \strpos( $element, 'background-image:' ) ) {
     904                if ( ! \str_contains( $element, 'background:' ) && ! \str_contains( $element, 'background-image:' ) ) {
    803905                    $element = $this->lazify_element( $element );
    804                     if ( $element !== $elements[ $index ] ) {
     906                    if ( $element !== $elements[ $index ][0] ) {
    805907                        $this->debug_message( "$tag_type lazified, replacing in html source" );
    806                         $buffer = \str_replace( $elements[ $index ], $element, $buffer );
    807                     }
    808                     continue;
    809                 }
    810                 if ( false !== \strpos( $element, '--background' ) ) {
     908                        $buffer = \str_replace( $elements[ $index ][0], $element, $buffer );
     909                    }
     910                    continue;
     911                }
     912                if ( \str_contains( $element, '--background' ) ) {
    811913                    $this->set_attribute( $element, 'class', $this->get_attribute( $element, 'class' ) . ' lazyload', true );
    812                     if ( $element !== $elements[ $index ] ) {
     914                    if ( $element !== $elements[ $index ][0] ) {
    813915                        $this->debug_message( "$tag_type with bg var lazified, replacing in html source" );
    814                         $buffer = \str_replace( $elements[ $index ], $element, $buffer );
     916                        $buffer = \str_replace( $elements[ $index ][0], $element, $buffer );
    815917                    }
    816918                    continue;
     
    853955                            }
    854956                        } elseif ( ! empty( $bg_image_urls[0] ) ) {
     957                            list( $physical_width, $physical_height ) = $this->get_image_dimensions_by_url( $bg_image_urls[0] );
     958                            $this->set_attribute( $element, 'data-back', $bg_image_urls[0] );
     959                            if ( $physical_width && $physical_height ) {
     960                                $this->set_attribute( $element, 'data-eio-rwidth', $physical_width, true );
     961                                $this->set_attribute( $element, 'data-eio-rheight', $physical_height, true );
     962                            }
    855963                            $webp_image_url = \apply_filters( 'eio_image_url_to_webp', $bg_image_urls[0] );
    856                             $this->set_attribute( $element, 'data-back', $bg_image_urls[0] );
    857964                            if ( $webp_image_url && $webp_image_url !== $bg_image_urls[0] ) {
    858965                                $this->set_attribute( $element, 'data-back-webp', $webp_image_url );
     
    862969                    }
    863970                }
    864                 $position = \strpos( $buffer, $elements[ $index ] );
    865                 if ( $position && $element !== $elements[ $index ] ) {
     971                if ( $position && $element !== $elements[ $index ][0] ) {
    866972                    $this->debug_message( "$tag_type modified, replacing in html source" );
    867973                    $replacements[ $position ] = array(
    868                         'orig' => $elements[ $index ],
     974                        'orig' => $elements[ $index ][0],
    869975                        'lazy' => $element,
    870976                    );
    871                     /* $buffer = str_replace( $elements[ $index ], $element, $buffer ); */
    872977                }
    873978            }
     
    10181123
    10191124    /**
     1125     * Normalize HTML for comparison.
     1126     *
     1127     * @param string $html The HTML to normalize.
     1128     * @return string The normalized HTML.
     1129     */
     1130    public function normalize_html( $html ) {
     1131        $html = str_replace( '&amp;', '&', $html );
     1132        $html = str_replace( '&#038;', '&', $html );
     1133        $html = str_replace( '%2C', ',', $html );
     1134        $html = str_replace( ' />', '>', $html );
     1135        $html = str_replace( "'", '"', $html );
     1136        $html = preg_replace( '/\s\s+/', ' ', $html );
     1137        $html = preg_replace( '/\s*=\s*/', '=', $html );
     1138        return $html;
     1139    }
     1140
     1141    /**
    10201142     * Checks if the tag is allowed to be lazy loaded.
    10211143     *
     
    10561178            }
    10571179        }
    1058 
     1180        // Check for exclusions.
    10591181        $exclusions = \apply_filters(
    10601182            'eio_lazy_exclusions',
     
    10891211                ),
    10901212                $this->user_exclusions
    1091             ),
    1092             $image
     1213            )
    10931214        );
    10941215        foreach ( $exclusions as $exclusion ) {
     
    11061227            }
    11071228        }
     1229
     1230        foreach ( $this->img_nodes as $img_node ) {
     1231            $img_html = $this->doc->saveHTML( $img_node );
     1232            if ( defined( 'EIO_IMGNODE_DEBUG' ) && EIO_IMGNODE_DEBUG ) {
     1233                $this->debug_message( 'comparing to node value: ' . $this->normalize_html( $img_html ) . ' to ' . $this->normalize_html( $image ) );
     1234            }
     1235            // Normalize the HTML before comparing to avoid issues with different quote styles or spacing.
     1236            if ( $this->normalize_html( $img_html ) === $this->normalize_html( $image ) ) {
     1237                $parent = $img_node->parentNode;
     1238                if ( $parent && 'body' !== $parent->nodeName && $parent->hasAttributes() ) {
     1239                    $class = trim( $parent->getAttribute( 'class' ) );
     1240                    $this->debug_message( "Parent class: $class" );
     1241                    if ( str_contains( $class, 'skip-lazy' ) ) {
     1242                        $this->debug_message( "Skipping lazy load due to 'skip-lazy' class on parent" );
     1243                        return false;
     1244                    }
     1245                    if ( $parent->hasAttribute( 'data-skip-lazy' ) ) {
     1246                        $this->debug_message( "Skipping lazy load due to 'data-skip-lazy' attribute on parent" );
     1247                        return false;
     1248                    }
     1249                }
     1250            }
     1251        }
     1252
    11081253        return true;
    11091254    }
    11101255
    11111256    /**
    1112      * Checks if a tag with a background image is allowed to be lazy loaded.
    1113      *
    1114      * @param string $tag The tag.
    1115      * @return bool True if the tag is allowed, false otherwise.
    1116      */
    1117     public function validate_bgimage_tag( $tag ) {
    1118         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     1257     * Gets the exclusion list for lazy loading background images.
     1258     *
     1259     * @param array $exclusions The current list of exclusions. Optional.
     1260     * @return array The modified list of exclusions.
     1261     */
     1262    public function get_bgimage_exclusions( $exclusions = array() ) {
     1263        if ( ! \is_array( $exclusions ) ) {
     1264            $exclusions = array();
     1265        }
    11191266        $exclusions = \apply_filters(
    11201267            'eio_lazy_bg_image_exclusions',
     
    11271274                    'skip-lazy',
    11281275                    'avia-bg-style-fixed',
     1276                    'trustindex',
     1277                ),
     1278                $this->user_exclusions,
     1279                $exclusions
     1280            )
     1281        );
     1282        return $exclusions;
     1283    }
     1284
     1285    /**
     1286     * Checks if a tag with a background image is allowed to be lazy loaded.
     1287     *
     1288     * @param string $tag The tag.
     1289     * @return bool True if the tag is allowed, false otherwise.
     1290     */
     1291    public function validate_bgimage_tag( $tag ) {
     1292        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     1293        $exclusions = $this->get_bgimage_exclusions( array() );
     1294        foreach ( $exclusions as $exclusion ) {
     1295            if ( false !== \strpos( $tag, $exclusion ) ) {
     1296                return false;
     1297            }
     1298        }
     1299        return true;
     1300    }
     1301
     1302    /**
     1303     * Checks if an iframe tag is allowed to be lazy loaded.
     1304     *
     1305     * @param string $tag The tag.
     1306     * @return bool True if the tag is allowed, false otherwise.
     1307     */
     1308    public function validate_iframe_tag( $tag ) {
     1309        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     1310        $exclusions = \apply_filters(
     1311            'eio_lazy_iframe_exclusions',
     1312            \array_merge(
     1313                array(
     1314                    'about:blank',
     1315                    'data-no-lazy=',
     1316                    'display:none',
     1317                    'display: none',
     1318                    'googletagmanager',
     1319                    'lazyload',
     1320                    'skip-lazy',
     1321                    'wprus/',
     1322                    'vimeo',
    11291323                ),
    11301324                $this->user_exclusions
     
    11411335
    11421336    /**
    1143      * Checks if an iframe tag is allowed to be lazy loaded.
    1144      *
    1145      * @param string $tag The tag.
    1146      * @return bool True if the tag is allowed, false otherwise.
    1147      */
    1148     public function validate_iframe_tag( $tag ) {
    1149         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    1150         $exclusions = \apply_filters(
    1151             'eio_lazy_iframe_exclusions',
    1152             \array_merge(
    1153                 array(
    1154                     'data-no-lazy=',
    1155                     'lazyload',
    1156                     'skip-lazy',
    1157                     'vimeo',
    1158                     'about:blank',
    1159                     'googletagmanager',
    1160                 ),
    1161                 $this->user_exclusions
    1162             ),
    1163             $tag
    1164         );
    1165         foreach ( $exclusions as $exclusion ) {
    1166             if ( false !== \strpos( $tag, $exclusion ) ) {
    1167                 return false;
    1168             }
    1169         }
    1170         return true;
    1171     }
    1172 
    1173     /**
    11741337     * Build a PNG inline image placeholder.
    11751338     *
     
    12111374            return $exactdn->generate_url( $this->content_url . 'lazy/placeholder-' . $width . 'x' . $height . '.png' );
    12121375        } elseif ( ! is_file( $piip_path ) ) {
    1213             // First try PIP generation via Imagick, as it is pretty efficient.
    1214             if ( $this->imagick_support() ) {
    1215                 $placeholder = new \Imagick();
    1216                 $placeholder->newimage( $width, $height, 'transparent' );
    1217                 $placeholder->setimageformat( 'PNG' );
    1218                 $placeholder->stripimage();
    1219                 $placeholder->writeimage( $piip_path );
    1220                 $placeholder->clear();
     1376            // First, try GD and then optimize it with optipng/pngout if available.
     1377            // This is now the first attempt, as the Imagick placeholders are grayscale and may not work in Safari, and GD is somehow faster.
     1378            if (
     1379                $this->gd_support() &&
     1380                $this->check_memory_available( $width * $height * 4.8 ) // 4.8 = 24-bit or 3 bytes per pixel multiplied by a factor of 1.6 for extra wiggle room.
     1381            ) {
     1382                $img   = \imagecreatetruecolor( $width, $height );
     1383                $color = \imagecolorallocatealpha( $img, 0, 0, 0, 127 );
     1384                \imagefill( $img, 0, 0, $color );
     1385                \imagesavealpha( $img, true );
     1386                \imagecolortransparent( $img, \imagecolorat( $img, 0, 0 ) );
     1387                \imagetruecolortopalette( $img, false, 1 );
     1388                \imagepng( $img, $piip_path, 9 );
     1389                if (
     1390                    \function_exists( '\ewww_image_optimizer' ) &&
     1391                    \function_exists( '\ewwwio' ) &&
     1392                    ! \ewwwio()->get_option( 'ewww_image_optimizer_cloud_key' ) &&
     1393                    \ewwwio()->local->exec_check()
     1394                ) {
     1395                    \ewww_image_optimizer( $piip_path );
     1396                }
    12211397            }
    12221398            // If that didn't work, and we have a premium service, use the API to generate the slimmest PIP available.
     
    12341410                }
    12351411            }
    1236             // Last shot, use GD and then optimize it with optipng/pngout if available.
     1412            // Last resort, do PIP generation via Imagick, as it is pretty efficient even though Safari doesn't like the grayscale images it produces.
    12371413            if (
    12381414                ! \is_file( $piip_path ) &&
    1239                 $this->gd_support() &&
    1240                 $this->check_memory_available( $width * $height * 4.8 ) // 4.8 = 24-bit or 3 bytes per pixel multiplied by a factor of 1.6 for extra wiggle room.
     1415                $this->imagick_support()
    12411416            ) {
    1242                 $img   = \imagecreatetruecolor( $width, $height );
    1243                 $color = \imagecolorallocatealpha( $img, 0, 0, 0, 127 );
    1244                 \imagefill( $img, 0, 0, $color );
    1245                 \imagesavealpha( $img, true );
    1246                 \imagecolortransparent( $img, \imagecolorat( $img, 0, 0 ) );
    1247                 \imagetruecolortopalette( $img, false, 1 );
    1248                 \imagepng( $img, $piip_path, 9 );
    1249                 if ( \function_exists( '\ewww_image_optimizer' ) ) {
    1250                     \ewww_image_optimizer( $piip_path );
    1251                 }
     1417                $placeholder = new \Imagick();
     1418                $placeholder->newimage( $width, $height, 'transparent' );
     1419                $placeholder->setimageformat( 'PNG' );
     1420                $placeholder->stripimage();
     1421                $placeholder->writeimage( $piip_path );
     1422                $placeholder->clear();
    12521423            }
    12531424        }
     
    13821553                        'exactdn_domain' => ( $this->parsing_exactdn ? $this->exactdn_domain : '' ),
    13831554                        'skip_autoscale' => ( \defined( 'EIO_LL_AUTOSCALE' ) && ! EIO_LL_AUTOSCALE ? 1 : 0 ),
     1555                        'bg_min_dpr'     => ( \defined( 'EIO_LL_BG_MIN_DPR' ) && EIO_LL_BG_MIN_DPR ? EIO_LL_BG_MIN_DPR : 1.1 ),
    13841556                        'threshold'      => (int) $threshold > 50 ? (int) $threshold : 0,
    13851557                        'use_dpr'        => (int) $this->get_option( 'exactdn_hidpi' ),
     
    14231595                        'exactdn_domain' => ( $this->parsing_exactdn ? $this->exactdn_domain : '' ),
    14241596                        'skip_autoscale' => ( \defined( 'EIO_LL_AUTOSCALE' ) && ! EIO_LL_AUTOSCALE ? 1 : 0 ),
     1597                        'bg_min_dpr'     => ( \defined( 'EIO_LL_BG_MIN_DPR' ) && EIO_LL_BG_MIN_DPR ? EIO_LL_BG_MIN_DPR : 1.1 ),
    14251598                        'threshold'      => (int) $threshold > 50 ? (int) $threshold : 0,
    14261599                        'use_dpr'        => (int) $this->get_option( 'exactdn_hidpi' ),
  • easy-image-optimizer/tags/4.3.0/classes/class-page-parser.php

    r3328397 r3398277  
    5757     * @param bool   $hyperlinks Default true. Should we include encasing hyperlinks in our search.
    5858     * @param bool   $src_required Default true. Should we look only for images with src attributes.
     59     * @param int    $flags Optional. Flags passed to preg_match_all. Default 0.
    5960     * @return array An array of $images matches, where $images[0] is
    6061     *         an array of full matches, and the link_url, img_tag,
    6162     *         and img_url keys are arrays of those matches.
    6263     */
    63     public function get_images_from_html( $content, $hyperlinks = true, $src_required = true ) {
     64    public function get_images_from_html( $content, $hyperlinks = true, $src_required = true, $flags = 0 ) {
    6465        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    6566        $images          = array();
     
    8081            $unquoted_pattern = '#(?P<img_tag><img[^>]*?\s+?src\s*=\s*(?P<img_url>[^"\'\\\\<>][^\s\\\\<>]+)(?:\s[^>]*?)?>)#is';
    8182        }
    82         if ( \preg_match_all( $search_pattern, $content, $images ) ) {
     83        if ( \preg_match_all( $search_pattern, $content, $images, $flags ) ) {
    8384            $this->debug_message( 'found ' . \count( $images[0] ) . ' image elements with quoted pattern' );
    8485            foreach ( $images as $key => $unused ) {
     
    9192        }
    9293        $images = \array_filter( $images );
    93         if ( $unquoted_pattern && \preg_match_all( $unquoted_pattern, $content, $unquoted_images ) ) {
     94        if ( $unquoted_pattern && \preg_match_all( $unquoted_pattern, $content, $unquoted_images, $flags ) ) {
    9495            $this->debug_message( 'found ' . \count( $unquoted_images[0] ) . ' image elements with unquoted pattern' );
    9596            foreach ( $unquoted_images as $key => $unused ) {
     
    147148     *
    148149     * @param string $content Some HTML.
     150     * @param int    $flags Optional. Flags passed to preg_match_all. Default 0.
    149151     * @return array An array of $pictures matches, containing full elements with ending tags.
    150152     */
    151     public function get_picture_tags_from_html( $content ) {
     153    public function get_picture_tags_from_html( $content, $flags = 0 ) {
    152154        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    153155        $pictures = array();
    154         if ( ! empty( $content ) && \preg_match_all( '#(?:<picture[^>]*?>\s*)(?:<source[^>]*?>)+(?:.*?</picture>)?#is', $content, $pictures ) ) {
     156        if ( ! empty( $content ) && \preg_match_all( '#(?:<picture[^>]*?>\s*)(?:<source[^>]*?>)+(?:.*?</picture>)?#is', $content, $pictures, $flags ) ) {
    155157            return $pictures[0];
    156158        }
     
    212214     * @param string $content Some HTML.
    213215     * @param string $tag_name The name of the elements to retrieve.
     216     * @param int    $flags Optional. Flags passed to preg_match_all. Default 0.
    214217     * @return array An array of $elements.
    215218     */
    216     public function get_elements_from_html( $content, $tag_name ) {
    217         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    218         if ( ! \ctype_alpha( str_replace( '-', '', $tag_name ) ) ) {
     219    public function get_elements_from_html( $content, $tag_name, $flags = 0 ) {
     220        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     221        if ( ! \ctype_alpha( \str_replace( '-', '', $tag_name ) ) ) {
    219222            return array();
    220223        }
    221         if ( ! empty( $content ) && \preg_match_all( '#<' . $tag_name . '\s[^\\\\>]+?>#is', $content, $elements ) ) {
     224        if ( ! empty( $content ) && \preg_match_all( '#<' . $tag_name . '\s[^\\\\>]+?>#is', $content, $elements, $flags ) ) {
    222225            return $elements[0];
    223226        }
  • easy-image-optimizer/tags/4.3.0/classes/class-plugin.php

    r3197623 r3398277  
    2828
    2929    /**
     30     * Buffer object.
     31     *
     32     * @var object|\EasyIO\Buffer $buffer
     33     */
     34    public $buffer;
     35
     36    /**
     37     * Lazy Load object.
     38     *
     39     * @var object|\EasyIO\Lazy_Load $lazy_load
     40     */
     41    public $lazy_load;
     42
     43    /**
    3044     * Helpscout Beacon object.
    3145     *
     
    3347     */
    3448    public $hs_beacon;
     49
     50    /**
     51     * Settings object.
     52     *
     53     * @var object|\EasyIO\Settings $settings
     54     */
     55    public $settings;
    3556
    3657    /**
     
    5071            self::$instance->requires();
    5172            self::$instance->load_children();
     73
     74            if ( ! self::$instance->php_supported() ) {
     75                return self::$instance;
     76            }
     77            if ( ! self::$instance->wp_supported() ) {
     78                return self::$instance;
     79            }
     80
     81            // Load plugin components that need to be available early.
     82            \add_action( 'plugins_loaded', array( self::$instance, 'plugins_loaded' ) );
     83            // Setup page parsing, if parsers are enabled.
     84            \add_action( 'init', array( self::$instance, 'parser_init' ), 99 );
    5285            // Initializes the plugin for admin interactions, like saving network settings and scheduling cron jobs.
    5386            \add_action( 'admin_init', array( self::$instance, 'admin_init' ) );
    5487
    55             // TODO: check PHP and WP compat here.
    56             // TODO: setup anything that needs to run on init/plugins_loaded.
    57             // TODO: add any custom option/setting hooks here (actions that need to be taken when certain settings are saved/updated).
     88            // Filters to set default permissions, admins can override these if they wish.
     89            \add_filter( 'easyio_admin_permissions', array( self::$instance, 'admin_permissions' ), 8 );
     90            \add_filter( 'easyio_superadmin_permissions', array( self::$instance, 'superadmin_permissions' ), 8 );
     91
     92            // Add Easy IO version to useragent for API requests.
     93            \add_filter( 'exactdn_api_request_useragent', array( self::$instance, 'api_useragent' ) );
     94            // Check the current screen ID to see if temp debugging should still be enabled.
     95            \add_action( 'current_screen', array( self::$instance, 'current_screen' ), 10, 1 );
     96            // Disable core WebP generation since we already do that.
     97            \add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
     98            // Makes sure we flush the debug info to the log on shutdown.
     99            \add_action( 'shutdown', array( self::$instance, 'debug_log' ) );
    58100        }
    59101
     
    86128     */
    87129    private function requires() {
     130        // Sets up the settings page and various option-related hooks/functions.
     131        require_once EASYIO_PLUGIN_PATH . 'classes/class-settings.php';
     132        // Starts the HTML buffer for all other functions to parse.
     133        require_once EASYIO_PLUGIN_PATH . 'classes/class-buffer.php';
     134        // Page Parsing class for working with HTML content.
     135        require_once EASYIO_PLUGIN_PATH . 'classes/class-page-parser.php';
     136        // Lazy Load class for parsing image urls and deferring off-screen images.
     137        require_once EASYIO_PLUGIN_PATH . 'classes/class-lazy-load.php';
    88138        // EasyIO\HS_Beacon class for integrated help/docs.
    89139        require_once EASYIO_PLUGIN_PATH . 'classes/class-hs-beacon.php';
     
    94144     */
    95145    public function load_children() {
    96         /* self::$instance->class = new Class(); */
     146        self::$instance->settings = new Settings();
     147    }
     148
     149    /**
     150     * Make sure we are on a supported version of PHP.
     151     *
     152     * @access private
     153     */
     154    private function php_supported() {
     155        if ( defined( 'PHP_VERSION_ID' ) && PHP_VERSION_ID >= 80100 ) {
     156            return true;
     157        }
     158        \add_action( 'network_admin_notices', array( self::$instance, 'unsupported_php_notice' ) );
     159        \add_action( 'admin_notices', array( self::$instance, 'unsupported_php_notice' ) );
     160        return false;
     161    }
     162
     163    /**
     164     * Make sure we are on a supported version of WordPress.
     165     *
     166     * @access private
     167     */
     168    private function wp_supported() {
     169        global $wp_version;
     170        if ( \version_compare( $wp_version, '6.6' ) >= 0 ) {
     171            return true;
     172        }
     173        \add_action( 'network_admin_notices', array( self::$instance, 'unsupported_wp_notice' ) );
     174        \add_action( 'admin_notices', array( self::$instance, 'unsupported_wp_notice' ) );
     175        return false;
     176    }
     177
     178    /**
     179     * Display a notice that the PHP version is too old.
     180     */
     181    public function unsupported_php_notice() {
     182        echo '<div id="easyio-warning-php" class="error"><p><a href="https://docs.ewww.io/article/55-upgrading-php" target="_blank" data-beacon-article="5ab2baa6042863478ea7c2ae">' . esc_html__( 'Easy Image Optimizer requires PHP 8.1 or greater. Newer versions of PHP are faster and more secure. If you are unsure how to upgrade to a supported version, ask your webhost for instructions.', 'easy-image-optimizer' ) . '</a></p></div>';
     183    }
     184
     185    /**
     186     * Display a notice that the WP version is too old.
     187     */
     188    public function unsupported_wp_notice() {
     189        echo '<div id="swis-warning-wp" class="notice notice-error"><p>' . esc_html__( 'Easy Image Optimizer requires WordPress 6.6 or greater, please update your website.', 'easy-image-optimizer' ) . '</p></div>';
     190    }
     191
     192    /**
     193     * Run things that need to go early, on plugins_loaded.
     194     */
     195    public function plugins_loaded() {
     196        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     197
     198        if ( $this->get_option( 'easyio_lazy_load' ) && $this->get_option( 'easyio_ll_external_bg' ) ) {
     199            $this->debug_message( 'requesting external parsing of CSS for background images via SWIS' );
     200            add_filter( 'eio_lazify_external_css', '__return_true' );
     201        }
     202    }
     203
     204    /**
     205     * Setup page parsing classes after theme functions.php is loaded and plugins have run init routines.
     206     */
     207    public function parser_init() {
     208        $buffer_start = false;
     209        // If ExactDN is enabled.
     210        if ( $this->get_option( 'easyio_exactdn' ) && ! \str_contains( \add_query_arg( '', '' ), 'exactdn_disable=1' ) ) {
     211            $buffer_start = true;
     212
     213            // ExactDN class for parsing image urls and rewriting them.
     214            require_once EASYIO_PLUGIN_PATH . 'classes/class-exactdn.php';
     215        }
     216        // If Lazy Load is enabled.
     217        if ( $this->get_option( 'easyio_lazy_load' ) ) {
     218            $buffer_start = true;
     219
     220            $this->lazy_load = new Lazy_Load();
     221        }
     222        if ( $buffer_start ) {
     223            // Start an output buffer before any output starts.
     224            $this->buffer = new Buffer();
     225        }
    97226    }
    98227
     
    102231    public function admin_init() {
    103232        $this->hs_beacon = new HS_Beacon();
    104         \easyio_upgrade();
    105         $this->register_settings();
    106233
    107234        if ( ! \class_exists( __NAMESPACE__ . '\ExactDN' ) || ! $this->get_option( 'easyio_exactdn' ) ) {
    108             add_action( 'network_admin_notices', 'easyio_notice_inactive' );
    109             add_action( 'admin_notices', 'easyio_notice_inactive' );
    110         }
     235            \add_action( 'network_admin_notices', array( $this, 'service_inactive_notice' ) );
     236            \add_action( 'admin_notices', array( $this, 'service_inactive_notice' ) );
     237        }
     238
     239        \add_action( 'exactdn_as3cf_cname_active', array( $this, 'exactdn_as3cf_cname_active_notice' ) );
     240        \add_action( 'exactdn_domain_mismatch', array( $this, 'exactdn_domain_mismatch_notice' ) );
     241
     242        \add_action( 'easyio_beacon_notice', array( $this, 'hs_beacon_notice' ) );
     243
    111244        // Prevent ShortPixel AIO messiness.
    112245        \remove_action( 'admin_notices', 'autoptimizeMain::notice_plug_imgopt' );
     
    117250                $ao_extra['autoptimize_imgopt_checkbox_field_1'] = 0;
    118251                \update_option( 'autoptimize_imgopt_settings', $ao_extra );
    119                 \add_action( 'admin_notices', 'easyio_notice_sp_conflict' );
     252                \add_action( 'admin_notices', array( $this, 'sp_conflict_notice' ) );
    120253            }
    121254        }
     
    124257            if ( $this->get_option( 'easyio_exactdn' ) && \HMWP_Classes_Tools::getOption( 'hmwp_hide_version' ) && ! \HMWP_Classes_Tools::getOption( 'hmwp_hide_version_random' ) ) {
    125258                $this->debug_message( 'detected HMWP Hide Version' );
    126                 \add_action( 'admin_notices', array( $this, 'notice_hmwp_hide_version' ) );
     259                \add_action( 'admin_notices', array( $this, 'hmwp_hide_version_notice' ) );
    127260            }
    128261        }
    129262
    130         if ( ! \defined( '\WP_CLI' ) || ! WP_CLI ) {
    131             \easyio_privacy_policy_content();
    132         }
    133     }
    134 
    135     /**
    136      * Save the multi-site settings, if this is the WP admin, and they've been POSTed.
    137      */
    138     public function save_network_settings() {
    139         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    140         // NOTE: we don't actually have a network settings screen, so...
    141         if ( ! \function_exists( 'is_plugin_active_for_network' ) && \is_multisite() ) {
    142             // Need to include the plugin library for the is_plugin_active function.
    143             require_once ABSPATH . 'wp-admin/includes/plugin.php';
    144         }
    145             // Set the common network settings if they have been POSTed.
    146         if (
    147             \is_multisite() &&
    148             \is_plugin_active_for_network( EASYIO_PLUGIN_FILE_REL ) &&
    149             ! empty( $_REQUEST['_wpnonce'] ) &&
    150             isset( $_POST['option_page'] ) &&
    151             false !== \strpos( sanitize_text_field( wp_unslash( $_POST['option_page'] ) ), 'easyio_options' ) &&
    152             \wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'easyio_options-options' ) &&
    153             \current_user_can( 'manage_network_options' ) &&
    154             ! \get_site_option( 'easyio_allow_multisite_override' ) &&
    155             false === \strpos( wp_get_referer(), 'options-general' )
    156         ) {
    157             $this->debug_message( 'network-wide settings, no override' );
    158             $easyio_debug = ( empty( $_POST['easyio_debug'] ) ? false : true );
    159             \update_site_option( 'easyio_debug', $easyio_debug );
    160             $easyio_metadata_remove = ( empty( $_POST['easyio_metadata_remove'] ) ? false : true );
    161             \update_site_option( 'easyio_metadata_remove', $easyio_metadata_remove );
    162             $exactdn_all_the_things = ( empty( $_POST['exactdn_all_the_things'] ) ? false : true );
    163             \update_site_option( 'exactdn_all_the_things', $exactdn_all_the_things );
    164             $exactdn_lossy = ( empty( $_POST['exactdn_lossy'] ) ? false : true );
    165             \update_site_option( 'exactdn_lossy', $exactdn_lossy );
    166             $exactdn_hidpi = ( empty( $_POST['exactdn_hidpi'] ) ? false : true );
    167             \update_site_option( 'exactdn_hidpi', $exactdn_hidpi );
    168             $exactdn_exclude = empty( $_POST['exactdn_exclude'] ) ? '' : sanitize_textarea_field( wp_unslash( $_POST['exactdn_exclude'] ) );
    169             \update_site_option( 'exactdn_exclude', $this->exclude_paths_sanitize( $exactdn_exclude ) );
    170             $easyio_add_missing_dims = ( empty( $_POST['easyio_add_missing_dims'] ) ? false : true );
    171             \update_site_option( 'easyio_add_missing_dims', $easyio_add_missing_dims );
    172             $easyio_lazy_load = ( empty( $_POST['easyio_lazy_load'] ) ? false : true );
    173             \update_site_option( 'easyio_lazy_load', $easyio_lazy_load );
    174             $easyio_ll_autoscale = ( empty( $_POST['easyio_ll_autoscale'] ) ? false : true );
    175             \update_site_option( 'easyio_ll_autoscale', $easyio_ll_autoscale );
    176             $easyio_ll_abovethefold = ! empty( $_POST['easyio_ll_abovethefold'] ) ? (int) $_POST['easyio_ll_abovethefold'] : 0;
    177             \update_site_option( 'easyio_ll_abovethefold', $easyio_ll_abovethefold );
    178             $easyio_use_lqip = ( empty( $_POST['easyio_use_lqip'] ) ? false : true );
    179             \update_site_option( 'easyio_use_lqip', $easyio_use_lqip );
    180             $easyio_use_dcip = ( empty( $_POST['easyio_use_dcip'] ) ? false : true );
    181             \update_site_option( 'easyio_use_dcip', $easyio_use_dcip );
    182             $easyio_ll_exclude = empty( $_POST['easyio_ll_exclude'] ) ? '' : sanitize_textarea_field( wp_unslash( $_POST['easyio_ll_exclude'] ) );
    183             \update_site_option( 'easyio_ll_exclude', $this->exclude_paths_sanitize( $easyio_ll_exclude ) );
    184             $easyio_ll_all_things = empty( $_POST['easyio_ll_all_things'] ) ? '' : sanitize_textarea_field( wp_unslash( $_POST['easyio_ll_all_things'] ) );
    185             \update_site_option( 'easyio_ll_all_things', $easyio_ll_all_things );
    186             $easyio_allow_multisite_override = empty( $_POST['easyio_allow_multisite_override'] ) ? false : true;
    187             \update_site_option( 'easyio_allow_multisite_override', $easyio_allow_multisite_override );
    188             $easyio_enable_help = empty( $_POST['easyio_enable_help'] ) ? false : true;
    189             \update_site_option( 'easyio_enable_help', $easyio_enable_help );
    190             \add_action( 'network_admin_notices', 'easyio_network_settings_saved' );
    191         } elseif ( isset( $_POST['easyio_allow_multisite_override_active'] ) && \current_user_can( 'manage_network_options' ) && ! empty( $_REQUEST['_wpnonce'] ) && \wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'easyio_options-options' ) ) {
    192             $this->debug_message( 'network-wide settings, single-site overriding' );
    193             $easyio_allow_multisite_override = empty( $_POST['easyio_allow_multisite_override'] ) ? false : true;
    194             \update_site_option( 'easyio_allow_multisite_override', $easyio_allow_multisite_override );
    195             \add_action( 'network_admin_notices', 'easyio_network_settings_saved' );
    196         } // End if().
    197     }
    198 
    199     /**
    200      * Register all our options and sanitation functions.
    201      */
    202     public function register_settings() {
    203         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    204         // Register all the common Easy IO settings.
    205         \register_setting( 'easyio_options', 'easyio_debug', 'boolval' );
    206         \register_setting( 'easyio_options', 'easyio_enable_help', 'boolval' );
    207         \register_setting( 'easyio_options', 'exactdn_all_the_things', 'boolval' );
    208         \register_setting( 'easyio_options', 'exactdn_lossy', 'boolval' );
    209         \register_setting( 'easyio_options', 'exactdn_hidpi', 'boolval' );
    210         \register_setting( 'easyio_options', 'exactdn_exclude', array( $this, 'exclude_paths_sanitize' ) );
    211         \register_setting( 'easyio_options', 'easyio_add_missing_dims', 'boolval' );
    212         \register_setting( 'easyio_options', 'easyio_lazy_load', 'boolval' );
    213         \register_setting( 'easyio_options', 'easyio_ll_abovethefold', 'intval' );
    214         \register_setting( 'easyio_options', 'easyio_use_lqip', 'boolval' );
    215         \register_setting( 'easyio_options', 'easyio_use_dcip', 'boolval' );
    216         \register_setting( 'easyio_options', 'easyio_ll_exclude', array( $this, 'exclude_paths_sanitize' ) );
    217         \register_setting( 'easyio_options', 'easyio_ll_all_things', 'sanitize_textarea_field' );
    218     }
    219 
    220     /**
    221      * Set some default option values.
    222      */
    223     public function set_defaults() {
    224         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    225         // Set defaults for all options that need to be autoloaded.
    226         \add_option( 'easyio_debug', false );
    227         \add_option( 'easyio_metadata_remove', true );
    228         \add_option( 'easyio_exactdn', false );
    229         \add_option( 'easyio_plan_id', 0 );
    230         \add_option( 'exactdn_all_the_things', false );
    231         \add_option( 'exactdn_lossy', false );
    232         \add_option( 'exactdn_hidpi', false );
    233         \add_option( 'exactdn_exclude', '' );
    234         \add_option( 'exactdn_sub_folder', false );
    235         \add_option( 'exactdn_prevent_db_queries', true );
    236         \add_option( 'exactdn_asset_domains', '' );
    237         \add_option( 'easyio_add_missing_dims', false );
    238         \add_option( 'easyio_lazy_load', false );
    239         \add_option( 'easyio_use_lqip', false );
    240         \add_option( 'easyio_use_dcip', false );
    241         \add_option( 'easyio_use_siip', false );
    242         \add_option( 'easyio_ll_autoscale', true );
    243         \add_option( 'easyio_ll_abovethefold', 0 );
    244         \add_option( 'easyio_ll_exclude', '' );
    245         \add_option( 'easyio_ll_all_things', '' );
    246 
    247         // Set network defaults.
    248         \add_site_option( 'easyio_metadata_remove', true );
    249         \add_site_option( 'easyio_add_missing_dims', true );
    250         \add_site_option( 'easyio_ll_autoscale', true );
    251         \add_site_option( 'exactdn_sub_folder', false );
    252         \add_site_option( 'exactdn_prevent_db_queries', true );
     263        if ( ! \defined( 'WP_CLI' ) || ! WP_CLI ) {
     264            $this->privacy_policy_content();
     265        }
     266    }
     267
     268    /**
     269     * Adds the Easy IO version to the useragent for http requests.
     270     *
     271     * @param string $useragent The current useragent used in http requests.
     272     * @return string The useragent with the Easy IO version appended.
     273     */
     274    public function api_useragent( $useragent ) {
     275        if ( ! \str_contains( $useragent, 'EIO' ) ) {
     276            $useragent .= ' EIO/' . EASYIO_VERSION . ' ';
     277        }
     278        return $useragent;
     279    }
     280
     281    /**
     282     * Adds suggested privacy policy content for site admins.
     283     *
     284     * Note that this is just a suggestion, it should be customized for your site.
     285     */
     286    private function privacy_policy_content() {
     287        if ( ! \function_exists( 'wp_add_privacy_policy_content' ) || ! \function_exists( 'wp_kses_post' ) ) {
     288            return;
     289        }
     290        $content  = '<p class="privacy-policy-tutorial">';
     291        $content .= \wp_kses_post( \__( 'Normally, this plugin does not process any information about your visitors. However, if you accept user-submitted images and display them on your site, you can use this language to keep your visitors informed.', 'easy-image-optimizer' ) ) . '</p>';
     292        $content .= '<p>' . \wp_kses_post( \__( 'User-submitted images that are displayed on this site will be transmitted and stored on a global network of third-party servers (a CDN).', 'easy-image-optimizer' ) ) . '</p>';
     293        \wp_add_privacy_policy_content( 'Easy Image Optimizer', $content );
     294    }
     295
     296    /**
     297     * Set default permissions for admin (configuration) and bulk operations.
     298     *
     299     * @param string $permissions A valid WP capability level.
     300     * @return string Either the original value, unchanged, or the default capability level.
     301     */
     302    public function admin_permissions( $permissions ) {
     303        if ( empty( $permissions ) ) {
     304            return 'activate_plugins';
     305        }
     306        return $permissions;
     307    }
     308
     309    /**
     310     * Set default permissions for multisite/network admin (configuration) operations.
     311     *
     312     * @param string $permissions A valid WP capability level.
     313     * @return string Either the original value, unchanged, or the default capability level.
     314     */
     315    public function superadmin_permissions( $permissions ) {
     316        if ( empty( $permissions ) ) {
     317            return 'manage_network_options';
     318        }
     319        return $permissions;
     320    }
     321
     322    /**
     323     * Check the current screen, used to temporarily enable debugging on settings page.
     324     *
     325     * @param object $screen Information about the page/screen currently being loaded.
     326     */
     327    public function current_screen( $screen ) {
     328        if ( $this->get_option( 'easyio_debug' ) ) {
     329            return;
     330        }
     331        if ( \str_contains( $screen->id, 'settings_page_easy-image-optimizer' ) ) {
     332            return;
     333        }
     334        // Otherwise, we are somewhere else and should disable temp debugging.
     335        Base::$debug_data = '';
     336        Base::$temp_debug = false;
     337    }
     338
     339    /**
     340     * Let the user know they need to take action!
     341     */
     342    public function service_inactive_notice() {
     343        ?>
     344        <div id='easyio-inactive' class='notice notice-warning'>
     345            <p>
     346                <a href="<?php echo \esc_url( \admin_url( 'options-general.php?page=easy-image-optimizer-options' ) ); ?>">
     347                    <?php \esc_html_e( 'Please visit the settings page to complete activation of the Easy Image Optimizer.', 'easy-image-optimizer' ); ?>
     348                </a>
     349            </p>
     350        </div>
     351        <?php
     352    }
     353
     354    /**
     355     * Let the user know they need to disable the WP Offload Media CNAME.
     356     */
     357    public function exactdn_as3cf_cname_active_notice() {
     358        ?>
     359        <div id="easyio-notice-exactdn-as3cf-cname-active" class="notice notice-error">
     360            <p>
     361                <?php \esc_html_e( 'Easy IO cannot optimize your images while using a custom domain (CNAME) in WP Offload Media. Please disable the custom domain in the WP Offload Media settings.', 'easy-image-optimizer' ); ?>
     362            </p>
     363        </div>
     364        <?php
     365    }
     366
     367    /**
     368     * Let the user know the local domain appears to have changed from what Easy IO has recorded in the db.
     369     */
     370    public function exactdn_domain_mismatch_notice() {
     371        global $exactdn;
     372        if ( ! isset( $exactdn->upload_domain ) ) {
     373            return;
     374        }
     375        $stored_local_domain = $this->get_option( 'easyio_exactdn_local_domain' );
     376        if ( empty( $stored_local_domain ) ) {
     377            return;
     378        }
     379        if ( ! \str_contains( $stored_local_domain, '.' ) ) {
     380            $stored_local_domain = \base64_decode( $stored_local_domain );
     381        }
     382        ?>
     383        <div id="easyio-notice-exactdn-domain-mismatch" class="notice notice-warning">
     384            <p>
     385                <?php
     386                \printf(
     387                    /* translators: 1: old domain name, 2: current domain name */
     388                    \esc_html__( 'Easy IO detected that the Site URL has changed since the initial activation (previously %1$s, currently %2$s).', 'easy-image-optimizer' ),
     389                    '<strong>' . \esc_html( $stored_local_domain ) . '</strong>',
     390                    '<strong>' . \esc_html( $exactdn->upload_domain ) . '</strong>'
     391                );
     392                ?>
     393                <br>
     394                <?php
     395                \printf(
     396                    /* translators: %s: settings page */
     397                    \esc_html__( 'Please visit the %s to refresh the Easy IO settings and verify activation status.', 'easy-image-optimizer' ),
     398                    '<a href="' . \esc_url( \admin_url( 'options-general.php?page=easy-image-optimizer-options' ) ) . '">' . \esc_html__( 'settings page', 'easy-image-optimizer' ) . '</a>'
     399                );
     400                ?>
     401            </p>
     402        </div>
     403        <?php
     404    }
     405
     406    /**
     407     * Inform the user of our beacon function so that they can opt-in.
     408     */
     409    public function hs_beacon_notice() {
     410        $optin_url  = \wp_nonce_url( 'admin.php?action=eio_opt_into_hs_beacon', 'eio_beacon' );
     411        $optout_url = \wp_nonce_url( 'admin.php?action=eio_opt_out_of_hs_beacon', 'eio_beacon' );
     412        ?>
     413        <div id="easyio-hs-beacon" class="notice notice-info">
     414            <p>
     415                <?php \esc_html_e( 'Enable the Easy IO support beacon, which gives you access to documentation and our support team right from your WordPress dashboard. To assist you more efficiently, we collect the current url, IP address, browser/device information, and debugging information.', 'easy-image-optimizer' ); ?><br>
     416                <a href="<?php echo \esc_url( $optin_url ); ?>" class="button-secondary"><?php esc_html_e( 'Allow', 'easy-image-optimizer' ); ?></a>&nbsp;
     417                <a href="<?php echo \esc_url( $optout_url ); ?>" class="button-secondary"><?php esc_html_e( 'Do not allow', 'easy-image-optimizer' ); ?></a>
     418            </p>
     419        </div>
     420        <?php
     421    }
     422
     423    /**
     424     * Inform the user that we disabled SP AIO to prevent conflicts with ExactDN.
     425     */
     426    public function sp_conflict_notice() {
     427        ?>
     428        <div id='easyio-sp-conflict' class='notice notice-warning'>
     429            <p>
     430                <?php \esc_html_e( 'ShortPixel/Autoptimize image optimization has been disabled to prevent conflicts with Easy Image Optimizer).', 'easy-image-optimizer' ); ?>
     431            </p>
     432        </div>
     433        <?php
    253434    }
    254435
     
    256437     * Tell the user to disable Hide my WP function that removes query strings.
    257438     */
    258     public function notice_hmwp_hide_version() {
     439    public function hmwp_hide_version_notice() {
    259440        ?>
    260441        <div id='easy-image-optimizer-warning-hmwp-hide-version' class='notice notice-warning'>
    261442            <p>
    262443                <?php \esc_html_e( 'Please enable the Random Static Number option in Hide My WP to ensure compatibility with Easy IO or disable the Hide Version option for best performance.', 'easy-image-optimizer' ); ?>
    263                 <?php \easyio_help_link( 'https://docs.ewww.io/article/50-exactdn-and-query-strings', '5a3d278a2c7d3a1943677b52' ); ?>
     444                <?php $this->settings->help_link( 'https://docs.ewww.io/article/50-exactdn-and-query-strings', '5a3d278a2c7d3a1943677b52' ); ?>
    264445            </p>
    265446        </div>
  • easy-image-optimizer/tags/4.3.0/easy-image-optimizer.php

    r3350722 r3398277  
    1414Description: Easily speed up your website to better connect with your visitors. Properly compress and size/scale images. Includes lazy load and WebP auto-convert.
    1515Author: Exactly WWW
    16 Version: 4.2.1
     16Version: 4.3.0
    1717Requires at least: 6.6
    1818Requires PHP: 8.1
     
    2525}
    2626
    27 // Check the PHP version.
    28 if ( ! defined( 'PHP_VERSION_ID' ) || PHP_VERSION_ID < 80100 ) {
    29     add_action( 'network_admin_notices', 'easyio_unsupported_php' );
    30     add_action( 'admin_notices', 'easyio_unsupported_php' );
    31 } elseif ( false === strpos( add_query_arg( '', '' ), 'easyio_disable=1' ) ) {
    32     define( 'EASYIO_VERSION', 421 );
     27if ( ! class_exists( 'EasyIO\Plugin' ) && ! str_contains( add_query_arg( '', '' ), 'easyio_disable=1' ) ) {
     28    define( 'EASYIO_VERSION', 430 );
    3329
    3430    /**
     
    5854            if ( ! is_writable( WP_CONTENT_DIR ) || ! empty( $_ENV['PANTHEON_ENVIRONMENT'] ) ) {
    5955                $upload_dir = wp_get_upload_dir();
    60                 if ( false === strpos( $upload_dir['basedir'], '://' ) && is_writable( $upload_dir['basedir'] ) ) {
     56                if ( ! str_contains( $upload_dir['basedir'], '://' ) && is_writable( $upload_dir['basedir'] ) ) {
    6157                    $easyio_content_dir = trailingslashit( $upload_dir['basedir'] ) . trailingslashit( 'easyio' );
    6258                }
     
    6662    }
    6763
    68     /**
    69      * All the 'unique' functions for the core Easy IO plugin.
    70      */
    71     require_once EASYIO_PLUGIN_PATH . 'unique.php';
    7264    /**
    7365     * All the base functions for our plugins.
     
    8880    easyio();
    8981} // End if().
    90 
    91 if ( ! function_exists( 'easyio_unsupported_php' ) ) {
    92     /**
    93      * Display a notice that the PHP version is too old.
    94      */
    95     function easyio_unsupported_php() {
    96         echo '<div id="easyio-warning-php" class="error"><p><a href="https://docs.ewww.io/article/55-upgrading-php" target="_blank" data-beacon-article="5ab2baa6042863478ea7c2ae">' . esc_html__( 'Easy Image Optimizer requires PHP 8.1 or greater. Newer versions of PHP are faster and more secure. If you are unsure how to upgrade to a supported version, ask your webhost for instructions.', 'easy-image-optimizer' ) . '</a></p></div>';
    97     }
    98 }
  • easy-image-optimizer/tags/4.3.0/includes/eio.js

    r3076002 r3398277  
    3838        });
    3939    }
    40     $('#easyio-general-settings').show();
    41     $('li.easyio-general-nav').addClass('easyio-selected');
    42     $('#easyio-support-settings').hide();
    4340    $('.easyio-general-nav').click(function() {
    4441        $('.easyio-tab-nav li').removeClass('easyio-selected');
     
    5956        $('#easyio-hidden-submit').show();
    6057    });
    61     return false;
     58    $('a#easyio-activate').on( 'click', function() {
     59        $('a#easyio-activate').hide();
     60        $('#easyio-activation-processing').show();
     61        activateExactDNSite();
     62        return false;
     63    });
     64    function activateExactDNSite() {
     65        var easyio_post_action = 'easyio_activate';
     66        var easyio_post_data = {
     67            action: easyio_post_action,
     68            _wpnonce: easyio_vars._wpnonce,
     69        };
     70        $.post(ajaxurl, easyio_post_data, function(response) {
     71            try {
     72                var easyio_response = JSON.parse(response);
     73            } catch (err) {
     74                $('#easyio-activation-processing').hide();
     75                $('#easyio-activation-result').html(easyio_vars.invalid_response);
     76                $('#easyio-activation-result').addClass('error');
     77                $('#easyio-activation-result').show();
     78                console.log( response );
     79                return false;
     80            }
     81            if ( easyio_response.error ) {
     82                $('#easyio-activation-processing').hide();
     83                $('a#easyio-activate').show();
     84                $('#easyio-activation-result').html(easyio_response.error);
     85                $('#easyio-activation-result').addClass('error');
     86                $('#easyio-activation-result').show();
     87            } else if ( ! easyio_response.success ) {
     88                $('#easyio-activation-processing').hide();
     89                $('#easyio-activation-result').html(easyio_vars.invalid_response);
     90                $('#easyio-activation-result').addClass('error');
     91                $('#easyio-activation-result').show();
     92                console.log( response );
     93            } else {
     94                $('#easyio-activation-processing').hide();
     95                $('#easyio-status').html(easyio_response.success);
     96                $('#exactdn_all_the_things').prop('checked', true);
     97                $('#easyio_lazy_load').prop('checked', true);
     98                $('#easyio_add_missing_dims').prop('disabled', false);
     99                $('.easyio-settings-table').show();
     100                $('#easyio-hidden-submit').show();
     101                $('table.easyio-inactive').hide();
     102            }
     103        });
     104        return false;
     105    }
     106    var easy_save_bar_width = $('#easyio-savings-fill').data('score');
     107    $('#easyio-savings-fill').animate( {
     108        width: easy_save_bar_width + '%',
     109    }, 1000 );
     110    var easy_bandwidth_bar_width = $('#easyio-bandwidth-fill').data('score');
     111    if ( easy_bandwidth_bar_width == 100 ) {
     112        $('#easyio-bandwidth-container .easyio-bar-fill').css('background-color', '#d63638');
     113        $('#easyio-bandwidth-flex a').css('color', '#d63638');
     114    }
     115    $('#easyio-bandwidth-fill').animate( {
     116        width: easy_bandwidth_bar_width + '%',
     117    }, 1000 );
     118    easyIORegisterStatsHandler();
     119    function easyIORegisterStatsHandler() {
     120        $('#easyio-show-stats').on('click', function(){
     121            var site_id = $(this).attr('data-site-id');
     122            var easyio_post_data = {
     123                action: 'easyio_get_site_stats',
     124                site_id: site_id,
     125                _wpnonce: easyio_vars._wpnonce,
     126            };     
     127            var statsContainerID = 'exactdn-stats-modal-' + site_id;
     128            var statsContainer = false;
     129            var statsExist = document.getElementById(statsContainerID);
     130            var closeIcon  = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>';                   
     131            if ( ! statsExist ) {
     132                $('body').append('<div id="' + statsContainerID + '" style="display:none;" class="exactdn-stats-modal"><div class="exactdn-stats-modal-close">' + closeIcon + '</div><div class="exactdn-stats-modal-charts"></div><img class="exactdn-loading-image" style="display:block;margin-left: auto;margin-right:auto;width:20px;" src="' + easyio_vars.loading_image_url + '" /></div>');
     133                statsContainer = $('#' + statsContainerID);
     134                $(statsContainer).on('click', '.exactdn-stats-modal-close', function() {
     135                    $('.exactdn-stats-modal').hide();
     136                    document.body.classList.toggle('exactdn-body-unscroll');
     137                });
     138                               
     139                $.post(ajaxurl, easyio_post_data, function(response) {
     140                    //console.log( response );
     141                    var is_json = true;
     142                    try {
     143                        var easyio_response = $.parseJSON(response);
     144                    } catch (err) {
     145                        is_json = false;
     146                    }
     147                    if ( ! is_json ) {
     148                        statsContainer.children('.exactdn-stats-modal-charts').html(easyio_vars.invalid_response);
     149                        $('.exactdn-loading-image').hide();
     150                        console.log(response);
     151                    } else if (easyio_response.error) {
     152                        statsContainer.children('.exactdn-stats-modal-charts').html('<strong>Error (contact support if necessary):</strong> ' + easyio_response.error);
     153                        $('.exactdn-loading-image').hide();
     154                    } else if (easyio_response.html) {
     155                        statsContainer.children('.exactdn-stats-modal-charts').html(easyio_response.html);
     156                        if (easyio_response.pending) {
     157                            console.log('need to fetch more stats, request pending');
     158                            setTimeout(fetchExtraStats, 10000, site_id);
     159                        } else {
     160                            $('.exactdn-loading-image').hide();
     161                        }
     162                    } else {
     163                        statsContainer.children('.exactdn-stats-modal-charts').html(easyio_vars.invalid_response);
     164                        $('.exactdn-loading-image').hide();
     165                        console.log(response);
     166                    }
     167                })
     168                .fail(function() {
     169                    statsContainer.children('.exactdn-stats-modal-charts').html(easyio_vars.invalid_response);
     170                    $('.exactdn-loading-image').hide();
     171                });
     172            } else {
     173                statsContainer = $('#' + statsContainerID);
     174            }
     175            statsContainer.show();
     176            document.body.classList.toggle('exactdn-body-unscroll');
     177            return false;
     178        });
     179    }
     180    var extraStatsRequests = 0;
     181    function fetchExtraStats(site_id) {
     182        var easyio_post_data = {
     183            action: 'easyio_get_site_stats',
     184            site_id: site_id,
     185            require_extra: 1,
     186            _wpnonce: easyio_vars._wpnonce,
     187        };
     188        var statsContainerID = 'exactdn-stats-modal-' + site_id;
     189        var statsContainer = false;
     190        var statsExist = document.getElementById(statsContainerID);
     191        if ( ! statsExist ) {
     192            console.log('no container for site #' + site_id);
     193            return;
     194        }
     195        statsContainer = $('#' + statsContainerID);
     196        if ( extraStatsRequests > 11 ) { // Roughly 2 minutes of waiting.
     197            $('.exactdn-loading-image').hide();
     198            statsContainer.find('.exactdn-stats-pending').text(easyio_vars.easyio_extra_stats_failed);
     199            return;
     200        }
     201        $.post(ajaxurl, easyio_post_data, function(response) {
     202            extraStatsRequests++;
     203            var is_json = true;
     204            try {
     205                var easyio_response = $.parseJSON(response);
     206            } catch (err) {
     207                is_json = false;
     208            }
     209            if ( ! is_json ) {
     210                console.log(response);
     211                setTimeout(fetchExtraStats, 10000, site_id);
     212                return;
     213            }
     214            if (easyio_response.error) {
     215                console.log(easyio_response.error);
     216            } else if (easyio_response.html) {
     217                $('.exactdn-loading-image').hide();
     218                statsContainer.children('.exactdn-stats-modal-charts').html(easyio_response.html);
     219                return;
     220            }
     221            setTimeout(fetchExtraStats, 10000, site_id);
     222        });
     223    }
    62224});
    63225function selectText(containerid) {
  • easy-image-optimizer/tags/4.3.0/includes/lazysizes-post.js

    r3197623 r3398277  
    2828            if(e.detail.instance != lazySizes){return;}
    2929
    30             var bg, bgWebP;
     30            var bg, bgWebP,swisLazyId;
    3131            if(!e.defaultPrevented) {
    3232
     
    3636
    3737                // handle data-back (so as not to conflict with the stock data-bg)
    38                 bg = e.target.getAttribute('data-back');
     38                bg = e.target.dataset.back;
     39                // Was getAttribute('data-back');
    3940                if (bg) {
    40                         if(ewww_webp_supported) {
     41                    if(ewww_webp_supported) {
    4142                        console.log('checking for data-back-webp');
    42                         bgWebP = e.target.getAttribute('data-back-webp');
     43                        bgWebP = e.target.dataset.backWebp;
     44                        // Was bgWebP = e.target.getAttribute('data-back-webp');
    4345                        if (bgWebP) {
    4446                            console.log('replacing data-back with data-back-webp');
     
    4648                        }
    4749                    }
    48                     var dPR = getdPR();
    49                     var targetWidth  = Math.round(e.target.offsetWidth * dPR);
    50                     var targetHeight = Math.round(e.target.offsetHeight * dPR);
    51                     if ( 0 === bg.search(/\[/) ) {
    52                     } else if (!shouldAutoScale(e.target)){
    53                     } else if (lazySizes.hC(e.target,'wp-block-cover')) {
    54                         console.log('found wp-block-cover with data-back');
    55                         if (lazySizes.hC(e.target,'has-parallax')) {
    56                             console.log('also has-parallax with data-back');
    57                             targetWidth  = Math.round(window.screen.width * dPR);
    58                             targetHeight = Math.round(window.screen.height * dPR);
    59                         } else if (targetHeight<300) {
    60                             targetHeight = 430;
    61                         }
    62                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    63                     } else if (lazySizes.hC(e.target,'cover-image')){
    64                         console.log('found .cover-image with data-back');
    65                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    66                     } else if (lazySizes.hC(e.target,'elementor-bg')){
    67                         console.log('found elementor-bg with data-back');
    68                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    69                     } else if (lazySizes.hC(e.target,'et_parallax_bg')){
    70                         console.log('found et_parallax_bg with data-back');
    71                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    72                     } else if (lazySizes.hC(e.target,'bg-image-crop')){
    73                         console.log('found bg-image-crop with data-back');
    74                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    75                     } else {
    76                         console.log('found other data-back');
    77                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg');
    78                     }
     50                    bg = constrainBg(bg,e.target);
    7951                    if ( e.target.style.backgroundImage && -1 === e.target.style.backgroundImage.search(/^initial/) ) {
    8052                        // Convert JSON for multiple URLs.
     
    11486                    }
    11587                }
     88                // Handle CSS images from SWIS.
     89                swisLazyId = e.target.dataset.swisLazyId;
     90                if (swisLazyId && swisLazyId in swis_lazy_css_images) {
     91                    console.log('fetching CSS images for swisLazyId ' + swisLazyId);
     92                    var css_images = swis_lazy_css_images[swisLazyId];
     93                    var swisStyle  = document.querySelector('style#swis-lazy-css-styles');
     94                    css_images.forEach(
     95                        function(css_image){
     96                            if (!css_image.url) {
     97                                return;
     98                            }
     99                            if(ewww_webp_supported && css_image.webp_url) {
     100                                console.log('webp supported, using webp url for css image');
     101                                css_image.url = css_image.webp_url;
     102                            }
     103                            css_image.url = constrainBg(css_image.url,e.target);
     104                            console.log('processing CSS image: ' + css_image.url + ' with hash ' + css_image.hash);
     105                            var cssRule = css_image.selector + ' {--swis-bg-' + css_image.hash + ': url(' + css_image.url + '); }';
     106                            swisStyle.sheet.insertRule(cssRule);
     107                        }
     108                    );
     109                }
    116110            }
    117111        }, false);
    118112    }
     113
     114    var constrainBg = function(bg,target){
     115        if ( 0 === bg.search(/\[/) ) {
     116            console.log('multiple URLs, not autoscaling background image');
     117            return bg;
     118        }
     119        if (!shouldAutoScale(target)){
     120            console.log('not autoscaling background image');
     121            return bg;
     122        }
     123        var dPR = getdPR();
     124        if ( dPR < eio_lazy_vars.bg_min_dpr ) {
     125            dPR = eio_lazy_vars.bg_min_dpr;
     126        }
     127        var targetWidth  = Math.round(target.offsetWidth * dPR);
     128        var targetHeight = Math.round(target.offsetHeight * dPR);
     129        var bgType       = 'bg';
     130        if (lazySizes.hC(target,'wp-block-cover')||lazySizes.hC(target,'wp-block-cover__image-background')){
     131            console.log('found wp-block-cover with data-back');
     132            if (lazySizes.hC(target,'has-parallax')) {
     133                console.log('also has-parallax with data-back');
     134                targetWidth  = Math.round(window.screen.width * dPR);
     135                targetHeight = Math.round(window.screen.height * dPR);
     136            } else if (targetHeight<300) {
     137                targetHeight = 430;
     138            }
     139            bgType = 'bg-cover';
     140        } else if (lazySizes.hC(target,'cover-image')){
     141            console.log('found .cover-image with data-back');
     142            bgType = 'bg-cover';
     143        } else if (lazySizes.hC(target,'elementor-bg')){
     144            console.log('found elementor-bg with data-back');
     145            bgType = 'bg-cover';
     146        } else if (lazySizes.hC(target,'et_parallax_bg')){
     147            console.log('found et_parallax_bg with data-back');
     148            bgType = 'bg-cover';
     149        } else if (lazySizes.hC(target,'bg-image-crop')){
     150            bgType = 'bg-cover';
     151            console.log('found bg-image-crop with data-back');
     152        } else {
     153            console.log('found other data-back');
     154        }
     155        var imgAspect = getAspectRatio(target);
     156        if ('bg' == bgType && targetHeight > 1 && targetWidth > 1 && imgAspect > 0) {
     157            var minimum_width  = Math.ceil(targetHeight * imgAspect);
     158            var minimum_height = Math.ceil(targetWidth / imgAspect);
     159            console.log('minimum_width = ' + minimum_width + ', targetWidth = ' + targetWidth);
     160            console.log('minimum_height = ' + minimum_height + ', targetHeight = ' + targetHeight);
     161            if (targetWidth+2 < minimum_width) {
     162                targetWidth = minimum_width;
     163            }
     164            if (targetHeight+2 < minimum_height) {
     165                targetHeight = minimum_height;
     166            }
     167            var realDims = getRealDimensionsFromImg(target);
     168            if (Math.abs(realDims.w - targetWidth) < 5 || Math.abs(realDims.h - targetHeight) < 5) {
     169                console.log('real dimensions within 5px of target sizes, no scaling');
     170                return bg;
     171            }
     172        }
     173        bg = constrainSrc(bg,targetWidth,targetHeight,bgType);
     174        return bg;
     175    };
    119176
    120177    var shouldAutoScale = function(target){
     
    294351
    295352    var getRealDimensionsFromImg = function(img){
    296         var realWidth = img.getAttribute('data-eio-rwidth');
    297         var realHeight = img.getAttribute('data-eio-rheight');
     353        var realWidth = img.dataset.eioRwidth;
     354        var realHeight = img.dataset.eioRheight;
    298355        if (realWidth > 1 && realHeight > 1) {
    299356            return {w:realWidth,h:realHeight};
     
    411468        }
    412469        if (e.target._lazysizesWidth === undefined) {
     470            if (!eio_lazy_vars.use_dpr && window.devicePixelRatio > 1) {
     471                console.log('use_dpr is disabled, reversing auto-sizes by dpr ' + window.devicePixelRatio);
     472                e.detail.width = Math.ceil(e.detail.width / window.devicePixelRatio);
     473            }
    413474            return;
    414475        }
     
    417478            console.log('no way! ' + e.detail.width + ' is smaller than ' + e.target._lazysizesWidth);
    418479            e.detail.width = e.target._lazysizesWidth;
     480        }
     481        if (!eio_lazy_vars.use_dpr && window.devicePixelRatio > 1) {
     482            console.log('use_dpr is disabled, reversing auto-sizes by dpr ' + window.devicePixelRatio);
     483            e.detail.width = Math.ceil(e.detail.width / window.devicePixelRatio);
    419484        }
    420485    });
  • easy-image-optimizer/tags/4.3.0/includes/lazysizes-pre.js

    r3197623 r3398277  
    11if (typeof ewww_webp_supported === 'undefined') {
    22    var ewww_webp_supported = false;
     3}
     4if (typeof swis_lazy_css_images === 'undefined') {
     5    var swis_lazy_css_images = {};
    36}
    47window.lazySizesConfig = window.lazySizesConfig || {};
     
    1821}
    1922console.log( 'root margin: ' + window.lazySizesConfig.expand );
     23for ( const [css_index, css_image] of Object.entries(swis_lazy_css_images)){
     24    console.log('processing css image ' + css_index + ': ' + css_image[0].url);
     25    try {
     26        document.querySelectorAll(css_image[0].selector).forEach((el) => {
     27            if (!el.classList.contains('lazyload')) {
     28                console.log('adding lazyload to css image ' + css_index + ': ' + css_image[0].url);
     29                el.classList.add('lazyload');
     30                el.dataset.swisLazyId = css_index;
     31                if (css_image[0].rwidth > 5 && css_image[0].rheight > 5) {
     32                    el.dataset.eioRwidth = css_image[0].rwidth;
     33                    el.dataset.eioRheight = css_image[0].rheight;
     34                }
     35            }
     36        });
     37    } catch (e) {
     38        console.log('error processing css image(s) for "' + css_index[0].selector + '": ' + e);
     39    }
     40}
  • easy-image-optimizer/tags/4.3.0/includes/lazysizes.min.js

    r3197623 r3398277  
    1 var ewww_webp_supported;void 0===ewww_webp_supported&&(ewww_webp_supported=!1),window.lazySizesConfig=window.lazySizesConfig||{},window.lazySizesConfig.expand=500<document.documentElement.clientHeight&&500<document.documentElement.clientWidth?1e3:740,window.lazySizesConfig.iframeLoadMode=1,"undefined"==typeof eio_lazy_vars&&(eio_lazy_vars={exactdn_domain:".exactdn.com",threshold:0,skip_autoscale:0,use_dpr:0}),50<eio_lazy_vars.threshold&&(window.lazySizesConfig.expand=eio_lazy_vars.threshold),function(e,t){function a(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)}t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):"function"==typeof define&&define.amd?define(["lazysizes"],t):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(e,n,o){"use strict";var s,l,d={};function c(e,t,a){var r,i;d[e]||(r=n.createElement(t?"link":"script"),i=n.getElementsByTagName("script")[0],t?(r.rel="stylesheet",r.href=e):(r.onload=function(){r.onerror=null,r.onload=null,a()},r.onerror=r.onload,r.src=e),d[e]=!0,d[r.src||r.href]=!0,i.parentNode.insertBefore(r,i))}n.addEventListener&&(l=/\(|\)|\s|'/,s=function(e,t){var a=n.createElement("img");a.onload=function(){a.onload=null,a.onerror=null,a=null,t()},a.onerror=a.onload,a.src=e,a&&a.complete&&a.onload&&a.onload()},addEventListener("lazybeforeunveil",function(e){var t,a,r;if(e.detail.instance==o&&!e.defaultPrevented){var i=e.target;if("none"==i.preload&&(i.preload=i.getAttribute("data-preload")||"auto"),null!=i.getAttribute("data-autoplay"))if(i.getAttribute("data-expand")&&!i.autoplay)try{i.play()}catch(e){}else requestAnimationFrame(function(){i.setAttribute("data-expand","-10"),o.aC(i,o.cfg.lazyClass)});(t=i.getAttribute("data-link"))&&c(t,!0),(t=i.getAttribute("data-script"))&&(e.detail.firesLoad=!0,c(t,null,function(){e.detail.firesLoad=!1,o.fire(i,"_lazyloaded",{},!0,!0)})),(t=i.getAttribute("data-require"))&&(o.cfg.requireJs?o.cfg.requireJs([t]):c(t)),(a=i.getAttribute("data-bg"))&&(e.detail.firesLoad=!0,s(a,function(){i.style.backgroundImage="url("+(l.test(a)?JSON.stringify(a):a)+")",e.detail.firesLoad=!1,o.fire(i,"_lazyloaded",{},!0,!0)})),(r=i.getAttribute("data-poster"))&&(e.detail.firesLoad=!0,s(r,function(){i.poster=r,e.detail.firesLoad=!1,o.fire(i,"_lazyloaded",{},!0,!0)}))}},!1))}),function(e,t){function a(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)}t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):"function"==typeof define&&define.amd?define(["lazysizes"],t):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(u,f,g){"use strict";var n;f.addEventListener&&(n=/\(|\)|\s|'/,addEventListener("lazybeforeunveil",function(e){var t,a,r,i;e.detail.instance==g&&(e.defaultPrevented||("none"==e.target.preload&&(e.target.preload="auto"),(r=e.target.getAttribute("data-back"))&&(ewww_webp_supported&&(i=e.target.getAttribute("data-back-webp"))&&(r=i),t=v(),a=Math.round(e.target.offsetWidth*t),i=Math.round(e.target.offsetHeight*t),0===r.search(/\[/)||o(e.target)&&(r=g.hC(e.target,"wp-block-cover")?(g.hC(e.target,"has-parallax")?(a=Math.round(u.screen.width*t),i=Math.round(u.screen.height*t)):i<300&&(i=430),s(r,a,i,"bg-cover")):g.hC(e.target,"cover-image")||g.hC(e.target,"elementor-bg")||g.hC(e.target,"et_parallax_bg")||g.hC(e.target,"bg-image-crop")?s(r,a,i,"bg-cover"):s(r,a,i,"bg")),e.target.style.backgroundImage&&-1===e.target.style.backgroundImage.search(/^initial/)?0===r.search(/\[/)?((r=JSON.parse(r)).forEach(function(e){n.test(e)&&JSON.stringify(e)}),r='url("'+r.join('"), url("')+'"',i=e.target.style.backgroundImage+", "+r,e.target.style.backgroundImage=i):e.target.style.backgroundImage=e.target.style.backgroundImage+', url("'+(n.test(r)?JSON.stringify(r):r)+'")':0===r.search(/\[/)?((r=JSON.parse(r)).forEach(function(e){n.test(e)&&JSON.stringify(e)}),r='url("'+r.join('"), url("')+'"',e.target.style.backgroundImage=r):e.target.style.backgroundImage="url("+(n.test(r)?JSON.stringify(r):r)+")")))},!1));function h(e){var t=e.getAttribute("data-eio-rwidth"),e=e.getAttribute("data-eio-rheight");return 1<t&&1<e?{w:t,h:e}:{w:0,h:0}}function m(e,t=!1){var a=v(),r=Math.round(e.offsetWidth*a),i=Math.round(e.offsetHeight*a),n=e.getAttribute("data-src"),a=e.getAttribute("data-src-webp");ewww_webp_supported&&a&&-1==n.search("webp=1")&&!t&&(n=a),o(e)&&(a=e,a=g.hC(a,"et_pb_jt_filterable_grid_item_image")||g.hC(a,"ss-foreground-image")||g.hC(a,"img-crop")?"img-crop":g.hC(a,"object-cover")&&(g.hC(a,"object-top")||g.hC(a,"object-bottom"))?"img-w":g.hC(a,"object-cover")&&(g.hC(a,"object-left")||g.hC(a,"object-right"))?"img-h":g.hC(a,"ct-image")&&g.hC(a,"object-cover")||!a.getAttribute("data-srcset")&&!a.srcset&&a.offsetHeight>a.offsetWidth&&1<l(a)?"img-crop":"img",(a=s(n,r,i,a,t))&&n!=a&&(t&&e.setAttribute("src",a),e.setAttribute("data-src",a)))}var o=function(e){if(1==eio_lazy_vars.skip_autoscale)return!1;for(var t=e,a=0;a<=7;a++){if(t.hasAttributes())for(var r=t.attributes,i=/skip-autoscale/,a=r.length-1;0<=a;a--){if(i.test(r[a].name))return!1;if(i.test(r[a].value))return!1}if(!t.parentNode||1!==t.parentNode.nodeType||!t.parentNode.hasAttributes)break;t=t.parentNode}return!0},s=function(e,t,a,r,i=!1){if(null===e)return e;var n=/w=(\d+)/,o=/fit=(\d+),(\d+)/,s=/resize=(\d+),(\d+)/,l=decodeURIComponent(e);if(/\.svg(\?.+)?$/.exec(l))return e;if(0<e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)){var d=s.exec(l);if(d&&(t<d[1]||i))return"img-w"===r?l.replace(s,"w="+t):"img-h"===r?l.replace(s,"h="+a):l.replace(s,"resize="+t+","+a);s=n.exec(e);if(s&&(t<=s[1]||i)){if("img-h"===r)return l.replace(n,"h="+a);if("bg-cover"!==r&&"img-crop"!==r)return e.replace(n,"w="+t);var c=Math.abs(s[1]-t);return 20<c||a<1080?e.replace(n,"resize="+t+","+a):e}c=o.exec(l);if(c&&(t<c[1]||i)){if("bg-cover"!==r&&"img-crop"!==r)return"img-w"===r?l.replace(o,"w="+t):"img-h"===r?l.replace(o,"h="+a):l.replace(o,"fit="+t+","+a);l=Math.abs(c[1]-t),o=Math.abs(c[2]-a);return 20<l||20<o?e.replace(n,"resize="+t+","+a):e}if(!s&&!c&&!d)return"img"===r?e+"&fit="+t+","+a:"bg-cover"===r||"img-crop"===r?e+"&resize="+t+","+a:"img-h"===r||t<a?e+"&h="+a:e+"&w="+t}return-1==e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)?"img"===r?e+"?fit="+t+","+a:"bg-cover"===r||"img-crop"===r?e+"?resize="+t+","+a:"img-h"===r||t<a?e+"?h="+a:e+"?w="+t:e},p=function(e){e=/-(\d+)x(\d+)\./.exec(e);return e&&1<e[1]&&1<e[2]?{w:e[1],h:e[2]}:{w:0,h:0}},l=function(e){var t=e.getAttribute("width"),a=e.getAttribute("height");if(1<t&&1<a)return t/a;a=!1;if(a=(a=e.src&&-1<e.src.search("http")?e.src:a)||e.getAttribute("data-src")){var r=p(a);if(r.w&&r.h)return r.w/r.h}r=h(e);if(r.w&&r.h)return r.w/r.h;e=function(e){var t;if(e.srcset?t=e.srcset.split(","):(e=e.getAttribute("data-srcset"))&&(t=e.split(",")),t){var a=0,r=t.length;if(r){for(;a<r;a++){var i,n=t[a].trim().split(" ");!n[0].length||(n=p(n[0])).w&&n.h&&(i=n)}if(i.w&&i.h)return i}}return{w:0,h:0}}(e);return e.w&&e.h?e.w/e.h:0},v=function(){return eio_lazy_vars.use_dpr&&1<u.devicePixelRatio?u.devicePixelRatio:1};f.addEventListener("lazybeforesizes",function(e){e.target.getAttribute("data-src");var t=l(e.target);1<e.target.clientHeight&&t&&(t=Math.ceil(t*e.target.clientHeight),e.detail.width+2<t&&(e.detail.width=t)),void 0!==e.target._lazysizesWidth&&e.detail.width<e.target._lazysizesWidth&&(e.detail.width=e.target._lazysizesWidth)}),f.addEventListener("lazybeforeunveil",function(e){var t,a,r,i,n=e.target,o=n.getAttribute("data-srcset");n.naturalWidth&&!o&&1<n.naturalWidth&&1<n.naturalHeight&&(t=v(),a=n.naturalWidth,r=n.naturalHeight,(e=h(n)).w&&e.w>a&&(a=e.w,r=e.h),a=n.clientWidth&&1.25*n.clientWidth*t<a,r=n.clientHeight&&1.25*n.clientHeight*t<r,(a||r)&&m(n)),ewww_webp_supported&&(!o||(i=n.getAttribute("data-srcset-webp"))&&n.setAttribute("data-srcset",i),(i=n.getAttribute("data-src-webp"))&&n.setAttribute("data-src",i))});function e(e=!1){e.type&&"load"===e.type&&g.autoSizer.checkElems(),v();var t,a=f.getElementsByClassName(g.cfg.loadedClass),r=a.length;if(r)for(t=0;t<r;t++){var i,n,o,s,l,d,c=a[t];c.src&&!c.srcset&&1<c.naturalWidth&&1<c.naturalHeight&&1<c.clientWidth&&1<c.clientHeight&&(i=c.naturalWidth,n=c.naturalHeight,o=u.innerWidth,s=u.innerHeight,l=h(c),d=p(c.src),l.w?o=l.w:d.w&&(o=d.w),l.h?s=l.h:d.h&&(s=d.h),l=c.clientWidth,d=c.clientHeight,(1.1*i<l&&l<=o||1.1*n<d&&d<=s)&&m(c,!0))}}var t,a,r,i,d=(t=e,i=function(){a=null,t()},function(){r=Date.now(),a=a||setTimeout(c,99)});function c(){var e=Date.now()-r;e<99?setTimeout(c,99-e):(u.requestIdleCallback||i)(i)}addEventListener("load",e),addEventListener("resize",d),setTimeout(e,2e4)}),function(e,t){t=t(e,e.document,Date);e.lazySizes=t,"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:{},function(r,f,n){"use strict";var g,h;if(!function(){var e,t={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:!0,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:!0,ricTimeout:0,throttleDelay:125};for(e in h=r.lazySizesConfig||r.lazysizesConfig||{},t)e in h||(h[e]=t[e])}(),!f||!f.getElementsByClassName)return{init:function(){},cfg:h,noSupport:!0};function c(e,t){E(e,t)||e.setAttribute("class",(e[v]("class")||"").trim()+" "+t)}function u(e,t){(t=E(e,t))&&e.setAttribute("class",(e[v]("class")||"").replace(t," "))}function m(e,t){var a;!l&&(a=r.picturefill||h.pf)?(t&&t.src&&!e[v]("srcset")&&e.setAttribute("srcset",t.src),a({reevaluate:!0,elements:[e]})):t&&t.src&&(e.src=t.src)}var a,i,t,o,s,p=f.documentElement,l=r.HTMLPictureElement,d="addEventListener",v="getAttribute",e=r[d].bind(r),y=r.setTimeout,b=r.requestAnimationFrame||y,z=r.requestIdleCallback,w=/^picture$/i,_=["load","error","lazyincluded","_lazyloaded"],C={},A=Array.prototype.forEach,E=function(e,t){return C[t]||(C[t]=new RegExp("(\\s|^)"+t+"(\\s|$)")),C[t].test(e[v]("class")||"")&&C[t]},L=function(t,a,e){var r=e?d:"removeEventListener";e&&L(t,a),_.forEach(function(e){t[r](e,a)})},x=function(e,t,a,r,i){var n=f.createEvent("Event");return(a=a||{}).instance=g,n.initEvent(t,!r,!i),n.detail=a,e.dispatchEvent(n),n},N=function(e,t){return(getComputedStyle(e,null)||{})[t]},M=function(e,t,a){for(a=a||e.offsetWidth;a<h.minSize&&t&&!e._lazysizesWidth;)a=t.offsetWidth,t=t.parentNode;return a},W=(o=[],s=t=[],k._lsFlush=S,k);function S(){var e=s;for(s=t.length?o:t,i=!(a=!0);e.length;)e.shift()();a=!1}function k(e,t){a&&!t?e.apply(this,arguments):(s.push(e),i||(i=!0,(f.hidden?y:b)(S)))}function H(a,e){return e?function(){W(a)}:function(){var e=this,t=arguments;W(function(){a.apply(e,t)})}}function I(e){function t(){var e=n.now()-r;e<99?y(t,99-e):(z||i)(i)}var a,r,i=function(){a=null,e()};return function(){r=n.now(),a=a||y(t,99)}}var T,j,B,O,R,q,F,J,P,D,$,U,G,K,Q,V,X,Y,Z,ee,te,ae,re,ie,ne,oe,se,le,de,ce,ue,fe=(Z=/^img$/i,ee=/^iframe$/i,te="onscroll"in r&&!/(gle|ing)bot/.test(navigator.userAgent),ie=-1,ne=function(e){return(U=null==U?"hidden"==N(f.body,"visibility"):U)||!("hidden"==N(e.parentNode,"visibility")&&"hidden"==N(e,"visibility"))},G=he,Q=re=ae=0,V=h.throttleDelay,X=h.ricTimeout,Y=z&&49<X?function(){z(me,{timeout:X}),X!==h.ricTimeout&&(X=h.ricTimeout)}:H(function(){y(me)},!0),se=H(pe),le=function(e){se({target:e.target})},de=H(function(t,e,a,r,i){var n,o,s,l,d;(s=x(t,"lazybeforeunveil",e)).defaultPrevented||(r&&(a?c(t,h.autosizesClass):t.setAttribute("sizes",r)),n=t[v](h.srcsetAttr),a=t[v](h.srcAttr),i&&(o=(d=t.parentNode)&&w.test(d.nodeName||"")),l=e.firesLoad||"src"in t&&(n||a||o),s={target:t},c(t,h.loadingClass),l&&(clearTimeout(B),B=y(ge,2500),L(t,le,!0)),o&&A.call(d.getElementsByTagName("source"),ve),n?t.setAttribute("srcset",n):a&&!o&&(ee.test(t.nodeName)?(r=a,0==(d=(e=t).getAttribute("data-load-mode")||h.iframeLoadMode)?e.contentWindow.location.replace(r):1==d&&(e.src=r)):t.src=a),i&&(n||o)&&m(t,{src:a})),t._lazyRace&&delete t._lazyRace,u(t,h.lazyClass),W(function(){var e=t.complete&&1<t.naturalWidth;l&&!e||(e&&c(t,h.fastLoadedClass),pe(s),t._lazyCache=!0,y(function(){"_lazyCache"in t&&delete t._lazyCache},9)),"lazy"==t.loading&&re--},!0)}),ue=I(function(){h.loadMode=3,oe()}),{_:function(){R=n.now(),g.elements=f.getElementsByClassName(h.lazyClass),T=f.getElementsByClassName(h.lazyClass+" "+h.preloadClass),e("scroll",oe,!0),e("resize",oe,!0),e("pageshow",function(e){var t;!e.persisted||(t=f.querySelectorAll("."+h.loadingClass)).length&&t.forEach&&b(function(){t.forEach(function(e){e.complete&&ce(e)})})}),r.MutationObserver?new MutationObserver(oe).observe(p,{childList:!0,subtree:!0,attributes:!0}):(p[d]("DOMNodeInserted",oe,!0),p[d]("DOMAttrModified",oe,!0),setInterval(oe,999)),e("hashchange",oe,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach(function(e){f[d](e,oe,!0)}),/d$|^c/.test(f.readyState)?be():(e("load",be),f[d]("DOMContentLoaded",oe),y(be,2e4)),g.elements.length?(he(),W._lsFlush()):oe()},checkElems:oe=function(e){var t;(e=!0===e)&&(X=33),K||(K=!0,(t=V-(n.now()-Q))<0&&(t=0),e||t<9?Y():y(Y,t))},unveil:ce=function(e){var t,a,r,i;e._lazyRace||(!(i="auto"==(r=(a=Z.test(e.nodeName))&&(e[v](h.sizesAttr)||e[v]("sizes"))))&&j||!a||!e[v]("src")&&!e.srcset||e.complete||E(e,h.errorClass)||!E(e,h.lazyClass))&&(t=x(e,"lazyunveilread").detail,i&&Ce.updateElem(e,!0,e.offsetWidth),e._lazyRace=!0,re++,de(e,t,i,r,a))},_aLSL:ye});function ge(e){re--,e&&!(re<0)&&e.target||(re=0)}function he(){var e,t,a,r,i,n,o,s,l,d,c,u=g.elements;if((O=h.loadMode)&&re<8&&(e=u.length)){for(t=0,ie++;t<e;t++)if(u[t]&&!u[t]._lazyRace)if(!te||g.prematureUnveil&&g.prematureUnveil(u[t]))ce(u[t]);else if((o=u[t][v]("data-expand"))&&(i=+o)||(i=ae),l||(l=!h.expand||h.expand<1?500<p.clientHeight&&500<p.clientWidth?500:370:h.expand,d=(g._defEx=l)*h.expFactor,c=h.hFac,U=null,ae<d&&re<1&&2<ie&&2<O&&!f.hidden?(ae=d,ie=0):ae=1<O&&1<ie&&re<6?l:0),s!==i&&(q=innerWidth+i*c,F=innerHeight+i,n=-1*i,s=i),d=u[t].getBoundingClientRect(),($=d.bottom)>=n&&(J=d.top)<=F&&(D=d.right)>=n*c&&(P=d.left)<=q&&($||D||P||J)&&(h.loadHidden||ne(u[t]))&&(j&&re<3&&!o&&(O<3||ie<4)||function(e,t){var a,r=e,i=ne(e);for(J-=t,$+=t,P-=t,D+=t;i&&(r=r.offsetParent)&&r!=f.body&&r!=p;)(i=0<(N(r,"opacity")||1))&&"visible"!=N(r,"overflow")&&(a=r.getBoundingClientRect(),i=D>a.left&&P<a.right&&$>a.top-1&&J<a.bottom+1);return i}(u[t],i))){if(ce(u[t]),r=!0,9<re)break}else!r&&j&&!a&&re<4&&ie<4&&2<O&&(T[0]||h.preloadAfterLoad)&&(T[0]||!o&&($||D||P||J||"auto"!=u[t][v](h.sizesAttr)))&&(a=T[0]||u[t]);a&&!r&&ce(a)}}function me(){K=!1,Q=n.now(),G()}function pe(e){var t=e.target;t._lazyCache?delete t._lazyCache:(ge(e),c(t,h.loadedClass),u(t,h.loadingClass),L(t,le),x(t,"lazyloaded"))}function ve(e){var t,a=e[v](h.srcsetAttr);(t=h.customMedia[e[v]("data-media")||e[v]("media")])&&e.setAttribute("media",t),a&&e.setAttribute("srcset",a)}function ye(){3==h.loadMode&&(h.loadMode=2),ue()}function be(){j||(n.now()-R<999?y(be,999):(j=!0,h.loadMode=3,oe(),e("scroll",ye,!0)))}var ze,we,_e,Ce=(we=H(function(e,t,a,r){var i,n,o;if(e._lazysizesWidth=r,e.setAttribute("sizes",r+="px"),w.test(t.nodeName||""))for(n=0,o=(i=t.getElementsByTagName("source")).length;n<o;n++)i[n].setAttribute("sizes",r);a.detail.dataAttr||m(e,a.detail)}),{_:function(){ze=f.getElementsByClassName(h.autosizesClass),e("resize",_e)},checkElems:_e=I(function(){var e,t=ze.length;if(t)for(e=0;e<t;e++)Ae(ze[e])}),updateElem:Ae});function Ae(e,t,a){var r=e.parentNode;r&&(a=M(e,r,a),(t=x(e,"lazybeforesizes",{width:a,dataAttr:!!t})).defaultPrevented||(a=t.detail.width)&&a!==e._lazysizesWidth&&we(e,r,t,a))}function Ee(){!Ee.i&&f.getElementsByClassName&&(Ee.i=!0,Ce._(),fe._())}return y(function(){h.init&&Ee()}),g={cfg:h,autoSizer:Ce,loader:fe,init:Ee,uP:m,aC:c,rC:u,hC:E,fire:x,gW:M,rAF:W}});
     1var ewww_webp_supported,swis_lazy_css_images;void 0===ewww_webp_supported&&(ewww_webp_supported=!1),void 0===swis_lazy_css_images&&(swis_lazy_css_images={}),window.lazySizesConfig=window.lazySizesConfig||{},window.lazySizesConfig.expand=500<document.documentElement.clientHeight&&500<document.documentElement.clientWidth?1e3:740,window.lazySizesConfig.iframeLoadMode=1,"undefined"==typeof eio_lazy_vars&&(eio_lazy_vars={exactdn_domain:".exactdn.com",threshold:0,skip_autoscale:0,use_dpr:0}),50<eio_lazy_vars.threshold&&(window.lazySizesConfig.expand=eio_lazy_vars.threshold);for(const[a,b]of Object.entries(swis_lazy_css_images))try{document.querySelectorAll(b[0].selector).forEach(e=>{e.classList.contains("lazyload")||(e.classList.add("lazyload"),e.dataset.swisLazyId=a,5<b[0].rwidth&&5<b[0].rheight&&(e.dataset.eioRwidth=b[0].rwidth,e.dataset.eioRheight=b[0].rheight))})}catch(e){}!function(e,t){function a(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)}t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):"function"==typeof define&&define.amd?define(["lazysizes"],t):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(e,n,s){"use strict";var o,l,d={};function c(e,t,a){var i,r;d[e]||(i=n.createElement(t?"link":"script"),r=n.getElementsByTagName("script")[0],t?(i.rel="stylesheet",i.href=e):(i.onload=function(){i.onerror=null,i.onload=null,a()},i.onerror=i.onload,i.src=e),d[e]=!0,d[i.src||i.href]=!0,r.parentNode.insertBefore(i,r))}n.addEventListener&&(l=/\(|\)|\s|'/,o=function(e,t){var a=n.createElement("img");a.onload=function(){a.onload=null,a.onerror=null,a=null,t()},a.onerror=a.onload,a.src=e,a&&a.complete&&a.onload&&a.onload()},addEventListener("lazybeforeunveil",function(e){var t,a,i;if(e.detail.instance==s&&!e.defaultPrevented){var r=e.target;if("none"==r.preload&&(r.preload=r.getAttribute("data-preload")||"auto"),null!=r.getAttribute("data-autoplay"))if(r.getAttribute("data-expand")&&!r.autoplay)try{r.play()}catch(e){}else requestAnimationFrame(function(){r.setAttribute("data-expand","-10"),s.aC(r,s.cfg.lazyClass)});(t=r.getAttribute("data-link"))&&c(t,!0),(t=r.getAttribute("data-script"))&&(e.detail.firesLoad=!0,c(t,null,function(){e.detail.firesLoad=!1,s.fire(r,"_lazyloaded",{},!0,!0)})),(t=r.getAttribute("data-require"))&&(s.cfg.requireJs?s.cfg.requireJs([t]):c(t)),(a=r.getAttribute("data-bg"))&&(e.detail.firesLoad=!0,o(a,function(){r.style.backgroundImage="url("+(l.test(a)?JSON.stringify(a):a)+")",e.detail.firesLoad=!1,s.fire(r,"_lazyloaded",{},!0,!0)})),(i=r.getAttribute("data-poster"))&&(e.detail.firesLoad=!0,o(i,function(){r.poster=i,e.detail.firesLoad=!1,s.fire(r,"_lazyloaded",{},!0,!0)}))}},!1))}),function(e,t){function a(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)}t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):"function"==typeof define&&define.amd?define(["lazysizes"],t):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(u,f,h){"use strict";var r;f.addEventListener&&(r=/\(|\)|\s|'/,addEventListener("lazybeforeunveil",function(t){var e,a,i;t.detail.instance==h&&(t.defaultPrevented||("none"==t.target.preload&&(t.target.preload="auto"),(a=t.target.dataset.back)&&(ewww_webp_supported&&(e=t.target.dataset.backWebp)&&(a=e),a=n(a,t.target),t.target.style.backgroundImage&&-1===t.target.style.backgroundImage.search(/^initial/)?0===a.search(/\[/)?((a=JSON.parse(a)).forEach(function(e){r.test(e)&&JSON.stringify(e)}),a='url("'+a.join('"), url("')+'"',e=t.target.style.backgroundImage+", "+a,t.target.style.backgroundImage=e):t.target.style.backgroundImage=t.target.style.backgroundImage+', url("'+(r.test(a)?JSON.stringify(a):a)+'")':0===a.search(/\[/)?((a=JSON.parse(a)).forEach(function(e){r.test(e)&&JSON.stringify(e)}),a='url("'+a.join('"), url("')+'"',t.target.style.backgroundImage=a):t.target.style.backgroundImage="url("+(r.test(a)?JSON.stringify(a):a)+")"),(a=t.target.dataset.swisLazyId)&&a in swis_lazy_css_images&&(a=swis_lazy_css_images[a],i=f.querySelector("style#swis-lazy-css-styles"),a.forEach(function(e){e.url&&(ewww_webp_supported&&e.webp_url&&(e.url=e.webp_url),e.url=n(e.url,t.target),e=e.selector+" {--swis-bg-"+e.hash+": url("+e.url+"); }",i.sheet.insertRule(e))}))))},!1));function g(e,t=!1){var a=y(),i=Math.round(e.offsetWidth*a),r=Math.round(e.offsetHeight*a),n=e.getAttribute("data-src"),a=e.getAttribute("data-src-webp");ewww_webp_supported&&a&&-1==n.search("webp=1")&&!t&&(n=a),o(e)&&(a=e,a=h.hC(a,"et_pb_jt_filterable_grid_item_image")||h.hC(a,"ss-foreground-image")||h.hC(a,"img-crop")?"img-crop":h.hC(a,"object-cover")&&(h.hC(a,"object-top")||h.hC(a,"object-bottom"))?"img-w":h.hC(a,"object-cover")&&(h.hC(a,"object-left")||h.hC(a,"object-right"))?"img-h":h.hC(a,"ct-image")&&h.hC(a,"object-cover")||!a.getAttribute("data-srcset")&&!a.srcset&&a.offsetHeight>a.offsetWidth&&1<d(a)?"img-crop":"img",(a=l(n,i,r,a,t))&&n!=a&&(t&&e.setAttribute("src",a),e.setAttribute("data-src",a)))}var n=function(e,t){if(0===e.search(/\[/))return e;if(!o(t))return e;var a=y();a<eio_lazy_vars.bg_min_dpr&&(a=eio_lazy_vars.bg_min_dpr);var i=Math.round(t.offsetWidth*a),r=Math.round(t.offsetHeight*a),n="bg";h.hC(t,"wp-block-cover")||h.hC(t,"wp-block-cover__image-background")?(h.hC(t,"has-parallax")?(i=Math.round(u.screen.width*a),r=Math.round(u.screen.height*a)):r<300&&(r=430),n="bg-cover"):(h.hC(t,"cover-image")||h.hC(t,"elementor-bg")||h.hC(t,"et_parallax_bg")||h.hC(t,"bg-image-crop"))&&(n="bg-cover");var s=d(t);if("bg"==n&&1<r&&1<i&&0<s){a=Math.ceil(r*s),s=Math.ceil(i/s);i+2<a&&(i=a),r+2<s&&(r=s);t=p(t);if(Math.abs(t.w-i)<5||Math.abs(t.h-r)<5)return e}return e=l(e,i,r,n)},o=function(e){if(1==eio_lazy_vars.skip_autoscale)return!1;for(var t=e,a=0;a<=7;a++){if(t.hasAttributes())for(var i=t.attributes,r=/skip-autoscale/,a=i.length-1;0<=a;a--){if(r.test(i[a].name))return!1;if(r.test(i[a].value))return!1}if(!t.parentNode||1!==t.parentNode.nodeType||!t.parentNode.hasAttributes)break;t=t.parentNode}return!0},l=function(e,t,a,i,r=!1){if(null===e)return e;var n=/w=(\d+)/,s=/fit=(\d+),(\d+)/,o=/resize=(\d+),(\d+)/,l=decodeURIComponent(e);if(/\.svg(\?.+)?$/.exec(l))return e;if(0<e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)){var d=o.exec(l);if(d&&(t<d[1]||r))return"img-w"===i?l.replace(o,"w="+t):"img-h"===i?l.replace(o,"h="+a):l.replace(o,"resize="+t+","+a);o=n.exec(e);if(o&&(t<=o[1]||r)){if("img-h"===i)return l.replace(n,"h="+a);if("bg-cover"!==i&&"img-crop"!==i)return e.replace(n,"w="+t);var c=Math.abs(o[1]-t);return 20<c||a<1080?e.replace(n,"resize="+t+","+a):e}c=s.exec(l);if(c&&(t<c[1]||r)){if("bg-cover"!==i&&"img-crop"!==i)return"img-w"===i?l.replace(s,"w="+t):"img-h"===i?l.replace(s,"h="+a):l.replace(s,"fit="+t+","+a);l=Math.abs(c[1]-t),s=Math.abs(c[2]-a);return 20<l||20<s?e.replace(n,"resize="+t+","+a):e}if(!o&&!c&&!d)return"img"===i?e+"&fit="+t+","+a:"bg-cover"===i||"img-crop"===i?e+"&resize="+t+","+a:"img-h"===i||t<a?e+"&h="+a:e+"&w="+t}return-1==e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)?"img"===i?e+"?fit="+t+","+a:"bg-cover"===i||"img-crop"===i?e+"?resize="+t+","+a:"img-h"===i||t<a?e+"?h="+a:e+"?w="+t:e},m=function(e){e=/-(\d+)x(\d+)\./.exec(e);return e&&1<e[1]&&1<e[2]?{w:e[1],h:e[2]}:{w:0,h:0}},p=function(e){var t=e.dataset.eioRwidth,e=e.dataset.eioRheight;return 1<t&&1<e?{w:t,h:e}:{w:0,h:0}},d=function(e){var t=e.getAttribute("width"),a=e.getAttribute("height");if(1<t&&1<a)return t/a;a=!1;if(a=(a=e.src&&-1<e.src.search("http")?e.src:a)||e.getAttribute("data-src")){var i=m(a);if(i.w&&i.h)return i.w/i.h}i=p(e);if(i.w&&i.h)return i.w/i.h;e=function(e){var t;if(e.srcset?t=e.srcset.split(","):(e=e.getAttribute("data-srcset"))&&(t=e.split(",")),t){var a=0,i=t.length;if(i){for(;a<i;a++){var r,n=t[a].trim().split(" ");!n[0].length||(n=m(n[0])).w&&n.h&&(r=n)}if(r.w&&r.h)return r}}return{w:0,h:0}}(e);return e.w&&e.h?e.w/e.h:0},y=function(){return eio_lazy_vars.use_dpr&&1<u.devicePixelRatio?u.devicePixelRatio:1};f.addEventListener("lazybeforesizes",function(e){e.target.getAttribute("data-src");var t=d(e.target);1<e.target.clientHeight&&t&&(t=Math.ceil(t*e.target.clientHeight),e.detail.width+2<t&&(e.detail.width=t)),void 0!==e.target._lazysizesWidth&&e.detail.width<e.target._lazysizesWidth&&(e.detail.width=e.target._lazysizesWidth),!eio_lazy_vars.use_dpr&&1<u.devicePixelRatio&&(e.detail.width=Math.ceil(e.detail.width/u.devicePixelRatio))}),f.addEventListener("lazybeforeunveil",function(e){var t,a,i,r,n=e.target,s=n.getAttribute("data-srcset");n.naturalWidth&&!s&&1<n.naturalWidth&&1<n.naturalHeight&&(t=y(),a=n.naturalWidth,i=n.naturalHeight,(e=p(n)).w&&e.w>a&&(a=e.w,i=e.h),a=n.clientWidth&&1.25*n.clientWidth*t<a,i=n.clientHeight&&1.25*n.clientHeight*t<i,(a||i)&&g(n)),ewww_webp_supported&&(!s||(r=n.getAttribute("data-srcset-webp"))&&n.setAttribute("data-srcset",r),(r=n.getAttribute("data-src-webp"))&&n.setAttribute("data-src",r))});function e(e=!1){e.type&&"load"===e.type&&h.autoSizer.checkElems(),y();var t,a=f.getElementsByClassName(h.cfg.loadedClass),i=a.length;if(i)for(t=0;t<i;t++){var r,n,s,o,l,d,c=a[t];c.src&&!c.srcset&&1<c.naturalWidth&&1<c.naturalHeight&&1<c.clientWidth&&1<c.clientHeight&&(r=c.naturalWidth,n=c.naturalHeight,s=u.innerWidth,o=u.innerHeight,l=p(c),d=m(c.src),l.w?s=l.w:d.w&&(s=d.w),l.h?o=l.h:d.h&&(o=d.h),l=c.clientWidth,d=c.clientHeight,(1.1*r<l&&l<=s||1.1*n<d&&d<=o)&&g(c,!0))}}var t,a,i,s,c=(t=e,s=function(){a=null,t()},function(){i=Date.now(),a=a||setTimeout(v,99)});function v(){var e=Date.now()-i;e<99?setTimeout(v,99-e):(u.requestIdleCallback||s)(s)}addEventListener("load",e),addEventListener("resize",c),setTimeout(e,2e4)}),function(e,t){t=t(e,e.document,Date);e.lazySizes=t,"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:{},function(i,f,n){"use strict";var h,g;if(!function(){var e,t={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:!0,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:!0,ricTimeout:0,throttleDelay:125};for(e in g=i.lazySizesConfig||i.lazysizesConfig||{},t)e in g||(g[e]=t[e])}(),!f||!f.getElementsByClassName)return{init:function(){},cfg:g,noSupport:!0};function c(e,t){E(e,t)||e.setAttribute("class",(e[y]("class")||"").trim()+" "+t)}function u(e,t){(t=E(e,t))&&e.setAttribute("class",(e[y]("class")||"").replace(t," "))}function m(e,t){var a;!l&&(a=i.picturefill||g.pf)?(t&&t.src&&!e[y]("srcset")&&e.setAttribute("srcset",t.src),a({reevaluate:!0,elements:[e]})):t&&t.src&&(e.src=t.src)}var a,r,t,s,o,p=f.documentElement,l=i.HTMLPictureElement,d="addEventListener",y="getAttribute",e=i[d].bind(i),v=i.setTimeout,z=i.requestAnimationFrame||v,b=i.requestIdleCallback,w=/^picture$/i,_=["load","error","lazyincluded","_lazyloaded"],C={},A=Array.prototype.forEach,E=function(e,t){return C[t]||(C[t]=new RegExp("(\\s|^)"+t+"(\\s|$)")),C[t].test(e[y]("class")||"")&&C[t]},L=function(t,a,e){var i=e?d:"removeEventListener";e&&L(t,a),_.forEach(function(e){t[i](e,a)})},x=function(e,t,a,i,r){var n=f.createEvent("Event");return(a=a||{}).instance=h,n.initEvent(t,!i,!r),n.detail=a,e.dispatchEvent(n),n},M=function(e,t){return(getComputedStyle(e,null)||{})[t]},N=function(e,t,a){for(a=a||e.offsetWidth;a<g.minSize&&t&&!e._lazysizesWidth;)a=t.offsetWidth,t=t.parentNode;return a},S=(s=[],o=t=[],k._lsFlush=W,k);function W(){var e=o;for(o=t.length?s:t,r=!(a=!0);e.length;)e.shift()();a=!1}function k(e,t){a&&!t?e.apply(this,arguments):(o.push(e),r||(r=!0,(f.hidden?v:z)(W)))}function H(a,e){return e?function(){S(a)}:function(){var e=this,t=arguments;S(function(){a.apply(e,t)})}}function R(e){function t(){var e=n.now()-i;e<99?v(t,99-e):(b||r)(r)}var a,i,r=function(){a=null,e()};return function(){i=n.now(),a=a||v(t,99)}}var I,j,T,O,q,B,P,F,J,D,$,U,G,K,Q,V,X,Y,Z,ee,te,ae,ie,re,ne,se,oe,le,de,ce,ue,fe=(Z=/^img$/i,ee=/^iframe$/i,te="onscroll"in i&&!/(gle|ing)bot/.test(navigator.userAgent),re=-1,ne=function(e){return(U=null==U?"hidden"==M(f.body,"visibility"):U)||!("hidden"==M(e.parentNode,"visibility")&&"hidden"==M(e,"visibility"))},G=ge,Q=ie=ae=0,V=g.throttleDelay,X=g.ricTimeout,Y=b&&49<X?function(){b(me,{timeout:X}),X!==g.ricTimeout&&(X=g.ricTimeout)}:H(function(){v(me)},!0),oe=H(pe),le=function(e){oe({target:e.target})},de=H(function(t,e,a,i,r){var n,s,o,l,d;(o=x(t,"lazybeforeunveil",e)).defaultPrevented||(i&&(a?c(t,g.autosizesClass):t.setAttribute("sizes",i)),n=t[y](g.srcsetAttr),a=t[y](g.srcAttr),r&&(s=(d=t.parentNode)&&w.test(d.nodeName||"")),l=e.firesLoad||"src"in t&&(n||a||s),o={target:t},c(t,g.loadingClass),l&&(clearTimeout(T),T=v(he,2500),L(t,le,!0)),s&&A.call(d.getElementsByTagName("source"),ye),n?t.setAttribute("srcset",n):a&&!s&&(ee.test(t.nodeName)?(i=a,0==(d=(e=t).getAttribute("data-load-mode")||g.iframeLoadMode)?e.contentWindow.location.replace(i):1==d&&(e.src=i)):t.src=a),r&&(n||s)&&m(t,{src:a})),t._lazyRace&&delete t._lazyRace,u(t,g.lazyClass),S(function(){var e=t.complete&&1<t.naturalWidth;l&&!e||(e&&c(t,g.fastLoadedClass),pe(o),t._lazyCache=!0,v(function(){"_lazyCache"in t&&delete t._lazyCache},9)),"lazy"==t.loading&&ie--},!0)}),ue=R(function(){g.loadMode=3,se()}),{_:function(){q=n.now(),h.elements=f.getElementsByClassName(g.lazyClass),I=f.getElementsByClassName(g.lazyClass+" "+g.preloadClass),e("scroll",se,!0),e("resize",se,!0),e("pageshow",function(e){var t;!e.persisted||(t=f.querySelectorAll("."+g.loadingClass)).length&&t.forEach&&z(function(){t.forEach(function(e){e.complete&&ce(e)})})}),i.MutationObserver?new MutationObserver(se).observe(p,{childList:!0,subtree:!0,attributes:!0}):(p[d]("DOMNodeInserted",se,!0),p[d]("DOMAttrModified",se,!0),setInterval(se,999)),e("hashchange",se,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach(function(e){f[d](e,se,!0)}),/d$|^c/.test(f.readyState)?ze():(e("load",ze),f[d]("DOMContentLoaded",se),v(ze,2e4)),h.elements.length?(ge(),S._lsFlush()):se()},checkElems:se=function(e){var t;(e=!0===e)&&(X=33),K||(K=!0,(t=V-(n.now()-Q))<0&&(t=0),e||t<9?Y():v(Y,t))},unveil:ce=function(e){var t,a,i,r;e._lazyRace||(!(r="auto"==(i=(a=Z.test(e.nodeName))&&(e[y](g.sizesAttr)||e[y]("sizes"))))&&j||!a||!e[y]("src")&&!e.srcset||e.complete||E(e,g.errorClass)||!E(e,g.lazyClass))&&(t=x(e,"lazyunveilread").detail,r&&Ce.updateElem(e,!0,e.offsetWidth),e._lazyRace=!0,ie++,de(e,t,r,i,a))},_aLSL:ve});function he(e){ie--,e&&!(ie<0)&&e.target||(ie=0)}function ge(){var e,t,a,i,r,n,s,o,l,d,c,u=h.elements;if((O=g.loadMode)&&ie<8&&(e=u.length)){for(t=0,re++;t<e;t++)if(u[t]&&!u[t]._lazyRace)if(!te||h.prematureUnveil&&h.prematureUnveil(u[t]))ce(u[t]);else if((s=u[t][y]("data-expand"))&&(r=+s)||(r=ae),l||(l=!g.expand||g.expand<1?500<p.clientHeight&&500<p.clientWidth?500:370:g.expand,d=(h._defEx=l)*g.expFactor,c=g.hFac,U=null,ae<d&&ie<1&&2<re&&2<O&&!f.hidden?(ae=d,re=0):ae=1<O&&1<re&&ie<6?l:0),o!==r&&(B=innerWidth+r*c,P=innerHeight+r,n=-1*r,o=r),d=u[t].getBoundingClientRect(),($=d.bottom)>=n&&(F=d.top)<=P&&(D=d.right)>=n*c&&(J=d.left)<=B&&($||D||J||F)&&(g.loadHidden||ne(u[t]))&&(j&&ie<3&&!s&&(O<3||re<4)||function(e,t){var a,i=e,r=ne(e);for(F-=t,$+=t,J-=t,D+=t;r&&(i=i.offsetParent)&&i!=f.body&&i!=p;)(r=0<(M(i,"opacity")||1))&&"visible"!=M(i,"overflow")&&(a=i.getBoundingClientRect(),r=D>a.left&&J<a.right&&$>a.top-1&&F<a.bottom+1);return r}(u[t],r))){if(ce(u[t]),i=!0,9<ie)break}else!i&&j&&!a&&ie<4&&re<4&&2<O&&(I[0]||g.preloadAfterLoad)&&(I[0]||!s&&($||D||J||F||"auto"!=u[t][y](g.sizesAttr)))&&(a=I[0]||u[t]);a&&!i&&ce(a)}}function me(){K=!1,Q=n.now(),G()}function pe(e){var t=e.target;t._lazyCache?delete t._lazyCache:(he(e),c(t,g.loadedClass),u(t,g.loadingClass),L(t,le),x(t,"lazyloaded"))}function ye(e){var t,a=e[y](g.srcsetAttr);(t=g.customMedia[e[y]("data-media")||e[y]("media")])&&e.setAttribute("media",t),a&&e.setAttribute("srcset",a)}function ve(){3==g.loadMode&&(g.loadMode=2),ue()}function ze(){j||(n.now()-q<999?v(ze,999):(j=!0,g.loadMode=3,se(),e("scroll",ve,!0)))}var be,we,_e,Ce=(we=H(function(e,t,a,i){var r,n,s;if(e._lazysizesWidth=i,e.setAttribute("sizes",i+="px"),w.test(t.nodeName||""))for(n=0,s=(r=t.getElementsByTagName("source")).length;n<s;n++)r[n].setAttribute("sizes",i);a.detail.dataAttr||m(e,a.detail)}),{_:function(){be=f.getElementsByClassName(g.autosizesClass),e("resize",_e)},checkElems:_e=R(function(){var e,t=be.length;if(t)for(e=0;e<t;e++)Ae(be[e])}),updateElem:Ae});function Ae(e,t,a){var i=e.parentNode;i&&(a=N(e,i,a),(t=x(e,"lazybeforesizes",{width:a,dataAttr:!!t})).defaultPrevented||(a=t.detail.width)&&a!==e._lazysizesWidth&&we(e,i,t,a))}function Ee(){!Ee.i&&f.getElementsByClassName&&(Ee.i=!0,Ce._(),fe._())}return v(function(){g.init&&Ee()}),h={cfg:g,autoSizer:Ce,loader:fe,init:Ee,uP:m,aC:c,rC:u,hC:E,fire:x,gW:N,rAF:S}});
  • easy-image-optimizer/tags/4.3.0/phpcs.ruleset.xml

    r3328397 r3398277  
    3232            <element value="imgQuality"/>
    3333            <element value="parentNode"/>
     34            <element value="nodeName"/>
    3435            <element value="nextSibling"/>
    3536            <element value="documentElement"/>
  • easy-image-optimizer/tags/4.3.0/readme.txt

    r3350722 r3398277  
    22Contributors: nosilver4u
    33Tags: image, resize, webp, lazy load, compress
    4 Tested up to: 6.8
    5 Stable tag: 4.2.1
     4Tested up to: 6.9
     5Stable tag: 4.3.0
    66License: GPLv3
    77
     
    5656* If you would like to help translate this plugin in your language, get started here: https://translate.wordpress.org/projects/wp-plugins/easy-image-optimizer/
    5757
     58= 4.3.0 =
     59*Release Date - November 18, 2025*
     60
     61* added: Lazy Load support for background images in external CSS files
     62* added: View CDN bandwidth usage on settings page
     63* changed: Lazy Load checks parent element for skip-lazy class
     64* changed: Lazy Load auto-sizing honors High DPI setting
     65* changed: Easy IO fills in 450px wide image when responsive (srcset) images have a gap
     66* improved: Lazy Load performance when searching for img elements
     67* improved: Lazy Load placeholder generation is faster and works better with Safari
     68* fixed: Lazy Load for iframes breaks WP Remote Users Sync plugin
     69
    5870= 4.2.1 =
    5971*Release Date - August 26, 2025*
  • easy-image-optimizer/trunk/changelog.txt

    r3350722 r3398277  
     1= 4.3.0 =
     2*Release Date - November 18, 2025*
     3
     4* added: Lazy Load support for background images in external CSS files
     5* added: View CDN bandwidth usage on settings page
     6* changed: Lazy Load checks parent element for skip-lazy class
     7* changed: Lazy Load auto-sizing honors High DPI setting
     8* changed: Easy IO fills in 450px wide image when responsive (srcset) images have a gap
     9* improved: Lazy Load performance when searching for img elements
     10* improved: Lazy Load placeholder generation is faster and works better with Safari
     11* fixed: Lazy Load for iframes breaks WP Remote Users Sync plugin
     12
    113= 4.2.1 =
    214*Release Date - August 26, 2025*
  • easy-image-optimizer/trunk/classes/class-base.php

    r3328397 r3398277  
    327327            if ( $this->is_iterable( $potential_logs ) ) {
    328328                foreach ( $potential_logs as $potential_log ) {
    329                     if ( $this->str_ends_with( $potential_log, '.log' ) && false !== strpos( $potential_log, strtolower( __NAMESPACE__ ) . '-debug-' ) && is_file( $this->content_dir . $potential_log ) ) {
     329                    if ( \str_ends_with( $potential_log, '.log' ) && false !== strpos( $potential_log, strtolower( __NAMESPACE__ ) . '-debug-' ) && is_file( $this->content_dir . $potential_log ) ) {
    330330                        return $this->content_dir . $potential_log;
    331331                    }
     
    13491349
    13501350    /**
    1351      * Performs a case-sensitive check indicating if
    1352      * the haystack ends with needle.
    1353      *
    1354      * @param string $haystack The string to search in.
    1355      * @param string $needle   The substring to search for in the `$haystack`.
    1356      * @return bool True if `$haystack` ends with `$needle`, otherwise false.
    1357      */
    1358     public function str_ends_with( $haystack, $needle ) {
    1359         if ( '' === $haystack && '' !== $needle ) {
    1360             return false;
    1361         }
    1362 
    1363         $len = \strlen( $needle );
    1364 
    1365         return 0 === \substr_compare( $haystack, $needle, -$len, $len );
     1351     * Wrapper around size_format to remove the decimal from sizes in bytes.
     1352     *
     1353     * @param int $size A filesize in bytes.
     1354     * @param int $precision Number of places after the decimal separator.
     1355     * @return string Human-readable filesize.
     1356     */
     1357    public function size_format( $size, $precision = 1 ) {
     1358            // Convert it to human readable format.
     1359            $size_str = \size_format( $size, $precision );
     1360            // Remove spaces and extra decimals when measurement is in bytes.
     1361            return \preg_replace( '/\.0+ B ?/', ' B', $size_str );
    13661362    }
    13671363
     
    16331629            return false;
    16341630        }
    1635         if ( 0 === \strpos( $url, '//' ) ) {
     1631        if ( \str_starts_with( $url, '//' ) ) {
    16361632            $url = ( \is_ssl() ? 'https:' : 'http:' ) . $url;
    16371633        }
    1638         if ( false === \strpos( $url, 'http' ) && '/' !== \substr( $url, 0, 1 ) ) {
     1634        if ( ! \str_starts_with( $url, 'http' ) && ! \str_starts_with( $url, '/' ) && ! \str_starts_with( $url, '.' ) ) {
    16391635            $url = ( \is_ssl() ? 'https://' : 'http://' ) . $url;
    16401636        }
  • easy-image-optimizer/trunk/classes/class-exactdn.php

    r3350722 r3398277  
    6565     */
    6666    public $full_width = false;
     67
     68    /**
     69     * Indicates the local domain has changed, and Easy IO should be re-initialized.
     70     *
     71     * @access public
     72     * @var bool $domain_mismatch
     73     */
     74    public $domain_mismatch = false;
    6775
    6876    /**
     
    399407        \add_filter( 'autoptimize_filter_cssjs_multidomain', array( $this, 'add_cdn_domain' ) );
    400408
    401         if ( $this->is_as3cf_cname_active() ) {
    402             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_as3cf_cname_active' );
    403             return;
    404         }
     409        \add_action( 'admin_notices', array( $this, 'admin_notices' ) );
    405410
    406411        $upload_url_parts = $this->parse_url( $this->site_url );
     
    414419            $this->set_exactdn_option( 'local_domain', \base64_encode( $this->upload_domain ) );
    415420            $stored_local_domain = $this->upload_domain;
    416         } elseif ( false !== \strpos( $stored_local_domain, '.' ) ) {
     421        } elseif ( \str_contains( $stored_local_domain, '.' ) ) {
    417422            $this->set_exactdn_option( 'local_domain', \base64_encode( $stored_local_domain ) );
    418         } else {
    419             $stored_local_domain = \base64_decode( $stored_local_domain );
    420         }
    421         $this->debug_message( "saved domain is $stored_local_domain" );
     423        }
     424        $this->debug_message( "saved (local) domain is $stored_local_domain" );
    422425
    423426        $this->debug_message( "allowing images from here: $this->upload_domain" );
     
    433436            $this->debug_message( "removing this from urls: $this->remove_path" );
    434437        }
    435         if (
    436             $stored_local_domain !== $this->upload_domain &&
    437             ! $this->allow_image_domain( $stored_local_domain ) &&
    438             \is_admin()
    439         ) {
    440             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_domain_mismatch' );
    441         }
    442438        $this->allowed_domains[] = $this->exactdn_domain;
    443439        $this->allowed_domains   = \apply_filters( 'exactdn_allowed_domains', $this->allowed_domains );
     
    500496     */
    501497    public function cron_setup( $schedule = true ) {
    502         $this->debug_message( '<b>' . __FUNCTION__ . '()</b>' );
     498        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    503499        $event = 'easyio_verification_checkin';
    504500        // Setup scheduled optimization if the user has enabled it, and it isn't already scheduled.
     
    530526
    531527    /**
     528     * Sends the useragent through filters for http requests to the EWWW IO API.
     529     *
     530     * @param string $useragent The current useragent used in http requests.
     531     * @return string The filtered useragent.
     532     */
     533    public function api_useragent( $useragent ) {
     534        return apply_filters( 'exactdn_api_request_useragent', $useragent );
     535    }
     536
     537    /**
    532538     * Use the Site URL to get the zone domain.
    533539     */
     
    538544            global $exactdn_activate_error;
    539545            $exactdn_activate_error = 'as3cf_cname_active';
    540             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_as3cf_cname_active' );
    541546            return false;
    542547        }
     
    548553            $url = \set_url_scheme( $url, 'https' );
    549554        }
    550         \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     555        \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    551556        $result = \wp_remote_post(
    552557            $url,
     
    564569            global $exactdn_activate_error;
    565570            $exactdn_activate_error = $error_message;
    566             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    567571            return false;
    568572        } elseif ( ! empty( $result['body'] ) && \strpos( $result['body'], 'domain' ) !== false ) {
     
    583587                if ( \get_option( 'exactdn_never_been_active' ) ) {
    584588                    $this->set_option( $this->prefix . 'lazy_load', true );
    585                     $this->set_option( 'exactdn_lossy', true );
    586589                    $this->set_option( 'exactdn_all_the_things', true );
    587590                    \delete_option( 'exactdn_never_been_active' );
     
    598601            global $exactdn_activate_error;
    599602            $exactdn_activate_error = $error_message;
    600             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    601603            return false;
    602604        }
     
    649651            $test_url     = \str_replace( $local_domain, $domain, $test_url );
    650652            $this->debug_message( "test url is $test_url" );
    651             \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     653            \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    652654            $test_result = \wp_remote_post(
    653655                $api_url,
     
    665667                $this->debug_message( "exactdn (1) verification request failed: $error_message" );
    666668                $exactdn_activate_error = $error_message;
    667                 \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    668669                return false;
    669             } elseif ( ! empty( $test_result['body'] ) && false === \strpos( $test_result['body'], 'error' ) ) {
     670            } elseif ( ! empty( $test_result['body'] ) && ! \str_contains( $test_result['body'], 'error' ) ) {
    670671                $response = \json_decode( $test_result['body'], true );
    671672                if ( ! empty( $response['success'] ) ) {
     
    677678                        $this->asset_domains = $response['asset_domains'];
    678679                    }
    679                     \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_success' );
    680680                    return true;
    681681                }
     
    689689                    \delete_site_option( $this->prefix . 'exactdn_domain' );
    690690                }
    691                 \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    692691                return false;
    693692            }
     
    695694                $this->debug_message( 'received response code: ' . $test_result['response']['code'] );
    696695            }
    697             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    698696            return false;
    699697        }
    700698
    701699        // Secondary test against the API db.
    702         \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     700        \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    703701        $result = \wp_remote_post(
    704702            $api_url,
     
    715713            $this->debug_message( "exactdn verification request failed: $error_message" );
    716714            $exactdn_activate_error = $error_message;
    717             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    718715            return false;
    719         } elseif ( ! empty( $result['body'] ) && false === \strpos( $result['body'], 'error' ) ) {
     716        } elseif ( ! empty( $result['body'] ) && ! \str_contains( $result['body'], 'error' ) ) {
    720717            $response = \json_decode( $result['body'], true );
    721718            if ( ! empty( $response['success'] ) ) {
     
    730727                $this->debug_message( 'exactdn verification via API succeeded' );
    731728                $this->set_exactdn_option( 'verified', 1, false );
    732                 if ( empty( $last_checkin ) ) {
    733                     \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_success' );
    734                 }
    735729                return true;
    736730            }
     
    744738                \delete_site_option( $this->prefix . 'exactdn_domain' );
    745739            }
    746             \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    747740            return false;
    748741        }
     
    750743            $this->debug_message( 'received response code: ' . $result['response']['code'] );
    751744        }
    752         \add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
    753745        return false;
    754746    }
     
    762754            // Prelim test with a known valid image to ensure http(s) connectivity.
    763755            $sim_url = 'https://optimize.exactdn.com/exactdn/testorig.jpg';
    764             \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     756            \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    765757            $sim_result = \wp_remote_get( $sim_url );
    766758            if ( \is_wp_error( $sim_result ) ) {
     
    900892        }
    901893        return \update_option( $this->prefix . 'exactdn_' . $option_name, $option_value, $autoload );
     894    }
     895
     896    /**
     897     * Check for conditions that need to trigger an admin notice (action).
     898     */
     899    public function admin_notices() {
     900        if ( $this->is_as3cf_cname_active() ) {
     901            \do_action( 'exactdn_as3cf_cname_active' );
     902        }
     903        $stored_local_domain = $this->get_exactdn_option( 'local_domain' );
     904        $stored_local_domain = \base64_decode( $stored_local_domain );
     905        if (
     906            $stored_local_domain !== $this->upload_domain &&
     907            ! $this->allow_image_domain( $stored_local_domain )
     908        ) {
     909            $this->domain_mismatch = true;
     910            \do_action( 'exactdn_domain_mismatch' );
     911        }
    902912    }
    903913
     
    29522962         * @param array|bool $multipliers Array of multipliers to use or false to bypass.
    29532963         */
    2954         $multipliers = \apply_filters( 'exactdn_srcset_multipliers', array( .2, .4, .6, .8, 1 ) );
     2964        $multipliers = \apply_filters( 'exactdn_srcset_multipliers', array( .2, .4, .6, .8, 1, 450 ) );
    29552965
    29562966        if ( empty( $url ) || empty( $multipliers ) ) {
     
    30393049
    30403050                $newwidth = \intval( $base * $multiplier );
    3041                 if ( 1920 === (int) $multiplier ) {
    3042                     $newwidth = 1920;
    3043                     if ( ! $w_descriptor || 1920 >= $reqwidth || 'soft' !== $crop ) {
     3051                if ( $multiplier > 50 ) { // Not a true multiplier, but a hard-coded width.
     3052                    $newwidth = $multiplier;
     3053                    if ( ! $w_descriptor || $multiplier >= $reqwidth || 'soft' !== $crop ) {
    30443054                        $this->debug_message( "skipping $multiplier due to no w descriptor, larger than $reqwidth, or $crop !== soft" );
    30453055                        continue;
     
    31503160         * @param array|bool $multipliers Array of multipliers to use or false to bypass.
    31513161         */
    3152         $multipliers = \apply_filters( 'exactdn_srcset_multipliers', array( .2, .4, .6, .8, 1 ) );
     3162        $multipliers = \apply_filters( 'exactdn_srcset_multipliers', array( .2, .4, .6, .8, 1, 450 ) );
    31533163        /**
    31543164         * Filter the width ExactDN will use to create srcset attribute.
     
    31753185            foreach ( $multipliers as $multiplier ) {
    31763186                $newwidth = \intval( $width * $multiplier );
    3177                 if ( 1920 === (int) $multiplier ) {
     3187                if ( $multiplier > 50 ) { // Not a true multiplier, but a hard-coded width.
    31783188                    if ( $multiplier >= $width ) {
    31793189                        continue;
    31803190                    }
    3181                     $newwidth = 1920;
     3191                    $newwidth = $multiplier;
    31823192                }
    31833193                if ( $newwidth < 50 ) {
     
    35693579        if ( $this->get_option( 'exactdn_hidpi' ) ) {
    35703580            $this->debug_message( 'adding hidpi multipliers' );
    3571             return array( .2, .4, .6, .8, 1, 2, 3, 1920 );
     3581            $multipliers[] = 2;
     3582            $multipliers[] = 3;
     3583            $multipliers[] = 1920;
    35723584        }
    35733585        return $multipliers;
     
    43174329
    43184330        $more_args = array();
    4319         if ( false === \strpos( $image_url, 'strip=all' ) && $this->get_option( $this->prefix . 'metadata_remove' ) ) {
     4331        if ( ! \str_contains( $image_url, 'strip=all' ) && $this->get_option( $this->prefix . 'metadata_remove' ) ) {
    43204332            $more_args['strip'] = 'all';
    43214333        }
     
    43254337        } elseif ( isset( $args['lossy'] ) && false !== \strpos( $image_url, 'lossy=0' ) ) {
    43264338            unset( $args['lossy'] );
    4327         } elseif ( false === \strpos( $image_url, 'lossy=' ) && ! $this->get_option( 'exactdn_lossy' ) ) {
    4328             $more_args['lossy'] = 0;
    4329         } elseif ( false === \strpos( $image_url, 'lossy=' ) && $this->get_option( 'exactdn_lossy' ) ) {
    4330             $more_args['lossy'] = \is_numeric( $this->get_option( 'exactdn_lossy' ) ) ? (int) $this->get_option( 'exactdn_lossy' ) : 1;
    43314339        }
    43324340        if ( false === \strpos( $image_url, 'quality=' ) && ! \is_null( $jpg_quality ) && 82 !== (int) $jpg_quality ) {
     
    43764384
    43774385        if ( isset( $image_url_parts['scheme'] ) && 'https' === $image_url_parts['scheme'] ) {
    4378             if ( \is_array( $args ) && false === \strpos( $image_url, 'ssl=' ) ) {
    4379                 $this->debug_message( 'adding ssl=1' );
    4380                 $args['ssl'] = 1;
    4381             }
    43824386            $this->debug_message( 'setting scheme to https' );
    43834387            $scheme = 'https';
     
    43974401            foreach ( $args as $arg => $value ) {
    43984402                if ( \is_array( $value ) ) {
    4399                     $args[ $arg ] = \implode( ',', $value );
     4403                    $value        = \implode( ',', $value );
     4404                    $args[ $arg ] = $value;
    44004405                }
    44014406            }
     
    45664571            $url = \set_url_scheme( $url, 'https' );
    45674572        }
    4568         \add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
     4573        \add_filter( 'http_headers_useragent', array( $this, 'api_useragent' ), PHP_INT_MAX );
    45694574        $result = \wp_remote_post(
    45704575            $url,
  • easy-image-optimizer/trunk/classes/class-hs-beacon.php

    r3035873 r3398277  
    101101            return;
    102102        }
    103         if ( \strpos( __FILE__, 'plugins/easy' ) ) {
    104             \easyio_notice_beacon();
    105         }
     103        \do_action( strtolower( __NAMESPACE__ ) . '_beacon_notice' );
    106104    }
    107105}
  • easy-image-optimizer/trunk/classes/class-lazy-load.php

    r3328397 r3398277  
    9696
    9797    /**
     98     * A list of image tags/sections where lazy loading should not be applied.
     99     *
     100     * @access private
     101     * @var array $forbidden_blocks
     102     */
     103    private $forbidden_blocks = array();
     104
     105    /**
    98106     * Request URI.
    99107     *
     
    101109     */
    102110    public $request_uri = '';
     111
     112    /**
     113     * DOM Document for parsing HTML.
     114     *
     115     * @var \DOMDocument $doc
     116     */
     117    private $doc;
     118
     119    /**
     120     * List of img nodes from the DOMDocument.
     121     *
     122     * @var \DOMNodeList $img_nodes
     123     */
     124    private $img_nodes;
    103125
    104126    /**
     
    198220            \add_action( 'wp_enqueue_scripts', array( $this, 'min_script' ), 1 );
    199221        }
     222
     223        // Allow other plugins to get the background image exclusions via filter.
     224        \add_filter( 'eio_get_lazy_bg_image_exclusions', array( $this, 'get_bgimage_exclusions' ), 10 );
     225
    200226        $this->inline_script_attrs = (array) \apply_filters( 'ewwwio_inline_script_attrs', $this->inline_script_attrs );
    201227        $this->validate_user_exclusions();
     
    354380
    355381    /**
     382     * Check if an img/iframe tag should be excluded because it falls within a forbidden block.
     383     *
     384     * @param string $tag The img or iframe tag to check.
     385     * @param int    $position The position of the tag within the HTML.
     386     * @return bool True if it is within a forbidden block, false otherwise.
     387     */
     388    private function is_in_forbidden_block( $tag, $position ) {
     389        if ( empty( $tag ) || empty( $position ) ) {
     390            return false;
     391        }
     392        if ( $this->is_iterable( $this->forbidden_blocks ) ) {
     393            foreach ( $this->forbidden_blocks as $forbidden_block ) {
     394                if ( empty( $forbidden_block[0] ) || empty( $forbidden_block[1] ) ) {
     395                    continue;
     396                }
     397                $start = $forbidden_block[1];
     398                $end   = $start + \strlen( $forbidden_block[0] );
     399                if ( $position > $start && $position < $end ) {
     400                    $this->debug_message( 'tag is within a forbidden block' );
     401                    return true;
     402                }
     403            }
     404        }
     405    }
     406
     407    /**
    356408     * Search for img elements and rewrite them for Lazy Load with fallback to noscript elements.
    357409     *
     
    391443        }
    392444
     445        $started_time     = \microtime( true );
    393446        $above_the_fold   = (int) \apply_filters( 'eio_lazy_fold', $this->get_option( $this->prefix . 'll_abovethefold' ) );
    394447        $images_processed = 0;
    395448        $replacements     = array();
    396449
    397         // Clean the buffer of incompatible sections.
    398         $search_buffer = \preg_replace( '/<div id="footer_photostream".*?\/div>/s', '', $buffer );
    399         $search_buffer = \preg_replace( '/<(picture|noscript|script).*?\/\1>/s', '', $search_buffer );
    400 
    401         $images = $this->get_images_from_html( $search_buffer, false );
     450        // Find all incompatible sections, and store them with their offsets.
     451        $this->forbidden_blocks = array();
     452        \preg_match_all( '/<div id="footer_photostream".*?\/div>/s', $buffer, $forbidden_blocks, PREG_OFFSET_CAPTURE );
     453        \preg_match_all( '/<(picture|noscript|script).*?\/\1>/s', $buffer, $more_forbidden_blocks, PREG_OFFSET_CAPTURE );
     454        if ( ! empty( $forbidden_blocks[0] ) && ! empty( $more_forbidden_blocks[0] ) ) {
     455            $forbidden_blocks[0] = \array_merge( $forbidden_blocks[0], $more_forbidden_blocks[0] );
     456        } elseif ( ! empty( $more_forbidden_blocks[0] ) ) {
     457            $forbidden_blocks = $more_forbidden_blocks;
     458        }
     459        $this->forbidden_blocks = ! empty( $forbidden_blocks[0] ) ? $forbidden_blocks[0] : array();
     460
     461        $this->doc = new \DOMDocument();
     462        libxml_use_internal_errors( true );
     463        $this->doc->loadHTML( $buffer );
     464        libxml_clear_errors();
     465        $this->img_nodes = $this->doc->getElementsByTagName( 'img' );
     466
     467        $images = $this->get_images_from_html( $buffer, false, true, PREG_OFFSET_CAPTURE );
    402468        if ( ! empty( $images[0] ) && $this->is_iterable( $images[0] ) ) {
    403469            foreach ( $images[0] as $index => $image ) {
    404                 $file = $images['img_url'][ $index ];
     470                $file = $images['img_url'][ $index ][0];
    405471                $this->debug_message( "parsing an image: $file" );
    406                 if ( $this->validate_image_tag( $image ) ) {
     472                if ( empty( $image[0] ) || empty( $image[1] ) ) {
     473                    $this->debug_message( 'missing image tag or position' );
     474                    continue;
     475                }
     476                $image_tag = $image[0];
     477                $position  = $image[1];
     478                if ( $this->is_in_forbidden_block( $image_tag, $position ) ) {
     479                    continue;
     480                }
     481                if ( $this->validate_image_tag( $image_tag ) ) {
    407482                    $this->debug_message( 'found a valid image tag' );
    408                     $this->debug_message( "original image tag: $image" );
    409                     $orig_img = $image;
    410                     $ns_img   = $image;
    411                     $image    = $this->parse_img_tag( $image, $file );
     483                    $this->debug_message( "original image tag: $image_tag" );
     484                    $orig_img  = $image_tag;
     485                    $ns_img    = $image_tag;
     486                    $image_tag = $this->parse_img_tag( $image_tag, $file );
    412487                    $this->set_attribute( $ns_img, 'data-eio', 'l', true );
    413488                    $noscript = '<noscript>' . $ns_img . '</noscript>';
    414                     $position = \strpos( $buffer, $orig_img );
    415                     if ( $position && $orig_img !== $image ) {
     489                    if ( $position && $orig_img !== $image_tag ) {
     490                        $this->debug_message( "lazified image at position $position" );
    416491                        $replacements[ $position ] = array(
    417492                            'orig' => $orig_img,
    418                             'lazy' => $image . $noscript,
     493                            'lazy' => $image_tag . $noscript,
    419494                        );
    420495                    }
    421                     /* $buffer   = str_replace( $orig_img, $image . $noscript, $buffer ); */
    422496                }
    423497            } // End foreach().
    424498        } // End if().
     499
    425500        $element_types = \apply_filters( 'eio_allowed_background_image_elements', array( 'div', 'li', 'span', 'section', 'a' ) );
    426501        foreach ( $element_types as $element_type ) {
     
    435510            }
    436511        }
     512
    437513        if ( \in_array( 'picture', $this->user_element_exclusions, true ) ) {
    438514            $pictures = '';
    439515        } else {
    440516            // Images listed as picture/source elements. Mostly for NextGEN, but should work anywhere.
    441             $pictures = $this->get_picture_tags_from_html( $buffer );
     517            $pictures = $this->get_picture_tags_from_html( $buffer, PREG_OFFSET_CAPTURE );
    442518        }
    443519        if ( $this->is_iterable( $pictures ) ) {
    444520            foreach ( $pictures as $index => $picture ) {
    445                 if ( ! $this->validate_image_tag( $picture ) ) {
    446                     continue;
    447                 }
    448                 $pimages = $this->get_images_from_html( $picture, false );
     521                if ( empty( $picture[0] ) || empty( $picture[1] ) ) {
     522                    $this->debug_message( 'missing picture tag or position' );
     523                    continue;
     524                }
     525                $picture_tag = $picture[0];
     526                $position    = $picture[1];
     527                if ( ! $this->validate_image_tag( $picture_tag ) ) {
     528                    continue;
     529                }
     530                $pimages = $this->get_images_from_html( $picture_tag, false );
    449531                if ( ! empty( $pimages[0] ) && $this->is_iterable( $pimages[0] ) && ! empty( $pimages[0][0] ) ) {
    450532                    $image = $pimages[0][0];
     
    458540                        $image    = $this->parse_img_tag( $image, $file );
    459541                        $this->set_attribute( $ns_img, 'data-eio', 'l', true );
    460                         $noscript = '<noscript>' . $ns_img . '</noscript>';
    461                         $picture  = \str_replace( $orig_img, $image, $picture ) . $noscript;
     542                        $noscript    = '<noscript>' . $ns_img . '</noscript>';
     543                        $picture_tag = \str_replace( $orig_img, $image, $picture_tag ) . $noscript;
    462544                    }
    463545                } else {
    464546                    continue;
    465547                }
    466                 $sources = $this->get_elements_from_html( $picture, 'source' );
     548                $sources = $this->get_elements_from_html( $picture_tag, 'source' );
    467549                if ( $this->is_iterable( $sources ) ) {
    468550                    foreach ( $sources as $source ) {
     
    477559                            $this->set_attribute( $lazy_source, 'data-srcset', $srcset );
    478560                            $this->remove_attribute( $lazy_source, 'srcset' );
    479                             $picture = \str_replace( $source, $lazy_source, $picture );
     561                            $picture_tag = \str_replace( $source, $lazy_source, $picture_tag );
    480562                        }
    481563                    }
    482                     $position = \strpos( $buffer, $pictures[ $index ] );
    483                     if ( $position && $picture !== $pictures[ $index ] ) {
     564                    if ( $position && $picture_tag !== $pictures[ $index ][0] ) {
    484565                        $this->debug_message( 'lazified sources for picture element' );
    485566                        $replacements[ $position ] = array(
    486                             'orig' => $pictures[ $index ],
    487                             'lazy' => $picture,
     567                            'orig' => $pictures[ $index ][0],
     568                            'lazy' => $picture_tag,
    488569                        );
    489                         /* $buffer = str_replace( $pictures[ $index ], $picture, $buffer ); */
    490                     }
    491                 }
    492             }
    493         }
     570                    }
     571                }
     572            }
     573        }
     574
    494575        // Iframe elements, looking for stuff like YouTube embeds.
    495576        if ( \in_array( 'iframe', $this->user_element_exclusions, true ) ) {
    496577            $frames = '';
    497578        } else {
    498             $frames = $this->get_elements_from_html( $search_buffer, 'iframe' );
     579            $frames = $this->get_elements_from_html( $buffer, 'iframe', PREG_OFFSET_CAPTURE );
    499580        }
    500581        if ( $this->is_iterable( $frames ) ) {
    501             foreach ( $frames as $index => $frame ) {
     582            foreach ( $frames as $index => $frame_data ) {
     583                if ( empty( $frame_data[0] ) || empty( $frame_data[1] ) ) {
     584                    $this->debug_message( 'missing iframe tag or position' );
     585                    continue;
     586                }
    502587                $this->debug_message( 'parsing an iframe element' );
     588                $frame    = $frame_data[0];
     589                $position = $frame_data[1];
     590                if ( $this->is_in_forbidden_block( $frame, $position ) ) {
     591                    continue;
     592                }
    503593                $url = $this->get_attribute( $frame, 'src' );
    504594                if ( $url && 0 === \strpos( $url, 'http' ) && $this->validate_iframe_tag( $frame ) ) {
     
    507597                    $this->remove_attribute( $frame, 'src' );
    508598                    $this->set_attribute( $frame, 'class', \trim( $this->get_attribute( $frame, 'class' ) . ' lazyload' ), true );
    509                     if ( $frame !== $frames[ $index ] ) {
    510                         $buffer = \str_replace( $frames[ $index ], $frame, $buffer );
    511                     }
    512                 }
    513             }
    514         }
     599                    if ( $frame !== $frames[ $index ][0] ) {
     600                        $buffer = \str_replace( $frames[ $index ][0], $frame, $buffer );
     601                    }
     602                }
     603            }
     604        }
     605
    515606        if ( $this->is_iterable( $replacements ) ) {
    516607            \ksort( $replacements );
     
    534625            }
    535626        }
    536         $this->debug_message( 'all done parsing page for lazy' );
     627
     628        $elapsed_time = \microtime( true ) - $started_time;
     629        $this->debug_message( "all done parsing page for lazy in $elapsed_time seconds" );
    537630        return $buffer;
    538631    }
     
    796889            return $replacements;
    797890        }
    798         $elements = $this->get_elements_from_html( \preg_replace( '/<(noscript|script).*?\/\1>/s', '', $buffer ), $tag_type );
     891        $elements = $this->get_elements_from_html( $buffer, $tag_type, PREG_OFFSET_CAPTURE );
    799892        if ( $this->is_iterable( $elements ) ) {
    800             foreach ( $elements as $index => $element ) {
     893            foreach ( $elements as $index => $element_data ) {
     894                if ( empty( $element_data[0] ) || empty( $element_data[1] ) ) {
     895                    $this->debug_message( 'missing element or position' );
     896                    continue;
     897                }
     898                $element  = $element_data[0];
     899                $position = $element_data[1];
     900                if ( $this->is_in_forbidden_block( $element, $position ) ) {
     901                    continue;
     902                }
    801903                $this->debug_message( "parsing a $tag_type" );
    802                 if ( false === \strpos( $element, 'background:' ) && false === \strpos( $element, 'background-image:' ) ) {
     904                if ( ! \str_contains( $element, 'background:' ) && ! \str_contains( $element, 'background-image:' ) ) {
    803905                    $element = $this->lazify_element( $element );
    804                     if ( $element !== $elements[ $index ] ) {
     906                    if ( $element !== $elements[ $index ][0] ) {
    805907                        $this->debug_message( "$tag_type lazified, replacing in html source" );
    806                         $buffer = \str_replace( $elements[ $index ], $element, $buffer );
    807                     }
    808                     continue;
    809                 }
    810                 if ( false !== \strpos( $element, '--background' ) ) {
     908                        $buffer = \str_replace( $elements[ $index ][0], $element, $buffer );
     909                    }
     910                    continue;
     911                }
     912                if ( \str_contains( $element, '--background' ) ) {
    811913                    $this->set_attribute( $element, 'class', $this->get_attribute( $element, 'class' ) . ' lazyload', true );
    812                     if ( $element !== $elements[ $index ] ) {
     914                    if ( $element !== $elements[ $index ][0] ) {
    813915                        $this->debug_message( "$tag_type with bg var lazified, replacing in html source" );
    814                         $buffer = \str_replace( $elements[ $index ], $element, $buffer );
     916                        $buffer = \str_replace( $elements[ $index ][0], $element, $buffer );
    815917                    }
    816918                    continue;
     
    853955                            }
    854956                        } elseif ( ! empty( $bg_image_urls[0] ) ) {
     957                            list( $physical_width, $physical_height ) = $this->get_image_dimensions_by_url( $bg_image_urls[0] );
     958                            $this->set_attribute( $element, 'data-back', $bg_image_urls[0] );
     959                            if ( $physical_width && $physical_height ) {
     960                                $this->set_attribute( $element, 'data-eio-rwidth', $physical_width, true );
     961                                $this->set_attribute( $element, 'data-eio-rheight', $physical_height, true );
     962                            }
    855963                            $webp_image_url = \apply_filters( 'eio_image_url_to_webp', $bg_image_urls[0] );
    856                             $this->set_attribute( $element, 'data-back', $bg_image_urls[0] );
    857964                            if ( $webp_image_url && $webp_image_url !== $bg_image_urls[0] ) {
    858965                                $this->set_attribute( $element, 'data-back-webp', $webp_image_url );
     
    862969                    }
    863970                }
    864                 $position = \strpos( $buffer, $elements[ $index ] );
    865                 if ( $position && $element !== $elements[ $index ] ) {
     971                if ( $position && $element !== $elements[ $index ][0] ) {
    866972                    $this->debug_message( "$tag_type modified, replacing in html source" );
    867973                    $replacements[ $position ] = array(
    868                         'orig' => $elements[ $index ],
     974                        'orig' => $elements[ $index ][0],
    869975                        'lazy' => $element,
    870976                    );
    871                     /* $buffer = str_replace( $elements[ $index ], $element, $buffer ); */
    872977                }
    873978            }
     
    10181123
    10191124    /**
     1125     * Normalize HTML for comparison.
     1126     *
     1127     * @param string $html The HTML to normalize.
     1128     * @return string The normalized HTML.
     1129     */
     1130    public function normalize_html( $html ) {
     1131        $html = str_replace( '&amp;', '&', $html );
     1132        $html = str_replace( '&#038;', '&', $html );
     1133        $html = str_replace( '%2C', ',', $html );
     1134        $html = str_replace( ' />', '>', $html );
     1135        $html = str_replace( "'", '"', $html );
     1136        $html = preg_replace( '/\s\s+/', ' ', $html );
     1137        $html = preg_replace( '/\s*=\s*/', '=', $html );
     1138        return $html;
     1139    }
     1140
     1141    /**
    10201142     * Checks if the tag is allowed to be lazy loaded.
    10211143     *
     
    10561178            }
    10571179        }
    1058 
     1180        // Check for exclusions.
    10591181        $exclusions = \apply_filters(
    10601182            'eio_lazy_exclusions',
     
    10891211                ),
    10901212                $this->user_exclusions
    1091             ),
    1092             $image
     1213            )
    10931214        );
    10941215        foreach ( $exclusions as $exclusion ) {
     
    11061227            }
    11071228        }
     1229
     1230        foreach ( $this->img_nodes as $img_node ) {
     1231            $img_html = $this->doc->saveHTML( $img_node );
     1232            if ( defined( 'EIO_IMGNODE_DEBUG' ) && EIO_IMGNODE_DEBUG ) {
     1233                $this->debug_message( 'comparing to node value: ' . $this->normalize_html( $img_html ) . ' to ' . $this->normalize_html( $image ) );
     1234            }
     1235            // Normalize the HTML before comparing to avoid issues with different quote styles or spacing.
     1236            if ( $this->normalize_html( $img_html ) === $this->normalize_html( $image ) ) {
     1237                $parent = $img_node->parentNode;
     1238                if ( $parent && 'body' !== $parent->nodeName && $parent->hasAttributes() ) {
     1239                    $class = trim( $parent->getAttribute( 'class' ) );
     1240                    $this->debug_message( "Parent class: $class" );
     1241                    if ( str_contains( $class, 'skip-lazy' ) ) {
     1242                        $this->debug_message( "Skipping lazy load due to 'skip-lazy' class on parent" );
     1243                        return false;
     1244                    }
     1245                    if ( $parent->hasAttribute( 'data-skip-lazy' ) ) {
     1246                        $this->debug_message( "Skipping lazy load due to 'data-skip-lazy' attribute on parent" );
     1247                        return false;
     1248                    }
     1249                }
     1250            }
     1251        }
     1252
    11081253        return true;
    11091254    }
    11101255
    11111256    /**
    1112      * Checks if a tag with a background image is allowed to be lazy loaded.
    1113      *
    1114      * @param string $tag The tag.
    1115      * @return bool True if the tag is allowed, false otherwise.
    1116      */
    1117     public function validate_bgimage_tag( $tag ) {
    1118         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     1257     * Gets the exclusion list for lazy loading background images.
     1258     *
     1259     * @param array $exclusions The current list of exclusions. Optional.
     1260     * @return array The modified list of exclusions.
     1261     */
     1262    public function get_bgimage_exclusions( $exclusions = array() ) {
     1263        if ( ! \is_array( $exclusions ) ) {
     1264            $exclusions = array();
     1265        }
    11191266        $exclusions = \apply_filters(
    11201267            'eio_lazy_bg_image_exclusions',
     
    11271274                    'skip-lazy',
    11281275                    'avia-bg-style-fixed',
     1276                    'trustindex',
     1277                ),
     1278                $this->user_exclusions,
     1279                $exclusions
     1280            )
     1281        );
     1282        return $exclusions;
     1283    }
     1284
     1285    /**
     1286     * Checks if a tag with a background image is allowed to be lazy loaded.
     1287     *
     1288     * @param string $tag The tag.
     1289     * @return bool True if the tag is allowed, false otherwise.
     1290     */
     1291    public function validate_bgimage_tag( $tag ) {
     1292        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     1293        $exclusions = $this->get_bgimage_exclusions( array() );
     1294        foreach ( $exclusions as $exclusion ) {
     1295            if ( false !== \strpos( $tag, $exclusion ) ) {
     1296                return false;
     1297            }
     1298        }
     1299        return true;
     1300    }
     1301
     1302    /**
     1303     * Checks if an iframe tag is allowed to be lazy loaded.
     1304     *
     1305     * @param string $tag The tag.
     1306     * @return bool True if the tag is allowed, false otherwise.
     1307     */
     1308    public function validate_iframe_tag( $tag ) {
     1309        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     1310        $exclusions = \apply_filters(
     1311            'eio_lazy_iframe_exclusions',
     1312            \array_merge(
     1313                array(
     1314                    'about:blank',
     1315                    'data-no-lazy=',
     1316                    'display:none',
     1317                    'display: none',
     1318                    'googletagmanager',
     1319                    'lazyload',
     1320                    'skip-lazy',
     1321                    'wprus/',
     1322                    'vimeo',
    11291323                ),
    11301324                $this->user_exclusions
     
    11411335
    11421336    /**
    1143      * Checks if an iframe tag is allowed to be lazy loaded.
    1144      *
    1145      * @param string $tag The tag.
    1146      * @return bool True if the tag is allowed, false otherwise.
    1147      */
    1148     public function validate_iframe_tag( $tag ) {
    1149         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    1150         $exclusions = \apply_filters(
    1151             'eio_lazy_iframe_exclusions',
    1152             \array_merge(
    1153                 array(
    1154                     'data-no-lazy=',
    1155                     'lazyload',
    1156                     'skip-lazy',
    1157                     'vimeo',
    1158                     'about:blank',
    1159                     'googletagmanager',
    1160                 ),
    1161                 $this->user_exclusions
    1162             ),
    1163             $tag
    1164         );
    1165         foreach ( $exclusions as $exclusion ) {
    1166             if ( false !== \strpos( $tag, $exclusion ) ) {
    1167                 return false;
    1168             }
    1169         }
    1170         return true;
    1171     }
    1172 
    1173     /**
    11741337     * Build a PNG inline image placeholder.
    11751338     *
     
    12111374            return $exactdn->generate_url( $this->content_url . 'lazy/placeholder-' . $width . 'x' . $height . '.png' );
    12121375        } elseif ( ! is_file( $piip_path ) ) {
    1213             // First try PIP generation via Imagick, as it is pretty efficient.
    1214             if ( $this->imagick_support() ) {
    1215                 $placeholder = new \Imagick();
    1216                 $placeholder->newimage( $width, $height, 'transparent' );
    1217                 $placeholder->setimageformat( 'PNG' );
    1218                 $placeholder->stripimage();
    1219                 $placeholder->writeimage( $piip_path );
    1220                 $placeholder->clear();
     1376            // First, try GD and then optimize it with optipng/pngout if available.
     1377            // This is now the first attempt, as the Imagick placeholders are grayscale and may not work in Safari, and GD is somehow faster.
     1378            if (
     1379                $this->gd_support() &&
     1380                $this->check_memory_available( $width * $height * 4.8 ) // 4.8 = 24-bit or 3 bytes per pixel multiplied by a factor of 1.6 for extra wiggle room.
     1381            ) {
     1382                $img   = \imagecreatetruecolor( $width, $height );
     1383                $color = \imagecolorallocatealpha( $img, 0, 0, 0, 127 );
     1384                \imagefill( $img, 0, 0, $color );
     1385                \imagesavealpha( $img, true );
     1386                \imagecolortransparent( $img, \imagecolorat( $img, 0, 0 ) );
     1387                \imagetruecolortopalette( $img, false, 1 );
     1388                \imagepng( $img, $piip_path, 9 );
     1389                if (
     1390                    \function_exists( '\ewww_image_optimizer' ) &&
     1391                    \function_exists( '\ewwwio' ) &&
     1392                    ! \ewwwio()->get_option( 'ewww_image_optimizer_cloud_key' ) &&
     1393                    \ewwwio()->local->exec_check()
     1394                ) {
     1395                    \ewww_image_optimizer( $piip_path );
     1396                }
    12211397            }
    12221398            // If that didn't work, and we have a premium service, use the API to generate the slimmest PIP available.
     
    12341410                }
    12351411            }
    1236             // Last shot, use GD and then optimize it with optipng/pngout if available.
     1412            // Last resort, do PIP generation via Imagick, as it is pretty efficient even though Safari doesn't like the grayscale images it produces.
    12371413            if (
    12381414                ! \is_file( $piip_path ) &&
    1239                 $this->gd_support() &&
    1240                 $this->check_memory_available( $width * $height * 4.8 ) // 4.8 = 24-bit or 3 bytes per pixel multiplied by a factor of 1.6 for extra wiggle room.
     1415                $this->imagick_support()
    12411416            ) {
    1242                 $img   = \imagecreatetruecolor( $width, $height );
    1243                 $color = \imagecolorallocatealpha( $img, 0, 0, 0, 127 );
    1244                 \imagefill( $img, 0, 0, $color );
    1245                 \imagesavealpha( $img, true );
    1246                 \imagecolortransparent( $img, \imagecolorat( $img, 0, 0 ) );
    1247                 \imagetruecolortopalette( $img, false, 1 );
    1248                 \imagepng( $img, $piip_path, 9 );
    1249                 if ( \function_exists( '\ewww_image_optimizer' ) ) {
    1250                     \ewww_image_optimizer( $piip_path );
    1251                 }
     1417                $placeholder = new \Imagick();
     1418                $placeholder->newimage( $width, $height, 'transparent' );
     1419                $placeholder->setimageformat( 'PNG' );
     1420                $placeholder->stripimage();
     1421                $placeholder->writeimage( $piip_path );
     1422                $placeholder->clear();
    12521423            }
    12531424        }
     
    13821553                        'exactdn_domain' => ( $this->parsing_exactdn ? $this->exactdn_domain : '' ),
    13831554                        'skip_autoscale' => ( \defined( 'EIO_LL_AUTOSCALE' ) && ! EIO_LL_AUTOSCALE ? 1 : 0 ),
     1555                        'bg_min_dpr'     => ( \defined( 'EIO_LL_BG_MIN_DPR' ) && EIO_LL_BG_MIN_DPR ? EIO_LL_BG_MIN_DPR : 1.1 ),
    13841556                        'threshold'      => (int) $threshold > 50 ? (int) $threshold : 0,
    13851557                        'use_dpr'        => (int) $this->get_option( 'exactdn_hidpi' ),
     
    14231595                        'exactdn_domain' => ( $this->parsing_exactdn ? $this->exactdn_domain : '' ),
    14241596                        'skip_autoscale' => ( \defined( 'EIO_LL_AUTOSCALE' ) && ! EIO_LL_AUTOSCALE ? 1 : 0 ),
     1597                        'bg_min_dpr'     => ( \defined( 'EIO_LL_BG_MIN_DPR' ) && EIO_LL_BG_MIN_DPR ? EIO_LL_BG_MIN_DPR : 1.1 ),
    14251598                        'threshold'      => (int) $threshold > 50 ? (int) $threshold : 0,
    14261599                        'use_dpr'        => (int) $this->get_option( 'exactdn_hidpi' ),
  • easy-image-optimizer/trunk/classes/class-page-parser.php

    r3328397 r3398277  
    5757     * @param bool   $hyperlinks Default true. Should we include encasing hyperlinks in our search.
    5858     * @param bool   $src_required Default true. Should we look only for images with src attributes.
     59     * @param int    $flags Optional. Flags passed to preg_match_all. Default 0.
    5960     * @return array An array of $images matches, where $images[0] is
    6061     *         an array of full matches, and the link_url, img_tag,
    6162     *         and img_url keys are arrays of those matches.
    6263     */
    63     public function get_images_from_html( $content, $hyperlinks = true, $src_required = true ) {
     64    public function get_images_from_html( $content, $hyperlinks = true, $src_required = true, $flags = 0 ) {
    6465        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    6566        $images          = array();
     
    8081            $unquoted_pattern = '#(?P<img_tag><img[^>]*?\s+?src\s*=\s*(?P<img_url>[^"\'\\\\<>][^\s\\\\<>]+)(?:\s[^>]*?)?>)#is';
    8182        }
    82         if ( \preg_match_all( $search_pattern, $content, $images ) ) {
     83        if ( \preg_match_all( $search_pattern, $content, $images, $flags ) ) {
    8384            $this->debug_message( 'found ' . \count( $images[0] ) . ' image elements with quoted pattern' );
    8485            foreach ( $images as $key => $unused ) {
     
    9192        }
    9293        $images = \array_filter( $images );
    93         if ( $unquoted_pattern && \preg_match_all( $unquoted_pattern, $content, $unquoted_images ) ) {
     94        if ( $unquoted_pattern && \preg_match_all( $unquoted_pattern, $content, $unquoted_images, $flags ) ) {
    9495            $this->debug_message( 'found ' . \count( $unquoted_images[0] ) . ' image elements with unquoted pattern' );
    9596            foreach ( $unquoted_images as $key => $unused ) {
     
    147148     *
    148149     * @param string $content Some HTML.
     150     * @param int    $flags Optional. Flags passed to preg_match_all. Default 0.
    149151     * @return array An array of $pictures matches, containing full elements with ending tags.
    150152     */
    151     public function get_picture_tags_from_html( $content ) {
     153    public function get_picture_tags_from_html( $content, $flags = 0 ) {
    152154        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    153155        $pictures = array();
    154         if ( ! empty( $content ) && \preg_match_all( '#(?:<picture[^>]*?>\s*)(?:<source[^>]*?>)+(?:.*?</picture>)?#is', $content, $pictures ) ) {
     156        if ( ! empty( $content ) && \preg_match_all( '#(?:<picture[^>]*?>\s*)(?:<source[^>]*?>)+(?:.*?</picture>)?#is', $content, $pictures, $flags ) ) {
    155157            return $pictures[0];
    156158        }
     
    212214     * @param string $content Some HTML.
    213215     * @param string $tag_name The name of the elements to retrieve.
     216     * @param int    $flags Optional. Flags passed to preg_match_all. Default 0.
    214217     * @return array An array of $elements.
    215218     */
    216     public function get_elements_from_html( $content, $tag_name ) {
    217         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    218         if ( ! \ctype_alpha( str_replace( '-', '', $tag_name ) ) ) {
     219    public function get_elements_from_html( $content, $tag_name, $flags = 0 ) {
     220        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     221        if ( ! \ctype_alpha( \str_replace( '-', '', $tag_name ) ) ) {
    219222            return array();
    220223        }
    221         if ( ! empty( $content ) && \preg_match_all( '#<' . $tag_name . '\s[^\\\\>]+?>#is', $content, $elements ) ) {
     224        if ( ! empty( $content ) && \preg_match_all( '#<' . $tag_name . '\s[^\\\\>]+?>#is', $content, $elements, $flags ) ) {
    222225            return $elements[0];
    223226        }
  • easy-image-optimizer/trunk/classes/class-plugin.php

    r3197623 r3398277  
    2828
    2929    /**
     30     * Buffer object.
     31     *
     32     * @var object|\EasyIO\Buffer $buffer
     33     */
     34    public $buffer;
     35
     36    /**
     37     * Lazy Load object.
     38     *
     39     * @var object|\EasyIO\Lazy_Load $lazy_load
     40     */
     41    public $lazy_load;
     42
     43    /**
    3044     * Helpscout Beacon object.
    3145     *
     
    3347     */
    3448    public $hs_beacon;
     49
     50    /**
     51     * Settings object.
     52     *
     53     * @var object|\EasyIO\Settings $settings
     54     */
     55    public $settings;
    3556
    3657    /**
     
    5071            self::$instance->requires();
    5172            self::$instance->load_children();
     73
     74            if ( ! self::$instance->php_supported() ) {
     75                return self::$instance;
     76            }
     77            if ( ! self::$instance->wp_supported() ) {
     78                return self::$instance;
     79            }
     80
     81            // Load plugin components that need to be available early.
     82            \add_action( 'plugins_loaded', array( self::$instance, 'plugins_loaded' ) );
     83            // Setup page parsing, if parsers are enabled.
     84            \add_action( 'init', array( self::$instance, 'parser_init' ), 99 );
    5285            // Initializes the plugin for admin interactions, like saving network settings and scheduling cron jobs.
    5386            \add_action( 'admin_init', array( self::$instance, 'admin_init' ) );
    5487
    55             // TODO: check PHP and WP compat here.
    56             // TODO: setup anything that needs to run on init/plugins_loaded.
    57             // TODO: add any custom option/setting hooks here (actions that need to be taken when certain settings are saved/updated).
     88            // Filters to set default permissions, admins can override these if they wish.
     89            \add_filter( 'easyio_admin_permissions', array( self::$instance, 'admin_permissions' ), 8 );
     90            \add_filter( 'easyio_superadmin_permissions', array( self::$instance, 'superadmin_permissions' ), 8 );
     91
     92            // Add Easy IO version to useragent for API requests.
     93            \add_filter( 'exactdn_api_request_useragent', array( self::$instance, 'api_useragent' ) );
     94            // Check the current screen ID to see if temp debugging should still be enabled.
     95            \add_action( 'current_screen', array( self::$instance, 'current_screen' ), 10, 1 );
     96            // Disable core WebP generation since we already do that.
     97            \add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
     98            // Makes sure we flush the debug info to the log on shutdown.
     99            \add_action( 'shutdown', array( self::$instance, 'debug_log' ) );
    58100        }
    59101
     
    86128     */
    87129    private function requires() {
     130        // Sets up the settings page and various option-related hooks/functions.
     131        require_once EASYIO_PLUGIN_PATH . 'classes/class-settings.php';
     132        // Starts the HTML buffer for all other functions to parse.
     133        require_once EASYIO_PLUGIN_PATH . 'classes/class-buffer.php';
     134        // Page Parsing class for working with HTML content.
     135        require_once EASYIO_PLUGIN_PATH . 'classes/class-page-parser.php';
     136        // Lazy Load class for parsing image urls and deferring off-screen images.
     137        require_once EASYIO_PLUGIN_PATH . 'classes/class-lazy-load.php';
    88138        // EasyIO\HS_Beacon class for integrated help/docs.
    89139        require_once EASYIO_PLUGIN_PATH . 'classes/class-hs-beacon.php';
     
    94144     */
    95145    public function load_children() {
    96         /* self::$instance->class = new Class(); */
     146        self::$instance->settings = new Settings();
     147    }
     148
     149    /**
     150     * Make sure we are on a supported version of PHP.
     151     *
     152     * @access private
     153     */
     154    private function php_supported() {
     155        if ( defined( 'PHP_VERSION_ID' ) && PHP_VERSION_ID >= 80100 ) {
     156            return true;
     157        }
     158        \add_action( 'network_admin_notices', array( self::$instance, 'unsupported_php_notice' ) );
     159        \add_action( 'admin_notices', array( self::$instance, 'unsupported_php_notice' ) );
     160        return false;
     161    }
     162
     163    /**
     164     * Make sure we are on a supported version of WordPress.
     165     *
     166     * @access private
     167     */
     168    private function wp_supported() {
     169        global $wp_version;
     170        if ( \version_compare( $wp_version, '6.6' ) >= 0 ) {
     171            return true;
     172        }
     173        \add_action( 'network_admin_notices', array( self::$instance, 'unsupported_wp_notice' ) );
     174        \add_action( 'admin_notices', array( self::$instance, 'unsupported_wp_notice' ) );
     175        return false;
     176    }
     177
     178    /**
     179     * Display a notice that the PHP version is too old.
     180     */
     181    public function unsupported_php_notice() {
     182        echo '<div id="easyio-warning-php" class="error"><p><a href="https://docs.ewww.io/article/55-upgrading-php" target="_blank" data-beacon-article="5ab2baa6042863478ea7c2ae">' . esc_html__( 'Easy Image Optimizer requires PHP 8.1 or greater. Newer versions of PHP are faster and more secure. If you are unsure how to upgrade to a supported version, ask your webhost for instructions.', 'easy-image-optimizer' ) . '</a></p></div>';
     183    }
     184
     185    /**
     186     * Display a notice that the WP version is too old.
     187     */
     188    public function unsupported_wp_notice() {
     189        echo '<div id="swis-warning-wp" class="notice notice-error"><p>' . esc_html__( 'Easy Image Optimizer requires WordPress 6.6 or greater, please update your website.', 'easy-image-optimizer' ) . '</p></div>';
     190    }
     191
     192    /**
     193     * Run things that need to go early, on plugins_loaded.
     194     */
     195    public function plugins_loaded() {
     196        $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
     197
     198        if ( $this->get_option( 'easyio_lazy_load' ) && $this->get_option( 'easyio_ll_external_bg' ) ) {
     199            $this->debug_message( 'requesting external parsing of CSS for background images via SWIS' );
     200            add_filter( 'eio_lazify_external_css', '__return_true' );
     201        }
     202    }
     203
     204    /**
     205     * Setup page parsing classes after theme functions.php is loaded and plugins have run init routines.
     206     */
     207    public function parser_init() {
     208        $buffer_start = false;
     209        // If ExactDN is enabled.
     210        if ( $this->get_option( 'easyio_exactdn' ) && ! \str_contains( \add_query_arg( '', '' ), 'exactdn_disable=1' ) ) {
     211            $buffer_start = true;
     212
     213            // ExactDN class for parsing image urls and rewriting them.
     214            require_once EASYIO_PLUGIN_PATH . 'classes/class-exactdn.php';
     215        }
     216        // If Lazy Load is enabled.
     217        if ( $this->get_option( 'easyio_lazy_load' ) ) {
     218            $buffer_start = true;
     219
     220            $this->lazy_load = new Lazy_Load();
     221        }
     222        if ( $buffer_start ) {
     223            // Start an output buffer before any output starts.
     224            $this->buffer = new Buffer();
     225        }
    97226    }
    98227
     
    102231    public function admin_init() {
    103232        $this->hs_beacon = new HS_Beacon();
    104         \easyio_upgrade();
    105         $this->register_settings();
    106233
    107234        if ( ! \class_exists( __NAMESPACE__ . '\ExactDN' ) || ! $this->get_option( 'easyio_exactdn' ) ) {
    108             add_action( 'network_admin_notices', 'easyio_notice_inactive' );
    109             add_action( 'admin_notices', 'easyio_notice_inactive' );
    110         }
     235            \add_action( 'network_admin_notices', array( $this, 'service_inactive_notice' ) );
     236            \add_action( 'admin_notices', array( $this, 'service_inactive_notice' ) );
     237        }
     238
     239        \add_action( 'exactdn_as3cf_cname_active', array( $this, 'exactdn_as3cf_cname_active_notice' ) );
     240        \add_action( 'exactdn_domain_mismatch', array( $this, 'exactdn_domain_mismatch_notice' ) );
     241
     242        \add_action( 'easyio_beacon_notice', array( $this, 'hs_beacon_notice' ) );
     243
    111244        // Prevent ShortPixel AIO messiness.
    112245        \remove_action( 'admin_notices', 'autoptimizeMain::notice_plug_imgopt' );
     
    117250                $ao_extra['autoptimize_imgopt_checkbox_field_1'] = 0;
    118251                \update_option( 'autoptimize_imgopt_settings', $ao_extra );
    119                 \add_action( 'admin_notices', 'easyio_notice_sp_conflict' );
     252                \add_action( 'admin_notices', array( $this, 'sp_conflict_notice' ) );
    120253            }
    121254        }
     
    124257            if ( $this->get_option( 'easyio_exactdn' ) && \HMWP_Classes_Tools::getOption( 'hmwp_hide_version' ) && ! \HMWP_Classes_Tools::getOption( 'hmwp_hide_version_random' ) ) {
    125258                $this->debug_message( 'detected HMWP Hide Version' );
    126                 \add_action( 'admin_notices', array( $this, 'notice_hmwp_hide_version' ) );
     259                \add_action( 'admin_notices', array( $this, 'hmwp_hide_version_notice' ) );
    127260            }
    128261        }
    129262
    130         if ( ! \defined( '\WP_CLI' ) || ! WP_CLI ) {
    131             \easyio_privacy_policy_content();
    132         }
    133     }
    134 
    135     /**
    136      * Save the multi-site settings, if this is the WP admin, and they've been POSTed.
    137      */
    138     public function save_network_settings() {
    139         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    140         // NOTE: we don't actually have a network settings screen, so...
    141         if ( ! \function_exists( 'is_plugin_active_for_network' ) && \is_multisite() ) {
    142             // Need to include the plugin library for the is_plugin_active function.
    143             require_once ABSPATH . 'wp-admin/includes/plugin.php';
    144         }
    145             // Set the common network settings if they have been POSTed.
    146         if (
    147             \is_multisite() &&
    148             \is_plugin_active_for_network( EASYIO_PLUGIN_FILE_REL ) &&
    149             ! empty( $_REQUEST['_wpnonce'] ) &&
    150             isset( $_POST['option_page'] ) &&
    151             false !== \strpos( sanitize_text_field( wp_unslash( $_POST['option_page'] ) ), 'easyio_options' ) &&
    152             \wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'easyio_options-options' ) &&
    153             \current_user_can( 'manage_network_options' ) &&
    154             ! \get_site_option( 'easyio_allow_multisite_override' ) &&
    155             false === \strpos( wp_get_referer(), 'options-general' )
    156         ) {
    157             $this->debug_message( 'network-wide settings, no override' );
    158             $easyio_debug = ( empty( $_POST['easyio_debug'] ) ? false : true );
    159             \update_site_option( 'easyio_debug', $easyio_debug );
    160             $easyio_metadata_remove = ( empty( $_POST['easyio_metadata_remove'] ) ? false : true );
    161             \update_site_option( 'easyio_metadata_remove', $easyio_metadata_remove );
    162             $exactdn_all_the_things = ( empty( $_POST['exactdn_all_the_things'] ) ? false : true );
    163             \update_site_option( 'exactdn_all_the_things', $exactdn_all_the_things );
    164             $exactdn_lossy = ( empty( $_POST['exactdn_lossy'] ) ? false : true );
    165             \update_site_option( 'exactdn_lossy', $exactdn_lossy );
    166             $exactdn_hidpi = ( empty( $_POST['exactdn_hidpi'] ) ? false : true );
    167             \update_site_option( 'exactdn_hidpi', $exactdn_hidpi );
    168             $exactdn_exclude = empty( $_POST['exactdn_exclude'] ) ? '' : sanitize_textarea_field( wp_unslash( $_POST['exactdn_exclude'] ) );
    169             \update_site_option( 'exactdn_exclude', $this->exclude_paths_sanitize( $exactdn_exclude ) );
    170             $easyio_add_missing_dims = ( empty( $_POST['easyio_add_missing_dims'] ) ? false : true );
    171             \update_site_option( 'easyio_add_missing_dims', $easyio_add_missing_dims );
    172             $easyio_lazy_load = ( empty( $_POST['easyio_lazy_load'] ) ? false : true );
    173             \update_site_option( 'easyio_lazy_load', $easyio_lazy_load );
    174             $easyio_ll_autoscale = ( empty( $_POST['easyio_ll_autoscale'] ) ? false : true );
    175             \update_site_option( 'easyio_ll_autoscale', $easyio_ll_autoscale );
    176             $easyio_ll_abovethefold = ! empty( $_POST['easyio_ll_abovethefold'] ) ? (int) $_POST['easyio_ll_abovethefold'] : 0;
    177             \update_site_option( 'easyio_ll_abovethefold', $easyio_ll_abovethefold );
    178             $easyio_use_lqip = ( empty( $_POST['easyio_use_lqip'] ) ? false : true );
    179             \update_site_option( 'easyio_use_lqip', $easyio_use_lqip );
    180             $easyio_use_dcip = ( empty( $_POST['easyio_use_dcip'] ) ? false : true );
    181             \update_site_option( 'easyio_use_dcip', $easyio_use_dcip );
    182             $easyio_ll_exclude = empty( $_POST['easyio_ll_exclude'] ) ? '' : sanitize_textarea_field( wp_unslash( $_POST['easyio_ll_exclude'] ) );
    183             \update_site_option( 'easyio_ll_exclude', $this->exclude_paths_sanitize( $easyio_ll_exclude ) );
    184             $easyio_ll_all_things = empty( $_POST['easyio_ll_all_things'] ) ? '' : sanitize_textarea_field( wp_unslash( $_POST['easyio_ll_all_things'] ) );
    185             \update_site_option( 'easyio_ll_all_things', $easyio_ll_all_things );
    186             $easyio_allow_multisite_override = empty( $_POST['easyio_allow_multisite_override'] ) ? false : true;
    187             \update_site_option( 'easyio_allow_multisite_override', $easyio_allow_multisite_override );
    188             $easyio_enable_help = empty( $_POST['easyio_enable_help'] ) ? false : true;
    189             \update_site_option( 'easyio_enable_help', $easyio_enable_help );
    190             \add_action( 'network_admin_notices', 'easyio_network_settings_saved' );
    191         } elseif ( isset( $_POST['easyio_allow_multisite_override_active'] ) && \current_user_can( 'manage_network_options' ) && ! empty( $_REQUEST['_wpnonce'] ) && \wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'easyio_options-options' ) ) {
    192             $this->debug_message( 'network-wide settings, single-site overriding' );
    193             $easyio_allow_multisite_override = empty( $_POST['easyio_allow_multisite_override'] ) ? false : true;
    194             \update_site_option( 'easyio_allow_multisite_override', $easyio_allow_multisite_override );
    195             \add_action( 'network_admin_notices', 'easyio_network_settings_saved' );
    196         } // End if().
    197     }
    198 
    199     /**
    200      * Register all our options and sanitation functions.
    201      */
    202     public function register_settings() {
    203         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    204         // Register all the common Easy IO settings.
    205         \register_setting( 'easyio_options', 'easyio_debug', 'boolval' );
    206         \register_setting( 'easyio_options', 'easyio_enable_help', 'boolval' );
    207         \register_setting( 'easyio_options', 'exactdn_all_the_things', 'boolval' );
    208         \register_setting( 'easyio_options', 'exactdn_lossy', 'boolval' );
    209         \register_setting( 'easyio_options', 'exactdn_hidpi', 'boolval' );
    210         \register_setting( 'easyio_options', 'exactdn_exclude', array( $this, 'exclude_paths_sanitize' ) );
    211         \register_setting( 'easyio_options', 'easyio_add_missing_dims', 'boolval' );
    212         \register_setting( 'easyio_options', 'easyio_lazy_load', 'boolval' );
    213         \register_setting( 'easyio_options', 'easyio_ll_abovethefold', 'intval' );
    214         \register_setting( 'easyio_options', 'easyio_use_lqip', 'boolval' );
    215         \register_setting( 'easyio_options', 'easyio_use_dcip', 'boolval' );
    216         \register_setting( 'easyio_options', 'easyio_ll_exclude', array( $this, 'exclude_paths_sanitize' ) );
    217         \register_setting( 'easyio_options', 'easyio_ll_all_things', 'sanitize_textarea_field' );
    218     }
    219 
    220     /**
    221      * Set some default option values.
    222      */
    223     public function set_defaults() {
    224         $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
    225         // Set defaults for all options that need to be autoloaded.
    226         \add_option( 'easyio_debug', false );
    227         \add_option( 'easyio_metadata_remove', true );
    228         \add_option( 'easyio_exactdn', false );
    229         \add_option( 'easyio_plan_id', 0 );
    230         \add_option( 'exactdn_all_the_things', false );
    231         \add_option( 'exactdn_lossy', false );
    232         \add_option( 'exactdn_hidpi', false );
    233         \add_option( 'exactdn_exclude', '' );
    234         \add_option( 'exactdn_sub_folder', false );
    235         \add_option( 'exactdn_prevent_db_queries', true );
    236         \add_option( 'exactdn_asset_domains', '' );
    237         \add_option( 'easyio_add_missing_dims', false );
    238         \add_option( 'easyio_lazy_load', false );
    239         \add_option( 'easyio_use_lqip', false );
    240         \add_option( 'easyio_use_dcip', false );
    241         \add_option( 'easyio_use_siip', false );
    242         \add_option( 'easyio_ll_autoscale', true );
    243         \add_option( 'easyio_ll_abovethefold', 0 );
    244         \add_option( 'easyio_ll_exclude', '' );
    245         \add_option( 'easyio_ll_all_things', '' );
    246 
    247         // Set network defaults.
    248         \add_site_option( 'easyio_metadata_remove', true );
    249         \add_site_option( 'easyio_add_missing_dims', true );
    250         \add_site_option( 'easyio_ll_autoscale', true );
    251         \add_site_option( 'exactdn_sub_folder', false );
    252         \add_site_option( 'exactdn_prevent_db_queries', true );
     263        if ( ! \defined( 'WP_CLI' ) || ! WP_CLI ) {
     264            $this->privacy_policy_content();
     265        }
     266    }
     267
     268    /**
     269     * Adds the Easy IO version to the useragent for http requests.
     270     *
     271     * @param string $useragent The current useragent used in http requests.
     272     * @return string The useragent with the Easy IO version appended.
     273     */
     274    public function api_useragent( $useragent ) {
     275        if ( ! \str_contains( $useragent, 'EIO' ) ) {
     276            $useragent .= ' EIO/' . EASYIO_VERSION . ' ';
     277        }
     278        return $useragent;
     279    }
     280
     281    /**
     282     * Adds suggested privacy policy content for site admins.
     283     *
     284     * Note that this is just a suggestion, it should be customized for your site.
     285     */
     286    private function privacy_policy_content() {
     287        if ( ! \function_exists( 'wp_add_privacy_policy_content' ) || ! \function_exists( 'wp_kses_post' ) ) {
     288            return;
     289        }
     290        $content  = '<p class="privacy-policy-tutorial">';
     291        $content .= \wp_kses_post( \__( 'Normally, this plugin does not process any information about your visitors. However, if you accept user-submitted images and display them on your site, you can use this language to keep your visitors informed.', 'easy-image-optimizer' ) ) . '</p>';
     292        $content .= '<p>' . \wp_kses_post( \__( 'User-submitted images that are displayed on this site will be transmitted and stored on a global network of third-party servers (a CDN).', 'easy-image-optimizer' ) ) . '</p>';
     293        \wp_add_privacy_policy_content( 'Easy Image Optimizer', $content );
     294    }
     295
     296    /**
     297     * Set default permissions for admin (configuration) and bulk operations.
     298     *
     299     * @param string $permissions A valid WP capability level.
     300     * @return string Either the original value, unchanged, or the default capability level.
     301     */
     302    public function admin_permissions( $permissions ) {
     303        if ( empty( $permissions ) ) {
     304            return 'activate_plugins';
     305        }
     306        return $permissions;
     307    }
     308
     309    /**
     310     * Set default permissions for multisite/network admin (configuration) operations.
     311     *
     312     * @param string $permissions A valid WP capability level.
     313     * @return string Either the original value, unchanged, or the default capability level.
     314     */
     315    public function superadmin_permissions( $permissions ) {
     316        if ( empty( $permissions ) ) {
     317            return 'manage_network_options';
     318        }
     319        return $permissions;
     320    }
     321
     322    /**
     323     * Check the current screen, used to temporarily enable debugging on settings page.
     324     *
     325     * @param object $screen Information about the page/screen currently being loaded.
     326     */
     327    public function current_screen( $screen ) {
     328        if ( $this->get_option( 'easyio_debug' ) ) {
     329            return;
     330        }
     331        if ( \str_contains( $screen->id, 'settings_page_easy-image-optimizer' ) ) {
     332            return;
     333        }
     334        // Otherwise, we are somewhere else and should disable temp debugging.
     335        Base::$debug_data = '';
     336        Base::$temp_debug = false;
     337    }
     338
     339    /**
     340     * Let the user know they need to take action!
     341     */
     342    public function service_inactive_notice() {
     343        ?>
     344        <div id='easyio-inactive' class='notice notice-warning'>
     345            <p>
     346                <a href="<?php echo \esc_url( \admin_url( 'options-general.php?page=easy-image-optimizer-options' ) ); ?>">
     347                    <?php \esc_html_e( 'Please visit the settings page to complete activation of the Easy Image Optimizer.', 'easy-image-optimizer' ); ?>
     348                </a>
     349            </p>
     350        </div>
     351        <?php
     352    }
     353
     354    /**
     355     * Let the user know they need to disable the WP Offload Media CNAME.
     356     */
     357    public function exactdn_as3cf_cname_active_notice() {
     358        ?>
     359        <div id="easyio-notice-exactdn-as3cf-cname-active" class="notice notice-error">
     360            <p>
     361                <?php \esc_html_e( 'Easy IO cannot optimize your images while using a custom domain (CNAME) in WP Offload Media. Please disable the custom domain in the WP Offload Media settings.', 'easy-image-optimizer' ); ?>
     362            </p>
     363        </div>
     364        <?php
     365    }
     366
     367    /**
     368     * Let the user know the local domain appears to have changed from what Easy IO has recorded in the db.
     369     */
     370    public function exactdn_domain_mismatch_notice() {
     371        global $exactdn;
     372        if ( ! isset( $exactdn->upload_domain ) ) {
     373            return;
     374        }
     375        $stored_local_domain = $this->get_option( 'easyio_exactdn_local_domain' );
     376        if ( empty( $stored_local_domain ) ) {
     377            return;
     378        }
     379        if ( ! \str_contains( $stored_local_domain, '.' ) ) {
     380            $stored_local_domain = \base64_decode( $stored_local_domain );
     381        }
     382        ?>
     383        <div id="easyio-notice-exactdn-domain-mismatch" class="notice notice-warning">
     384            <p>
     385                <?php
     386                \printf(
     387                    /* translators: 1: old domain name, 2: current domain name */
     388                    \esc_html__( 'Easy IO detected that the Site URL has changed since the initial activation (previously %1$s, currently %2$s).', 'easy-image-optimizer' ),
     389                    '<strong>' . \esc_html( $stored_local_domain ) . '</strong>',
     390                    '<strong>' . \esc_html( $exactdn->upload_domain ) . '</strong>'
     391                );
     392                ?>
     393                <br>
     394                <?php
     395                \printf(
     396                    /* translators: %s: settings page */
     397                    \esc_html__( 'Please visit the %s to refresh the Easy IO settings and verify activation status.', 'easy-image-optimizer' ),
     398                    '<a href="' . \esc_url( \admin_url( 'options-general.php?page=easy-image-optimizer-options' ) ) . '">' . \esc_html__( 'settings page', 'easy-image-optimizer' ) . '</a>'
     399                );
     400                ?>
     401            </p>
     402        </div>
     403        <?php
     404    }
     405
     406    /**
     407     * Inform the user of our beacon function so that they can opt-in.
     408     */
     409    public function hs_beacon_notice() {
     410        $optin_url  = \wp_nonce_url( 'admin.php?action=eio_opt_into_hs_beacon', 'eio_beacon' );
     411        $optout_url = \wp_nonce_url( 'admin.php?action=eio_opt_out_of_hs_beacon', 'eio_beacon' );
     412        ?>
     413        <div id="easyio-hs-beacon" class="notice notice-info">
     414            <p>
     415                <?php \esc_html_e( 'Enable the Easy IO support beacon, which gives you access to documentation and our support team right from your WordPress dashboard. To assist you more efficiently, we collect the current url, IP address, browser/device information, and debugging information.', 'easy-image-optimizer' ); ?><br>
     416                <a href="<?php echo \esc_url( $optin_url ); ?>" class="button-secondary"><?php esc_html_e( 'Allow', 'easy-image-optimizer' ); ?></a>&nbsp;
     417                <a href="<?php echo \esc_url( $optout_url ); ?>" class="button-secondary"><?php esc_html_e( 'Do not allow', 'easy-image-optimizer' ); ?></a>
     418            </p>
     419        </div>
     420        <?php
     421    }
     422
     423    /**
     424     * Inform the user that we disabled SP AIO to prevent conflicts with ExactDN.
     425     */
     426    public function sp_conflict_notice() {
     427        ?>
     428        <div id='easyio-sp-conflict' class='notice notice-warning'>
     429            <p>
     430                <?php \esc_html_e( 'ShortPixel/Autoptimize image optimization has been disabled to prevent conflicts with Easy Image Optimizer).', 'easy-image-optimizer' ); ?>
     431            </p>
     432        </div>
     433        <?php
    253434    }
    254435
     
    256437     * Tell the user to disable Hide my WP function that removes query strings.
    257438     */
    258     public function notice_hmwp_hide_version() {
     439    public function hmwp_hide_version_notice() {
    259440        ?>
    260441        <div id='easy-image-optimizer-warning-hmwp-hide-version' class='notice notice-warning'>
    261442            <p>
    262443                <?php \esc_html_e( 'Please enable the Random Static Number option in Hide My WP to ensure compatibility with Easy IO or disable the Hide Version option for best performance.', 'easy-image-optimizer' ); ?>
    263                 <?php \easyio_help_link( 'https://docs.ewww.io/article/50-exactdn-and-query-strings', '5a3d278a2c7d3a1943677b52' ); ?>
     444                <?php $this->settings->help_link( 'https://docs.ewww.io/article/50-exactdn-and-query-strings', '5a3d278a2c7d3a1943677b52' ); ?>
    264445            </p>
    265446        </div>
  • easy-image-optimizer/trunk/easy-image-optimizer.php

    r3350722 r3398277  
    1414Description: Easily speed up your website to better connect with your visitors. Properly compress and size/scale images. Includes lazy load and WebP auto-convert.
    1515Author: Exactly WWW
    16 Version: 4.2.1
     16Version: 4.3.0
    1717Requires at least: 6.6
    1818Requires PHP: 8.1
     
    2525}
    2626
    27 // Check the PHP version.
    28 if ( ! defined( 'PHP_VERSION_ID' ) || PHP_VERSION_ID < 80100 ) {
    29     add_action( 'network_admin_notices', 'easyio_unsupported_php' );
    30     add_action( 'admin_notices', 'easyio_unsupported_php' );
    31 } elseif ( false === strpos( add_query_arg( '', '' ), 'easyio_disable=1' ) ) {
    32     define( 'EASYIO_VERSION', 421 );
     27if ( ! class_exists( 'EasyIO\Plugin' ) && ! str_contains( add_query_arg( '', '' ), 'easyio_disable=1' ) ) {
     28    define( 'EASYIO_VERSION', 430 );
    3329
    3430    /**
     
    5854            if ( ! is_writable( WP_CONTENT_DIR ) || ! empty( $_ENV['PANTHEON_ENVIRONMENT'] ) ) {
    5955                $upload_dir = wp_get_upload_dir();
    60                 if ( false === strpos( $upload_dir['basedir'], '://' ) && is_writable( $upload_dir['basedir'] ) ) {
     56                if ( ! str_contains( $upload_dir['basedir'], '://' ) && is_writable( $upload_dir['basedir'] ) ) {
    6157                    $easyio_content_dir = trailingslashit( $upload_dir['basedir'] ) . trailingslashit( 'easyio' );
    6258                }
     
    6662    }
    6763
    68     /**
    69      * All the 'unique' functions for the core Easy IO plugin.
    70      */
    71     require_once EASYIO_PLUGIN_PATH . 'unique.php';
    7264    /**
    7365     * All the base functions for our plugins.
     
    8880    easyio();
    8981} // End if().
    90 
    91 if ( ! function_exists( 'easyio_unsupported_php' ) ) {
    92     /**
    93      * Display a notice that the PHP version is too old.
    94      */
    95     function easyio_unsupported_php() {
    96         echo '<div id="easyio-warning-php" class="error"><p><a href="https://docs.ewww.io/article/55-upgrading-php" target="_blank" data-beacon-article="5ab2baa6042863478ea7c2ae">' . esc_html__( 'Easy Image Optimizer requires PHP 8.1 or greater. Newer versions of PHP are faster and more secure. If you are unsure how to upgrade to a supported version, ask your webhost for instructions.', 'easy-image-optimizer' ) . '</a></p></div>';
    97     }
    98 }
  • easy-image-optimizer/trunk/includes/eio.js

    r3076002 r3398277  
    3838        });
    3939    }
    40     $('#easyio-general-settings').show();
    41     $('li.easyio-general-nav').addClass('easyio-selected');
    42     $('#easyio-support-settings').hide();
    4340    $('.easyio-general-nav').click(function() {
    4441        $('.easyio-tab-nav li').removeClass('easyio-selected');
     
    5956        $('#easyio-hidden-submit').show();
    6057    });
    61     return false;
     58    $('a#easyio-activate').on( 'click', function() {
     59        $('a#easyio-activate').hide();
     60        $('#easyio-activation-processing').show();
     61        activateExactDNSite();
     62        return false;
     63    });
     64    function activateExactDNSite() {
     65        var easyio_post_action = 'easyio_activate';
     66        var easyio_post_data = {
     67            action: easyio_post_action,
     68            _wpnonce: easyio_vars._wpnonce,
     69        };
     70        $.post(ajaxurl, easyio_post_data, function(response) {
     71            try {
     72                var easyio_response = JSON.parse(response);
     73            } catch (err) {
     74                $('#easyio-activation-processing').hide();
     75                $('#easyio-activation-result').html(easyio_vars.invalid_response);
     76                $('#easyio-activation-result').addClass('error');
     77                $('#easyio-activation-result').show();
     78                console.log( response );
     79                return false;
     80            }
     81            if ( easyio_response.error ) {
     82                $('#easyio-activation-processing').hide();
     83                $('a#easyio-activate').show();
     84                $('#easyio-activation-result').html(easyio_response.error);
     85                $('#easyio-activation-result').addClass('error');
     86                $('#easyio-activation-result').show();
     87            } else if ( ! easyio_response.success ) {
     88                $('#easyio-activation-processing').hide();
     89                $('#easyio-activation-result').html(easyio_vars.invalid_response);
     90                $('#easyio-activation-result').addClass('error');
     91                $('#easyio-activation-result').show();
     92                console.log( response );
     93            } else {
     94                $('#easyio-activation-processing').hide();
     95                $('#easyio-status').html(easyio_response.success);
     96                $('#exactdn_all_the_things').prop('checked', true);
     97                $('#easyio_lazy_load').prop('checked', true);
     98                $('#easyio_add_missing_dims').prop('disabled', false);
     99                $('.easyio-settings-table').show();
     100                $('#easyio-hidden-submit').show();
     101                $('table.easyio-inactive').hide();
     102            }
     103        });
     104        return false;
     105    }
     106    var easy_save_bar_width = $('#easyio-savings-fill').data('score');
     107    $('#easyio-savings-fill').animate( {
     108        width: easy_save_bar_width + '%',
     109    }, 1000 );
     110    var easy_bandwidth_bar_width = $('#easyio-bandwidth-fill').data('score');
     111    if ( easy_bandwidth_bar_width == 100 ) {
     112        $('#easyio-bandwidth-container .easyio-bar-fill').css('background-color', '#d63638');
     113        $('#easyio-bandwidth-flex a').css('color', '#d63638');
     114    }
     115    $('#easyio-bandwidth-fill').animate( {
     116        width: easy_bandwidth_bar_width + '%',
     117    }, 1000 );
     118    easyIORegisterStatsHandler();
     119    function easyIORegisterStatsHandler() {
     120        $('#easyio-show-stats').on('click', function(){
     121            var site_id = $(this).attr('data-site-id');
     122            var easyio_post_data = {
     123                action: 'easyio_get_site_stats',
     124                site_id: site_id,
     125                _wpnonce: easyio_vars._wpnonce,
     126            };     
     127            var statsContainerID = 'exactdn-stats-modal-' + site_id;
     128            var statsContainer = false;
     129            var statsExist = document.getElementById(statsContainerID);
     130            var closeIcon  = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>';                   
     131            if ( ! statsExist ) {
     132                $('body').append('<div id="' + statsContainerID + '" style="display:none;" class="exactdn-stats-modal"><div class="exactdn-stats-modal-close">' + closeIcon + '</div><div class="exactdn-stats-modal-charts"></div><img class="exactdn-loading-image" style="display:block;margin-left: auto;margin-right:auto;width:20px;" src="' + easyio_vars.loading_image_url + '" /></div>');
     133                statsContainer = $('#' + statsContainerID);
     134                $(statsContainer).on('click', '.exactdn-stats-modal-close', function() {
     135                    $('.exactdn-stats-modal').hide();
     136                    document.body.classList.toggle('exactdn-body-unscroll');
     137                });
     138                               
     139                $.post(ajaxurl, easyio_post_data, function(response) {
     140                    //console.log( response );
     141                    var is_json = true;
     142                    try {
     143                        var easyio_response = $.parseJSON(response);
     144                    } catch (err) {
     145                        is_json = false;
     146                    }
     147                    if ( ! is_json ) {
     148                        statsContainer.children('.exactdn-stats-modal-charts').html(easyio_vars.invalid_response);
     149                        $('.exactdn-loading-image').hide();
     150                        console.log(response);
     151                    } else if (easyio_response.error) {
     152                        statsContainer.children('.exactdn-stats-modal-charts').html('<strong>Error (contact support if necessary):</strong> ' + easyio_response.error);
     153                        $('.exactdn-loading-image').hide();
     154                    } else if (easyio_response.html) {
     155                        statsContainer.children('.exactdn-stats-modal-charts').html(easyio_response.html);
     156                        if (easyio_response.pending) {
     157                            console.log('need to fetch more stats, request pending');
     158                            setTimeout(fetchExtraStats, 10000, site_id);
     159                        } else {
     160                            $('.exactdn-loading-image').hide();
     161                        }
     162                    } else {
     163                        statsContainer.children('.exactdn-stats-modal-charts').html(easyio_vars.invalid_response);
     164                        $('.exactdn-loading-image').hide();
     165                        console.log(response);
     166                    }
     167                })
     168                .fail(function() {
     169                    statsContainer.children('.exactdn-stats-modal-charts').html(easyio_vars.invalid_response);
     170                    $('.exactdn-loading-image').hide();
     171                });
     172            } else {
     173                statsContainer = $('#' + statsContainerID);
     174            }
     175            statsContainer.show();
     176            document.body.classList.toggle('exactdn-body-unscroll');
     177            return false;
     178        });
     179    }
     180    var extraStatsRequests = 0;
     181    function fetchExtraStats(site_id) {
     182        var easyio_post_data = {
     183            action: 'easyio_get_site_stats',
     184            site_id: site_id,
     185            require_extra: 1,
     186            _wpnonce: easyio_vars._wpnonce,
     187        };
     188        var statsContainerID = 'exactdn-stats-modal-' + site_id;
     189        var statsContainer = false;
     190        var statsExist = document.getElementById(statsContainerID);
     191        if ( ! statsExist ) {
     192            console.log('no container for site #' + site_id);
     193            return;
     194        }
     195        statsContainer = $('#' + statsContainerID);
     196        if ( extraStatsRequests > 11 ) { // Roughly 2 minutes of waiting.
     197            $('.exactdn-loading-image').hide();
     198            statsContainer.find('.exactdn-stats-pending').text(easyio_vars.easyio_extra_stats_failed);
     199            return;
     200        }
     201        $.post(ajaxurl, easyio_post_data, function(response) {
     202            extraStatsRequests++;
     203            var is_json = true;
     204            try {
     205                var easyio_response = $.parseJSON(response);
     206            } catch (err) {
     207                is_json = false;
     208            }
     209            if ( ! is_json ) {
     210                console.log(response);
     211                setTimeout(fetchExtraStats, 10000, site_id);
     212                return;
     213            }
     214            if (easyio_response.error) {
     215                console.log(easyio_response.error);
     216            } else if (easyio_response.html) {
     217                $('.exactdn-loading-image').hide();
     218                statsContainer.children('.exactdn-stats-modal-charts').html(easyio_response.html);
     219                return;
     220            }
     221            setTimeout(fetchExtraStats, 10000, site_id);
     222        });
     223    }
    62224});
    63225function selectText(containerid) {
  • easy-image-optimizer/trunk/includes/lazysizes-post.js

    r3197623 r3398277  
    2828            if(e.detail.instance != lazySizes){return;}
    2929
    30             var bg, bgWebP;
     30            var bg, bgWebP,swisLazyId;
    3131            if(!e.defaultPrevented) {
    3232
     
    3636
    3737                // handle data-back (so as not to conflict with the stock data-bg)
    38                 bg = e.target.getAttribute('data-back');
     38                bg = e.target.dataset.back;
     39                // Was getAttribute('data-back');
    3940                if (bg) {
    40                         if(ewww_webp_supported) {
     41                    if(ewww_webp_supported) {
    4142                        console.log('checking for data-back-webp');
    42                         bgWebP = e.target.getAttribute('data-back-webp');
     43                        bgWebP = e.target.dataset.backWebp;
     44                        // Was bgWebP = e.target.getAttribute('data-back-webp');
    4345                        if (bgWebP) {
    4446                            console.log('replacing data-back with data-back-webp');
     
    4648                        }
    4749                    }
    48                     var dPR = getdPR();
    49                     var targetWidth  = Math.round(e.target.offsetWidth * dPR);
    50                     var targetHeight = Math.round(e.target.offsetHeight * dPR);
    51                     if ( 0 === bg.search(/\[/) ) {
    52                     } else if (!shouldAutoScale(e.target)){
    53                     } else if (lazySizes.hC(e.target,'wp-block-cover')) {
    54                         console.log('found wp-block-cover with data-back');
    55                         if (lazySizes.hC(e.target,'has-parallax')) {
    56                             console.log('also has-parallax with data-back');
    57                             targetWidth  = Math.round(window.screen.width * dPR);
    58                             targetHeight = Math.round(window.screen.height * dPR);
    59                         } else if (targetHeight<300) {
    60                             targetHeight = 430;
    61                         }
    62                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    63                     } else if (lazySizes.hC(e.target,'cover-image')){
    64                         console.log('found .cover-image with data-back');
    65                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    66                     } else if (lazySizes.hC(e.target,'elementor-bg')){
    67                         console.log('found elementor-bg with data-back');
    68                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    69                     } else if (lazySizes.hC(e.target,'et_parallax_bg')){
    70                         console.log('found et_parallax_bg with data-back');
    71                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    72                     } else if (lazySizes.hC(e.target,'bg-image-crop')){
    73                         console.log('found bg-image-crop with data-back');
    74                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg-cover');
    75                     } else {
    76                         console.log('found other data-back');
    77                         bg = constrainSrc(bg,targetWidth,targetHeight,'bg');
    78                     }
     50                    bg = constrainBg(bg,e.target);
    7951                    if ( e.target.style.backgroundImage && -1 === e.target.style.backgroundImage.search(/^initial/) ) {
    8052                        // Convert JSON for multiple URLs.
     
    11486                    }
    11587                }
     88                // Handle CSS images from SWIS.
     89                swisLazyId = e.target.dataset.swisLazyId;
     90                if (swisLazyId && swisLazyId in swis_lazy_css_images) {
     91                    console.log('fetching CSS images for swisLazyId ' + swisLazyId);
     92                    var css_images = swis_lazy_css_images[swisLazyId];
     93                    var swisStyle  = document.querySelector('style#swis-lazy-css-styles');
     94                    css_images.forEach(
     95                        function(css_image){
     96                            if (!css_image.url) {
     97                                return;
     98                            }
     99                            if(ewww_webp_supported && css_image.webp_url) {
     100                                console.log('webp supported, using webp url for css image');
     101                                css_image.url = css_image.webp_url;
     102                            }
     103                            css_image.url = constrainBg(css_image.url,e.target);
     104                            console.log('processing CSS image: ' + css_image.url + ' with hash ' + css_image.hash);
     105                            var cssRule = css_image.selector + ' {--swis-bg-' + css_image.hash + ': url(' + css_image.url + '); }';
     106                            swisStyle.sheet.insertRule(cssRule);
     107                        }
     108                    );
     109                }
    116110            }
    117111        }, false);
    118112    }
     113
     114    var constrainBg = function(bg,target){
     115        if ( 0 === bg.search(/\[/) ) {
     116            console.log('multiple URLs, not autoscaling background image');
     117            return bg;
     118        }
     119        if (!shouldAutoScale(target)){
     120            console.log('not autoscaling background image');
     121            return bg;
     122        }
     123        var dPR = getdPR();
     124        if ( dPR < eio_lazy_vars.bg_min_dpr ) {
     125            dPR = eio_lazy_vars.bg_min_dpr;
     126        }
     127        var targetWidth  = Math.round(target.offsetWidth * dPR);
     128        var targetHeight = Math.round(target.offsetHeight * dPR);
     129        var bgType       = 'bg';
     130        if (lazySizes.hC(target,'wp-block-cover')||lazySizes.hC(target,'wp-block-cover__image-background')){
     131            console.log('found wp-block-cover with data-back');
     132            if (lazySizes.hC(target,'has-parallax')) {
     133                console.log('also has-parallax with data-back');
     134                targetWidth  = Math.round(window.screen.width * dPR);
     135                targetHeight = Math.round(window.screen.height * dPR);
     136            } else if (targetHeight<300) {
     137                targetHeight = 430;
     138            }
     139            bgType = 'bg-cover';
     140        } else if (lazySizes.hC(target,'cover-image')){
     141            console.log('found .cover-image with data-back');
     142            bgType = 'bg-cover';
     143        } else if (lazySizes.hC(target,'elementor-bg')){
     144            console.log('found elementor-bg with data-back');
     145            bgType = 'bg-cover';
     146        } else if (lazySizes.hC(target,'et_parallax_bg')){
     147            console.log('found et_parallax_bg with data-back');
     148            bgType = 'bg-cover';
     149        } else if (lazySizes.hC(target,'bg-image-crop')){
     150            bgType = 'bg-cover';
     151            console.log('found bg-image-crop with data-back');
     152        } else {
     153            console.log('found other data-back');
     154        }
     155        var imgAspect = getAspectRatio(target);
     156        if ('bg' == bgType && targetHeight > 1 && targetWidth > 1 && imgAspect > 0) {
     157            var minimum_width  = Math.ceil(targetHeight * imgAspect);
     158            var minimum_height = Math.ceil(targetWidth / imgAspect);
     159            console.log('minimum_width = ' + minimum_width + ', targetWidth = ' + targetWidth);
     160            console.log('minimum_height = ' + minimum_height + ', targetHeight = ' + targetHeight);
     161            if (targetWidth+2 < minimum_width) {
     162                targetWidth = minimum_width;
     163            }
     164            if (targetHeight+2 < minimum_height) {
     165                targetHeight = minimum_height;
     166            }
     167            var realDims = getRealDimensionsFromImg(target);
     168            if (Math.abs(realDims.w - targetWidth) < 5 || Math.abs(realDims.h - targetHeight) < 5) {
     169                console.log('real dimensions within 5px of target sizes, no scaling');
     170                return bg;
     171            }
     172        }
     173        bg = constrainSrc(bg,targetWidth,targetHeight,bgType);
     174        return bg;
     175    };
    119176
    120177    var shouldAutoScale = function(target){
     
    294351
    295352    var getRealDimensionsFromImg = function(img){
    296         var realWidth = img.getAttribute('data-eio-rwidth');
    297         var realHeight = img.getAttribute('data-eio-rheight');
     353        var realWidth = img.dataset.eioRwidth;
     354        var realHeight = img.dataset.eioRheight;
    298355        if (realWidth > 1 && realHeight > 1) {
    299356            return {w:realWidth,h:realHeight};
     
    411468        }
    412469        if (e.target._lazysizesWidth === undefined) {
     470            if (!eio_lazy_vars.use_dpr && window.devicePixelRatio > 1) {
     471                console.log('use_dpr is disabled, reversing auto-sizes by dpr ' + window.devicePixelRatio);
     472                e.detail.width = Math.ceil(e.detail.width / window.devicePixelRatio);
     473            }
    413474            return;
    414475        }
     
    417478            console.log('no way! ' + e.detail.width + ' is smaller than ' + e.target._lazysizesWidth);
    418479            e.detail.width = e.target._lazysizesWidth;
     480        }
     481        if (!eio_lazy_vars.use_dpr && window.devicePixelRatio > 1) {
     482            console.log('use_dpr is disabled, reversing auto-sizes by dpr ' + window.devicePixelRatio);
     483            e.detail.width = Math.ceil(e.detail.width / window.devicePixelRatio);
    419484        }
    420485    });
  • easy-image-optimizer/trunk/includes/lazysizes-pre.js

    r3197623 r3398277  
    11if (typeof ewww_webp_supported === 'undefined') {
    22    var ewww_webp_supported = false;
     3}
     4if (typeof swis_lazy_css_images === 'undefined') {
     5    var swis_lazy_css_images = {};
    36}
    47window.lazySizesConfig = window.lazySizesConfig || {};
     
    1821}
    1922console.log( 'root margin: ' + window.lazySizesConfig.expand );
     23for ( const [css_index, css_image] of Object.entries(swis_lazy_css_images)){
     24    console.log('processing css image ' + css_index + ': ' + css_image[0].url);
     25    try {
     26        document.querySelectorAll(css_image[0].selector).forEach((el) => {
     27            if (!el.classList.contains('lazyload')) {
     28                console.log('adding lazyload to css image ' + css_index + ': ' + css_image[0].url);
     29                el.classList.add('lazyload');
     30                el.dataset.swisLazyId = css_index;
     31                if (css_image[0].rwidth > 5 && css_image[0].rheight > 5) {
     32                    el.dataset.eioRwidth = css_image[0].rwidth;
     33                    el.dataset.eioRheight = css_image[0].rheight;
     34                }
     35            }
     36        });
     37    } catch (e) {
     38        console.log('error processing css image(s) for "' + css_index[0].selector + '": ' + e);
     39    }
     40}
  • easy-image-optimizer/trunk/includes/lazysizes.min.js

    r3197623 r3398277  
    1 var ewww_webp_supported;void 0===ewww_webp_supported&&(ewww_webp_supported=!1),window.lazySizesConfig=window.lazySizesConfig||{},window.lazySizesConfig.expand=500<document.documentElement.clientHeight&&500<document.documentElement.clientWidth?1e3:740,window.lazySizesConfig.iframeLoadMode=1,"undefined"==typeof eio_lazy_vars&&(eio_lazy_vars={exactdn_domain:".exactdn.com",threshold:0,skip_autoscale:0,use_dpr:0}),50<eio_lazy_vars.threshold&&(window.lazySizesConfig.expand=eio_lazy_vars.threshold),function(e,t){function a(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)}t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):"function"==typeof define&&define.amd?define(["lazysizes"],t):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(e,n,o){"use strict";var s,l,d={};function c(e,t,a){var r,i;d[e]||(r=n.createElement(t?"link":"script"),i=n.getElementsByTagName("script")[0],t?(r.rel="stylesheet",r.href=e):(r.onload=function(){r.onerror=null,r.onload=null,a()},r.onerror=r.onload,r.src=e),d[e]=!0,d[r.src||r.href]=!0,i.parentNode.insertBefore(r,i))}n.addEventListener&&(l=/\(|\)|\s|'/,s=function(e,t){var a=n.createElement("img");a.onload=function(){a.onload=null,a.onerror=null,a=null,t()},a.onerror=a.onload,a.src=e,a&&a.complete&&a.onload&&a.onload()},addEventListener("lazybeforeunveil",function(e){var t,a,r;if(e.detail.instance==o&&!e.defaultPrevented){var i=e.target;if("none"==i.preload&&(i.preload=i.getAttribute("data-preload")||"auto"),null!=i.getAttribute("data-autoplay"))if(i.getAttribute("data-expand")&&!i.autoplay)try{i.play()}catch(e){}else requestAnimationFrame(function(){i.setAttribute("data-expand","-10"),o.aC(i,o.cfg.lazyClass)});(t=i.getAttribute("data-link"))&&c(t,!0),(t=i.getAttribute("data-script"))&&(e.detail.firesLoad=!0,c(t,null,function(){e.detail.firesLoad=!1,o.fire(i,"_lazyloaded",{},!0,!0)})),(t=i.getAttribute("data-require"))&&(o.cfg.requireJs?o.cfg.requireJs([t]):c(t)),(a=i.getAttribute("data-bg"))&&(e.detail.firesLoad=!0,s(a,function(){i.style.backgroundImage="url("+(l.test(a)?JSON.stringify(a):a)+")",e.detail.firesLoad=!1,o.fire(i,"_lazyloaded",{},!0,!0)})),(r=i.getAttribute("data-poster"))&&(e.detail.firesLoad=!0,s(r,function(){i.poster=r,e.detail.firesLoad=!1,o.fire(i,"_lazyloaded",{},!0,!0)}))}},!1))}),function(e,t){function a(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)}t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):"function"==typeof define&&define.amd?define(["lazysizes"],t):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(u,f,g){"use strict";var n;f.addEventListener&&(n=/\(|\)|\s|'/,addEventListener("lazybeforeunveil",function(e){var t,a,r,i;e.detail.instance==g&&(e.defaultPrevented||("none"==e.target.preload&&(e.target.preload="auto"),(r=e.target.getAttribute("data-back"))&&(ewww_webp_supported&&(i=e.target.getAttribute("data-back-webp"))&&(r=i),t=v(),a=Math.round(e.target.offsetWidth*t),i=Math.round(e.target.offsetHeight*t),0===r.search(/\[/)||o(e.target)&&(r=g.hC(e.target,"wp-block-cover")?(g.hC(e.target,"has-parallax")?(a=Math.round(u.screen.width*t),i=Math.round(u.screen.height*t)):i<300&&(i=430),s(r,a,i,"bg-cover")):g.hC(e.target,"cover-image")||g.hC(e.target,"elementor-bg")||g.hC(e.target,"et_parallax_bg")||g.hC(e.target,"bg-image-crop")?s(r,a,i,"bg-cover"):s(r,a,i,"bg")),e.target.style.backgroundImage&&-1===e.target.style.backgroundImage.search(/^initial/)?0===r.search(/\[/)?((r=JSON.parse(r)).forEach(function(e){n.test(e)&&JSON.stringify(e)}),r='url("'+r.join('"), url("')+'"',i=e.target.style.backgroundImage+", "+r,e.target.style.backgroundImage=i):e.target.style.backgroundImage=e.target.style.backgroundImage+', url("'+(n.test(r)?JSON.stringify(r):r)+'")':0===r.search(/\[/)?((r=JSON.parse(r)).forEach(function(e){n.test(e)&&JSON.stringify(e)}),r='url("'+r.join('"), url("')+'"',e.target.style.backgroundImage=r):e.target.style.backgroundImage="url("+(n.test(r)?JSON.stringify(r):r)+")")))},!1));function h(e){var t=e.getAttribute("data-eio-rwidth"),e=e.getAttribute("data-eio-rheight");return 1<t&&1<e?{w:t,h:e}:{w:0,h:0}}function m(e,t=!1){var a=v(),r=Math.round(e.offsetWidth*a),i=Math.round(e.offsetHeight*a),n=e.getAttribute("data-src"),a=e.getAttribute("data-src-webp");ewww_webp_supported&&a&&-1==n.search("webp=1")&&!t&&(n=a),o(e)&&(a=e,a=g.hC(a,"et_pb_jt_filterable_grid_item_image")||g.hC(a,"ss-foreground-image")||g.hC(a,"img-crop")?"img-crop":g.hC(a,"object-cover")&&(g.hC(a,"object-top")||g.hC(a,"object-bottom"))?"img-w":g.hC(a,"object-cover")&&(g.hC(a,"object-left")||g.hC(a,"object-right"))?"img-h":g.hC(a,"ct-image")&&g.hC(a,"object-cover")||!a.getAttribute("data-srcset")&&!a.srcset&&a.offsetHeight>a.offsetWidth&&1<l(a)?"img-crop":"img",(a=s(n,r,i,a,t))&&n!=a&&(t&&e.setAttribute("src",a),e.setAttribute("data-src",a)))}var o=function(e){if(1==eio_lazy_vars.skip_autoscale)return!1;for(var t=e,a=0;a<=7;a++){if(t.hasAttributes())for(var r=t.attributes,i=/skip-autoscale/,a=r.length-1;0<=a;a--){if(i.test(r[a].name))return!1;if(i.test(r[a].value))return!1}if(!t.parentNode||1!==t.parentNode.nodeType||!t.parentNode.hasAttributes)break;t=t.parentNode}return!0},s=function(e,t,a,r,i=!1){if(null===e)return e;var n=/w=(\d+)/,o=/fit=(\d+),(\d+)/,s=/resize=(\d+),(\d+)/,l=decodeURIComponent(e);if(/\.svg(\?.+)?$/.exec(l))return e;if(0<e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)){var d=s.exec(l);if(d&&(t<d[1]||i))return"img-w"===r?l.replace(s,"w="+t):"img-h"===r?l.replace(s,"h="+a):l.replace(s,"resize="+t+","+a);s=n.exec(e);if(s&&(t<=s[1]||i)){if("img-h"===r)return l.replace(n,"h="+a);if("bg-cover"!==r&&"img-crop"!==r)return e.replace(n,"w="+t);var c=Math.abs(s[1]-t);return 20<c||a<1080?e.replace(n,"resize="+t+","+a):e}c=o.exec(l);if(c&&(t<c[1]||i)){if("bg-cover"!==r&&"img-crop"!==r)return"img-w"===r?l.replace(o,"w="+t):"img-h"===r?l.replace(o,"h="+a):l.replace(o,"fit="+t+","+a);l=Math.abs(c[1]-t),o=Math.abs(c[2]-a);return 20<l||20<o?e.replace(n,"resize="+t+","+a):e}if(!s&&!c&&!d)return"img"===r?e+"&fit="+t+","+a:"bg-cover"===r||"img-crop"===r?e+"&resize="+t+","+a:"img-h"===r||t<a?e+"&h="+a:e+"&w="+t}return-1==e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)?"img"===r?e+"?fit="+t+","+a:"bg-cover"===r||"img-crop"===r?e+"?resize="+t+","+a:"img-h"===r||t<a?e+"?h="+a:e+"?w="+t:e},p=function(e){e=/-(\d+)x(\d+)\./.exec(e);return e&&1<e[1]&&1<e[2]?{w:e[1],h:e[2]}:{w:0,h:0}},l=function(e){var t=e.getAttribute("width"),a=e.getAttribute("height");if(1<t&&1<a)return t/a;a=!1;if(a=(a=e.src&&-1<e.src.search("http")?e.src:a)||e.getAttribute("data-src")){var r=p(a);if(r.w&&r.h)return r.w/r.h}r=h(e);if(r.w&&r.h)return r.w/r.h;e=function(e){var t;if(e.srcset?t=e.srcset.split(","):(e=e.getAttribute("data-srcset"))&&(t=e.split(",")),t){var a=0,r=t.length;if(r){for(;a<r;a++){var i,n=t[a].trim().split(" ");!n[0].length||(n=p(n[0])).w&&n.h&&(i=n)}if(i.w&&i.h)return i}}return{w:0,h:0}}(e);return e.w&&e.h?e.w/e.h:0},v=function(){return eio_lazy_vars.use_dpr&&1<u.devicePixelRatio?u.devicePixelRatio:1};f.addEventListener("lazybeforesizes",function(e){e.target.getAttribute("data-src");var t=l(e.target);1<e.target.clientHeight&&t&&(t=Math.ceil(t*e.target.clientHeight),e.detail.width+2<t&&(e.detail.width=t)),void 0!==e.target._lazysizesWidth&&e.detail.width<e.target._lazysizesWidth&&(e.detail.width=e.target._lazysizesWidth)}),f.addEventListener("lazybeforeunveil",function(e){var t,a,r,i,n=e.target,o=n.getAttribute("data-srcset");n.naturalWidth&&!o&&1<n.naturalWidth&&1<n.naturalHeight&&(t=v(),a=n.naturalWidth,r=n.naturalHeight,(e=h(n)).w&&e.w>a&&(a=e.w,r=e.h),a=n.clientWidth&&1.25*n.clientWidth*t<a,r=n.clientHeight&&1.25*n.clientHeight*t<r,(a||r)&&m(n)),ewww_webp_supported&&(!o||(i=n.getAttribute("data-srcset-webp"))&&n.setAttribute("data-srcset",i),(i=n.getAttribute("data-src-webp"))&&n.setAttribute("data-src",i))});function e(e=!1){e.type&&"load"===e.type&&g.autoSizer.checkElems(),v();var t,a=f.getElementsByClassName(g.cfg.loadedClass),r=a.length;if(r)for(t=0;t<r;t++){var i,n,o,s,l,d,c=a[t];c.src&&!c.srcset&&1<c.naturalWidth&&1<c.naturalHeight&&1<c.clientWidth&&1<c.clientHeight&&(i=c.naturalWidth,n=c.naturalHeight,o=u.innerWidth,s=u.innerHeight,l=h(c),d=p(c.src),l.w?o=l.w:d.w&&(o=d.w),l.h?s=l.h:d.h&&(s=d.h),l=c.clientWidth,d=c.clientHeight,(1.1*i<l&&l<=o||1.1*n<d&&d<=s)&&m(c,!0))}}var t,a,r,i,d=(t=e,i=function(){a=null,t()},function(){r=Date.now(),a=a||setTimeout(c,99)});function c(){var e=Date.now()-r;e<99?setTimeout(c,99-e):(u.requestIdleCallback||i)(i)}addEventListener("load",e),addEventListener("resize",d),setTimeout(e,2e4)}),function(e,t){t=t(e,e.document,Date);e.lazySizes=t,"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:{},function(r,f,n){"use strict";var g,h;if(!function(){var e,t={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:!0,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:!0,ricTimeout:0,throttleDelay:125};for(e in h=r.lazySizesConfig||r.lazysizesConfig||{},t)e in h||(h[e]=t[e])}(),!f||!f.getElementsByClassName)return{init:function(){},cfg:h,noSupport:!0};function c(e,t){E(e,t)||e.setAttribute("class",(e[v]("class")||"").trim()+" "+t)}function u(e,t){(t=E(e,t))&&e.setAttribute("class",(e[v]("class")||"").replace(t," "))}function m(e,t){var a;!l&&(a=r.picturefill||h.pf)?(t&&t.src&&!e[v]("srcset")&&e.setAttribute("srcset",t.src),a({reevaluate:!0,elements:[e]})):t&&t.src&&(e.src=t.src)}var a,i,t,o,s,p=f.documentElement,l=r.HTMLPictureElement,d="addEventListener",v="getAttribute",e=r[d].bind(r),y=r.setTimeout,b=r.requestAnimationFrame||y,z=r.requestIdleCallback,w=/^picture$/i,_=["load","error","lazyincluded","_lazyloaded"],C={},A=Array.prototype.forEach,E=function(e,t){return C[t]||(C[t]=new RegExp("(\\s|^)"+t+"(\\s|$)")),C[t].test(e[v]("class")||"")&&C[t]},L=function(t,a,e){var r=e?d:"removeEventListener";e&&L(t,a),_.forEach(function(e){t[r](e,a)})},x=function(e,t,a,r,i){var n=f.createEvent("Event");return(a=a||{}).instance=g,n.initEvent(t,!r,!i),n.detail=a,e.dispatchEvent(n),n},N=function(e,t){return(getComputedStyle(e,null)||{})[t]},M=function(e,t,a){for(a=a||e.offsetWidth;a<h.minSize&&t&&!e._lazysizesWidth;)a=t.offsetWidth,t=t.parentNode;return a},W=(o=[],s=t=[],k._lsFlush=S,k);function S(){var e=s;for(s=t.length?o:t,i=!(a=!0);e.length;)e.shift()();a=!1}function k(e,t){a&&!t?e.apply(this,arguments):(s.push(e),i||(i=!0,(f.hidden?y:b)(S)))}function H(a,e){return e?function(){W(a)}:function(){var e=this,t=arguments;W(function(){a.apply(e,t)})}}function I(e){function t(){var e=n.now()-r;e<99?y(t,99-e):(z||i)(i)}var a,r,i=function(){a=null,e()};return function(){r=n.now(),a=a||y(t,99)}}var T,j,B,O,R,q,F,J,P,D,$,U,G,K,Q,V,X,Y,Z,ee,te,ae,re,ie,ne,oe,se,le,de,ce,ue,fe=(Z=/^img$/i,ee=/^iframe$/i,te="onscroll"in r&&!/(gle|ing)bot/.test(navigator.userAgent),ie=-1,ne=function(e){return(U=null==U?"hidden"==N(f.body,"visibility"):U)||!("hidden"==N(e.parentNode,"visibility")&&"hidden"==N(e,"visibility"))},G=he,Q=re=ae=0,V=h.throttleDelay,X=h.ricTimeout,Y=z&&49<X?function(){z(me,{timeout:X}),X!==h.ricTimeout&&(X=h.ricTimeout)}:H(function(){y(me)},!0),se=H(pe),le=function(e){se({target:e.target})},de=H(function(t,e,a,r,i){var n,o,s,l,d;(s=x(t,"lazybeforeunveil",e)).defaultPrevented||(r&&(a?c(t,h.autosizesClass):t.setAttribute("sizes",r)),n=t[v](h.srcsetAttr),a=t[v](h.srcAttr),i&&(o=(d=t.parentNode)&&w.test(d.nodeName||"")),l=e.firesLoad||"src"in t&&(n||a||o),s={target:t},c(t,h.loadingClass),l&&(clearTimeout(B),B=y(ge,2500),L(t,le,!0)),o&&A.call(d.getElementsByTagName("source"),ve),n?t.setAttribute("srcset",n):a&&!o&&(ee.test(t.nodeName)?(r=a,0==(d=(e=t).getAttribute("data-load-mode")||h.iframeLoadMode)?e.contentWindow.location.replace(r):1==d&&(e.src=r)):t.src=a),i&&(n||o)&&m(t,{src:a})),t._lazyRace&&delete t._lazyRace,u(t,h.lazyClass),W(function(){var e=t.complete&&1<t.naturalWidth;l&&!e||(e&&c(t,h.fastLoadedClass),pe(s),t._lazyCache=!0,y(function(){"_lazyCache"in t&&delete t._lazyCache},9)),"lazy"==t.loading&&re--},!0)}),ue=I(function(){h.loadMode=3,oe()}),{_:function(){R=n.now(),g.elements=f.getElementsByClassName(h.lazyClass),T=f.getElementsByClassName(h.lazyClass+" "+h.preloadClass),e("scroll",oe,!0),e("resize",oe,!0),e("pageshow",function(e){var t;!e.persisted||(t=f.querySelectorAll("."+h.loadingClass)).length&&t.forEach&&b(function(){t.forEach(function(e){e.complete&&ce(e)})})}),r.MutationObserver?new MutationObserver(oe).observe(p,{childList:!0,subtree:!0,attributes:!0}):(p[d]("DOMNodeInserted",oe,!0),p[d]("DOMAttrModified",oe,!0),setInterval(oe,999)),e("hashchange",oe,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach(function(e){f[d](e,oe,!0)}),/d$|^c/.test(f.readyState)?be():(e("load",be),f[d]("DOMContentLoaded",oe),y(be,2e4)),g.elements.length?(he(),W._lsFlush()):oe()},checkElems:oe=function(e){var t;(e=!0===e)&&(X=33),K||(K=!0,(t=V-(n.now()-Q))<0&&(t=0),e||t<9?Y():y(Y,t))},unveil:ce=function(e){var t,a,r,i;e._lazyRace||(!(i="auto"==(r=(a=Z.test(e.nodeName))&&(e[v](h.sizesAttr)||e[v]("sizes"))))&&j||!a||!e[v]("src")&&!e.srcset||e.complete||E(e,h.errorClass)||!E(e,h.lazyClass))&&(t=x(e,"lazyunveilread").detail,i&&Ce.updateElem(e,!0,e.offsetWidth),e._lazyRace=!0,re++,de(e,t,i,r,a))},_aLSL:ye});function ge(e){re--,e&&!(re<0)&&e.target||(re=0)}function he(){var e,t,a,r,i,n,o,s,l,d,c,u=g.elements;if((O=h.loadMode)&&re<8&&(e=u.length)){for(t=0,ie++;t<e;t++)if(u[t]&&!u[t]._lazyRace)if(!te||g.prematureUnveil&&g.prematureUnveil(u[t]))ce(u[t]);else if((o=u[t][v]("data-expand"))&&(i=+o)||(i=ae),l||(l=!h.expand||h.expand<1?500<p.clientHeight&&500<p.clientWidth?500:370:h.expand,d=(g._defEx=l)*h.expFactor,c=h.hFac,U=null,ae<d&&re<1&&2<ie&&2<O&&!f.hidden?(ae=d,ie=0):ae=1<O&&1<ie&&re<6?l:0),s!==i&&(q=innerWidth+i*c,F=innerHeight+i,n=-1*i,s=i),d=u[t].getBoundingClientRect(),($=d.bottom)>=n&&(J=d.top)<=F&&(D=d.right)>=n*c&&(P=d.left)<=q&&($||D||P||J)&&(h.loadHidden||ne(u[t]))&&(j&&re<3&&!o&&(O<3||ie<4)||function(e,t){var a,r=e,i=ne(e);for(J-=t,$+=t,P-=t,D+=t;i&&(r=r.offsetParent)&&r!=f.body&&r!=p;)(i=0<(N(r,"opacity")||1))&&"visible"!=N(r,"overflow")&&(a=r.getBoundingClientRect(),i=D>a.left&&P<a.right&&$>a.top-1&&J<a.bottom+1);return i}(u[t],i))){if(ce(u[t]),r=!0,9<re)break}else!r&&j&&!a&&re<4&&ie<4&&2<O&&(T[0]||h.preloadAfterLoad)&&(T[0]||!o&&($||D||P||J||"auto"!=u[t][v](h.sizesAttr)))&&(a=T[0]||u[t]);a&&!r&&ce(a)}}function me(){K=!1,Q=n.now(),G()}function pe(e){var t=e.target;t._lazyCache?delete t._lazyCache:(ge(e),c(t,h.loadedClass),u(t,h.loadingClass),L(t,le),x(t,"lazyloaded"))}function ve(e){var t,a=e[v](h.srcsetAttr);(t=h.customMedia[e[v]("data-media")||e[v]("media")])&&e.setAttribute("media",t),a&&e.setAttribute("srcset",a)}function ye(){3==h.loadMode&&(h.loadMode=2),ue()}function be(){j||(n.now()-R<999?y(be,999):(j=!0,h.loadMode=3,oe(),e("scroll",ye,!0)))}var ze,we,_e,Ce=(we=H(function(e,t,a,r){var i,n,o;if(e._lazysizesWidth=r,e.setAttribute("sizes",r+="px"),w.test(t.nodeName||""))for(n=0,o=(i=t.getElementsByTagName("source")).length;n<o;n++)i[n].setAttribute("sizes",r);a.detail.dataAttr||m(e,a.detail)}),{_:function(){ze=f.getElementsByClassName(h.autosizesClass),e("resize",_e)},checkElems:_e=I(function(){var e,t=ze.length;if(t)for(e=0;e<t;e++)Ae(ze[e])}),updateElem:Ae});function Ae(e,t,a){var r=e.parentNode;r&&(a=M(e,r,a),(t=x(e,"lazybeforesizes",{width:a,dataAttr:!!t})).defaultPrevented||(a=t.detail.width)&&a!==e._lazysizesWidth&&we(e,r,t,a))}function Ee(){!Ee.i&&f.getElementsByClassName&&(Ee.i=!0,Ce._(),fe._())}return y(function(){h.init&&Ee()}),g={cfg:h,autoSizer:Ce,loader:fe,init:Ee,uP:m,aC:c,rC:u,hC:E,fire:x,gW:M,rAF:W}});
     1var ewww_webp_supported,swis_lazy_css_images;void 0===ewww_webp_supported&&(ewww_webp_supported=!1),void 0===swis_lazy_css_images&&(swis_lazy_css_images={}),window.lazySizesConfig=window.lazySizesConfig||{},window.lazySizesConfig.expand=500<document.documentElement.clientHeight&&500<document.documentElement.clientWidth?1e3:740,window.lazySizesConfig.iframeLoadMode=1,"undefined"==typeof eio_lazy_vars&&(eio_lazy_vars={exactdn_domain:".exactdn.com",threshold:0,skip_autoscale:0,use_dpr:0}),50<eio_lazy_vars.threshold&&(window.lazySizesConfig.expand=eio_lazy_vars.threshold);for(const[a,b]of Object.entries(swis_lazy_css_images))try{document.querySelectorAll(b[0].selector).forEach(e=>{e.classList.contains("lazyload")||(e.classList.add("lazyload"),e.dataset.swisLazyId=a,5<b[0].rwidth&&5<b[0].rheight&&(e.dataset.eioRwidth=b[0].rwidth,e.dataset.eioRheight=b[0].rheight))})}catch(e){}!function(e,t){function a(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)}t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):"function"==typeof define&&define.amd?define(["lazysizes"],t):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(e,n,s){"use strict";var o,l,d={};function c(e,t,a){var i,r;d[e]||(i=n.createElement(t?"link":"script"),r=n.getElementsByTagName("script")[0],t?(i.rel="stylesheet",i.href=e):(i.onload=function(){i.onerror=null,i.onload=null,a()},i.onerror=i.onload,i.src=e),d[e]=!0,d[i.src||i.href]=!0,r.parentNode.insertBefore(i,r))}n.addEventListener&&(l=/\(|\)|\s|'/,o=function(e,t){var a=n.createElement("img");a.onload=function(){a.onload=null,a.onerror=null,a=null,t()},a.onerror=a.onload,a.src=e,a&&a.complete&&a.onload&&a.onload()},addEventListener("lazybeforeunveil",function(e){var t,a,i;if(e.detail.instance==s&&!e.defaultPrevented){var r=e.target;if("none"==r.preload&&(r.preload=r.getAttribute("data-preload")||"auto"),null!=r.getAttribute("data-autoplay"))if(r.getAttribute("data-expand")&&!r.autoplay)try{r.play()}catch(e){}else requestAnimationFrame(function(){r.setAttribute("data-expand","-10"),s.aC(r,s.cfg.lazyClass)});(t=r.getAttribute("data-link"))&&c(t,!0),(t=r.getAttribute("data-script"))&&(e.detail.firesLoad=!0,c(t,null,function(){e.detail.firesLoad=!1,s.fire(r,"_lazyloaded",{},!0,!0)})),(t=r.getAttribute("data-require"))&&(s.cfg.requireJs?s.cfg.requireJs([t]):c(t)),(a=r.getAttribute("data-bg"))&&(e.detail.firesLoad=!0,o(a,function(){r.style.backgroundImage="url("+(l.test(a)?JSON.stringify(a):a)+")",e.detail.firesLoad=!1,s.fire(r,"_lazyloaded",{},!0,!0)})),(i=r.getAttribute("data-poster"))&&(e.detail.firesLoad=!0,o(i,function(){r.poster=i,e.detail.firesLoad=!1,s.fire(r,"_lazyloaded",{},!0,!0)}))}},!1))}),function(e,t){function a(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)}t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):"function"==typeof define&&define.amd?define(["lazysizes"],t):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(u,f,h){"use strict";var r;f.addEventListener&&(r=/\(|\)|\s|'/,addEventListener("lazybeforeunveil",function(t){var e,a,i;t.detail.instance==h&&(t.defaultPrevented||("none"==t.target.preload&&(t.target.preload="auto"),(a=t.target.dataset.back)&&(ewww_webp_supported&&(e=t.target.dataset.backWebp)&&(a=e),a=n(a,t.target),t.target.style.backgroundImage&&-1===t.target.style.backgroundImage.search(/^initial/)?0===a.search(/\[/)?((a=JSON.parse(a)).forEach(function(e){r.test(e)&&JSON.stringify(e)}),a='url("'+a.join('"), url("')+'"',e=t.target.style.backgroundImage+", "+a,t.target.style.backgroundImage=e):t.target.style.backgroundImage=t.target.style.backgroundImage+', url("'+(r.test(a)?JSON.stringify(a):a)+'")':0===a.search(/\[/)?((a=JSON.parse(a)).forEach(function(e){r.test(e)&&JSON.stringify(e)}),a='url("'+a.join('"), url("')+'"',t.target.style.backgroundImage=a):t.target.style.backgroundImage="url("+(r.test(a)?JSON.stringify(a):a)+")"),(a=t.target.dataset.swisLazyId)&&a in swis_lazy_css_images&&(a=swis_lazy_css_images[a],i=f.querySelector("style#swis-lazy-css-styles"),a.forEach(function(e){e.url&&(ewww_webp_supported&&e.webp_url&&(e.url=e.webp_url),e.url=n(e.url,t.target),e=e.selector+" {--swis-bg-"+e.hash+": url("+e.url+"); }",i.sheet.insertRule(e))}))))},!1));function g(e,t=!1){var a=y(),i=Math.round(e.offsetWidth*a),r=Math.round(e.offsetHeight*a),n=e.getAttribute("data-src"),a=e.getAttribute("data-src-webp");ewww_webp_supported&&a&&-1==n.search("webp=1")&&!t&&(n=a),o(e)&&(a=e,a=h.hC(a,"et_pb_jt_filterable_grid_item_image")||h.hC(a,"ss-foreground-image")||h.hC(a,"img-crop")?"img-crop":h.hC(a,"object-cover")&&(h.hC(a,"object-top")||h.hC(a,"object-bottom"))?"img-w":h.hC(a,"object-cover")&&(h.hC(a,"object-left")||h.hC(a,"object-right"))?"img-h":h.hC(a,"ct-image")&&h.hC(a,"object-cover")||!a.getAttribute("data-srcset")&&!a.srcset&&a.offsetHeight>a.offsetWidth&&1<d(a)?"img-crop":"img",(a=l(n,i,r,a,t))&&n!=a&&(t&&e.setAttribute("src",a),e.setAttribute("data-src",a)))}var n=function(e,t){if(0===e.search(/\[/))return e;if(!o(t))return e;var a=y();a<eio_lazy_vars.bg_min_dpr&&(a=eio_lazy_vars.bg_min_dpr);var i=Math.round(t.offsetWidth*a),r=Math.round(t.offsetHeight*a),n="bg";h.hC(t,"wp-block-cover")||h.hC(t,"wp-block-cover__image-background")?(h.hC(t,"has-parallax")?(i=Math.round(u.screen.width*a),r=Math.round(u.screen.height*a)):r<300&&(r=430),n="bg-cover"):(h.hC(t,"cover-image")||h.hC(t,"elementor-bg")||h.hC(t,"et_parallax_bg")||h.hC(t,"bg-image-crop"))&&(n="bg-cover");var s=d(t);if("bg"==n&&1<r&&1<i&&0<s){a=Math.ceil(r*s),s=Math.ceil(i/s);i+2<a&&(i=a),r+2<s&&(r=s);t=p(t);if(Math.abs(t.w-i)<5||Math.abs(t.h-r)<5)return e}return e=l(e,i,r,n)},o=function(e){if(1==eio_lazy_vars.skip_autoscale)return!1;for(var t=e,a=0;a<=7;a++){if(t.hasAttributes())for(var i=t.attributes,r=/skip-autoscale/,a=i.length-1;0<=a;a--){if(r.test(i[a].name))return!1;if(r.test(i[a].value))return!1}if(!t.parentNode||1!==t.parentNode.nodeType||!t.parentNode.hasAttributes)break;t=t.parentNode}return!0},l=function(e,t,a,i,r=!1){if(null===e)return e;var n=/w=(\d+)/,s=/fit=(\d+),(\d+)/,o=/resize=(\d+),(\d+)/,l=decodeURIComponent(e);if(/\.svg(\?.+)?$/.exec(l))return e;if(0<e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)){var d=o.exec(l);if(d&&(t<d[1]||r))return"img-w"===i?l.replace(o,"w="+t):"img-h"===i?l.replace(o,"h="+a):l.replace(o,"resize="+t+","+a);o=n.exec(e);if(o&&(t<=o[1]||r)){if("img-h"===i)return l.replace(n,"h="+a);if("bg-cover"!==i&&"img-crop"!==i)return e.replace(n,"w="+t);var c=Math.abs(o[1]-t);return 20<c||a<1080?e.replace(n,"resize="+t+","+a):e}c=s.exec(l);if(c&&(t<c[1]||r)){if("bg-cover"!==i&&"img-crop"!==i)return"img-w"===i?l.replace(s,"w="+t):"img-h"===i?l.replace(s,"h="+a):l.replace(s,"fit="+t+","+a);l=Math.abs(c[1]-t),s=Math.abs(c[2]-a);return 20<l||20<s?e.replace(n,"resize="+t+","+a):e}if(!o&&!c&&!d)return"img"===i?e+"&fit="+t+","+a:"bg-cover"===i||"img-crop"===i?e+"&resize="+t+","+a:"img-h"===i||t<a?e+"&h="+a:e+"&w="+t}return-1==e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)?"img"===i?e+"?fit="+t+","+a:"bg-cover"===i||"img-crop"===i?e+"?resize="+t+","+a:"img-h"===i||t<a?e+"?h="+a:e+"?w="+t:e},m=function(e){e=/-(\d+)x(\d+)\./.exec(e);return e&&1<e[1]&&1<e[2]?{w:e[1],h:e[2]}:{w:0,h:0}},p=function(e){var t=e.dataset.eioRwidth,e=e.dataset.eioRheight;return 1<t&&1<e?{w:t,h:e}:{w:0,h:0}},d=function(e){var t=e.getAttribute("width"),a=e.getAttribute("height");if(1<t&&1<a)return t/a;a=!1;if(a=(a=e.src&&-1<e.src.search("http")?e.src:a)||e.getAttribute("data-src")){var i=m(a);if(i.w&&i.h)return i.w/i.h}i=p(e);if(i.w&&i.h)return i.w/i.h;e=function(e){var t;if(e.srcset?t=e.srcset.split(","):(e=e.getAttribute("data-srcset"))&&(t=e.split(",")),t){var a=0,i=t.length;if(i){for(;a<i;a++){var r,n=t[a].trim().split(" ");!n[0].length||(n=m(n[0])).w&&n.h&&(r=n)}if(r.w&&r.h)return r}}return{w:0,h:0}}(e);return e.w&&e.h?e.w/e.h:0},y=function(){return eio_lazy_vars.use_dpr&&1<u.devicePixelRatio?u.devicePixelRatio:1};f.addEventListener("lazybeforesizes",function(e){e.target.getAttribute("data-src");var t=d(e.target);1<e.target.clientHeight&&t&&(t=Math.ceil(t*e.target.clientHeight),e.detail.width+2<t&&(e.detail.width=t)),void 0!==e.target._lazysizesWidth&&e.detail.width<e.target._lazysizesWidth&&(e.detail.width=e.target._lazysizesWidth),!eio_lazy_vars.use_dpr&&1<u.devicePixelRatio&&(e.detail.width=Math.ceil(e.detail.width/u.devicePixelRatio))}),f.addEventListener("lazybeforeunveil",function(e){var t,a,i,r,n=e.target,s=n.getAttribute("data-srcset");n.naturalWidth&&!s&&1<n.naturalWidth&&1<n.naturalHeight&&(t=y(),a=n.naturalWidth,i=n.naturalHeight,(e=p(n)).w&&e.w>a&&(a=e.w,i=e.h),a=n.clientWidth&&1.25*n.clientWidth*t<a,i=n.clientHeight&&1.25*n.clientHeight*t<i,(a||i)&&g(n)),ewww_webp_supported&&(!s||(r=n.getAttribute("data-srcset-webp"))&&n.setAttribute("data-srcset",r),(r=n.getAttribute("data-src-webp"))&&n.setAttribute("data-src",r))});function e(e=!1){e.type&&"load"===e.type&&h.autoSizer.checkElems(),y();var t,a=f.getElementsByClassName(h.cfg.loadedClass),i=a.length;if(i)for(t=0;t<i;t++){var r,n,s,o,l,d,c=a[t];c.src&&!c.srcset&&1<c.naturalWidth&&1<c.naturalHeight&&1<c.clientWidth&&1<c.clientHeight&&(r=c.naturalWidth,n=c.naturalHeight,s=u.innerWidth,o=u.innerHeight,l=p(c),d=m(c.src),l.w?s=l.w:d.w&&(s=d.w),l.h?o=l.h:d.h&&(o=d.h),l=c.clientWidth,d=c.clientHeight,(1.1*r<l&&l<=s||1.1*n<d&&d<=o)&&g(c,!0))}}var t,a,i,s,c=(t=e,s=function(){a=null,t()},function(){i=Date.now(),a=a||setTimeout(v,99)});function v(){var e=Date.now()-i;e<99?setTimeout(v,99-e):(u.requestIdleCallback||s)(s)}addEventListener("load",e),addEventListener("resize",c),setTimeout(e,2e4)}),function(e,t){t=t(e,e.document,Date);e.lazySizes=t,"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:{},function(i,f,n){"use strict";var h,g;if(!function(){var e,t={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:!0,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:!0,ricTimeout:0,throttleDelay:125};for(e in g=i.lazySizesConfig||i.lazysizesConfig||{},t)e in g||(g[e]=t[e])}(),!f||!f.getElementsByClassName)return{init:function(){},cfg:g,noSupport:!0};function c(e,t){E(e,t)||e.setAttribute("class",(e[y]("class")||"").trim()+" "+t)}function u(e,t){(t=E(e,t))&&e.setAttribute("class",(e[y]("class")||"").replace(t," "))}function m(e,t){var a;!l&&(a=i.picturefill||g.pf)?(t&&t.src&&!e[y]("srcset")&&e.setAttribute("srcset",t.src),a({reevaluate:!0,elements:[e]})):t&&t.src&&(e.src=t.src)}var a,r,t,s,o,p=f.documentElement,l=i.HTMLPictureElement,d="addEventListener",y="getAttribute",e=i[d].bind(i),v=i.setTimeout,z=i.requestAnimationFrame||v,b=i.requestIdleCallback,w=/^picture$/i,_=["load","error","lazyincluded","_lazyloaded"],C={},A=Array.prototype.forEach,E=function(e,t){return C[t]||(C[t]=new RegExp("(\\s|^)"+t+"(\\s|$)")),C[t].test(e[y]("class")||"")&&C[t]},L=function(t,a,e){var i=e?d:"removeEventListener";e&&L(t,a),_.forEach(function(e){t[i](e,a)})},x=function(e,t,a,i,r){var n=f.createEvent("Event");return(a=a||{}).instance=h,n.initEvent(t,!i,!r),n.detail=a,e.dispatchEvent(n),n},M=function(e,t){return(getComputedStyle(e,null)||{})[t]},N=function(e,t,a){for(a=a||e.offsetWidth;a<g.minSize&&t&&!e._lazysizesWidth;)a=t.offsetWidth,t=t.parentNode;return a},S=(s=[],o=t=[],k._lsFlush=W,k);function W(){var e=o;for(o=t.length?s:t,r=!(a=!0);e.length;)e.shift()();a=!1}function k(e,t){a&&!t?e.apply(this,arguments):(o.push(e),r||(r=!0,(f.hidden?v:z)(W)))}function H(a,e){return e?function(){S(a)}:function(){var e=this,t=arguments;S(function(){a.apply(e,t)})}}function R(e){function t(){var e=n.now()-i;e<99?v(t,99-e):(b||r)(r)}var a,i,r=function(){a=null,e()};return function(){i=n.now(),a=a||v(t,99)}}var I,j,T,O,q,B,P,F,J,D,$,U,G,K,Q,V,X,Y,Z,ee,te,ae,ie,re,ne,se,oe,le,de,ce,ue,fe=(Z=/^img$/i,ee=/^iframe$/i,te="onscroll"in i&&!/(gle|ing)bot/.test(navigator.userAgent),re=-1,ne=function(e){return(U=null==U?"hidden"==M(f.body,"visibility"):U)||!("hidden"==M(e.parentNode,"visibility")&&"hidden"==M(e,"visibility"))},G=ge,Q=ie=ae=0,V=g.throttleDelay,X=g.ricTimeout,Y=b&&49<X?function(){b(me,{timeout:X}),X!==g.ricTimeout&&(X=g.ricTimeout)}:H(function(){v(me)},!0),oe=H(pe),le=function(e){oe({target:e.target})},de=H(function(t,e,a,i,r){var n,s,o,l,d;(o=x(t,"lazybeforeunveil",e)).defaultPrevented||(i&&(a?c(t,g.autosizesClass):t.setAttribute("sizes",i)),n=t[y](g.srcsetAttr),a=t[y](g.srcAttr),r&&(s=(d=t.parentNode)&&w.test(d.nodeName||"")),l=e.firesLoad||"src"in t&&(n||a||s),o={target:t},c(t,g.loadingClass),l&&(clearTimeout(T),T=v(he,2500),L(t,le,!0)),s&&A.call(d.getElementsByTagName("source"),ye),n?t.setAttribute("srcset",n):a&&!s&&(ee.test(t.nodeName)?(i=a,0==(d=(e=t).getAttribute("data-load-mode")||g.iframeLoadMode)?e.contentWindow.location.replace(i):1==d&&(e.src=i)):t.src=a),r&&(n||s)&&m(t,{src:a})),t._lazyRace&&delete t._lazyRace,u(t,g.lazyClass),S(function(){var e=t.complete&&1<t.naturalWidth;l&&!e||(e&&c(t,g.fastLoadedClass),pe(o),t._lazyCache=!0,v(function(){"_lazyCache"in t&&delete t._lazyCache},9)),"lazy"==t.loading&&ie--},!0)}),ue=R(function(){g.loadMode=3,se()}),{_:function(){q=n.now(),h.elements=f.getElementsByClassName(g.lazyClass),I=f.getElementsByClassName(g.lazyClass+" "+g.preloadClass),e("scroll",se,!0),e("resize",se,!0),e("pageshow",function(e){var t;!e.persisted||(t=f.querySelectorAll("."+g.loadingClass)).length&&t.forEach&&z(function(){t.forEach(function(e){e.complete&&ce(e)})})}),i.MutationObserver?new MutationObserver(se).observe(p,{childList:!0,subtree:!0,attributes:!0}):(p[d]("DOMNodeInserted",se,!0),p[d]("DOMAttrModified",se,!0),setInterval(se,999)),e("hashchange",se,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach(function(e){f[d](e,se,!0)}),/d$|^c/.test(f.readyState)?ze():(e("load",ze),f[d]("DOMContentLoaded",se),v(ze,2e4)),h.elements.length?(ge(),S._lsFlush()):se()},checkElems:se=function(e){var t;(e=!0===e)&&(X=33),K||(K=!0,(t=V-(n.now()-Q))<0&&(t=0),e||t<9?Y():v(Y,t))},unveil:ce=function(e){var t,a,i,r;e._lazyRace||(!(r="auto"==(i=(a=Z.test(e.nodeName))&&(e[y](g.sizesAttr)||e[y]("sizes"))))&&j||!a||!e[y]("src")&&!e.srcset||e.complete||E(e,g.errorClass)||!E(e,g.lazyClass))&&(t=x(e,"lazyunveilread").detail,r&&Ce.updateElem(e,!0,e.offsetWidth),e._lazyRace=!0,ie++,de(e,t,r,i,a))},_aLSL:ve});function he(e){ie--,e&&!(ie<0)&&e.target||(ie=0)}function ge(){var e,t,a,i,r,n,s,o,l,d,c,u=h.elements;if((O=g.loadMode)&&ie<8&&(e=u.length)){for(t=0,re++;t<e;t++)if(u[t]&&!u[t]._lazyRace)if(!te||h.prematureUnveil&&h.prematureUnveil(u[t]))ce(u[t]);else if((s=u[t][y]("data-expand"))&&(r=+s)||(r=ae),l||(l=!g.expand||g.expand<1?500<p.clientHeight&&500<p.clientWidth?500:370:g.expand,d=(h._defEx=l)*g.expFactor,c=g.hFac,U=null,ae<d&&ie<1&&2<re&&2<O&&!f.hidden?(ae=d,re=0):ae=1<O&&1<re&&ie<6?l:0),o!==r&&(B=innerWidth+r*c,P=innerHeight+r,n=-1*r,o=r),d=u[t].getBoundingClientRect(),($=d.bottom)>=n&&(F=d.top)<=P&&(D=d.right)>=n*c&&(J=d.left)<=B&&($||D||J||F)&&(g.loadHidden||ne(u[t]))&&(j&&ie<3&&!s&&(O<3||re<4)||function(e,t){var a,i=e,r=ne(e);for(F-=t,$+=t,J-=t,D+=t;r&&(i=i.offsetParent)&&i!=f.body&&i!=p;)(r=0<(M(i,"opacity")||1))&&"visible"!=M(i,"overflow")&&(a=i.getBoundingClientRect(),r=D>a.left&&J<a.right&&$>a.top-1&&F<a.bottom+1);return r}(u[t],r))){if(ce(u[t]),i=!0,9<ie)break}else!i&&j&&!a&&ie<4&&re<4&&2<O&&(I[0]||g.preloadAfterLoad)&&(I[0]||!s&&($||D||J||F||"auto"!=u[t][y](g.sizesAttr)))&&(a=I[0]||u[t]);a&&!i&&ce(a)}}function me(){K=!1,Q=n.now(),G()}function pe(e){var t=e.target;t._lazyCache?delete t._lazyCache:(he(e),c(t,g.loadedClass),u(t,g.loadingClass),L(t,le),x(t,"lazyloaded"))}function ye(e){var t,a=e[y](g.srcsetAttr);(t=g.customMedia[e[y]("data-media")||e[y]("media")])&&e.setAttribute("media",t),a&&e.setAttribute("srcset",a)}function ve(){3==g.loadMode&&(g.loadMode=2),ue()}function ze(){j||(n.now()-q<999?v(ze,999):(j=!0,g.loadMode=3,se(),e("scroll",ve,!0)))}var be,we,_e,Ce=(we=H(function(e,t,a,i){var r,n,s;if(e._lazysizesWidth=i,e.setAttribute("sizes",i+="px"),w.test(t.nodeName||""))for(n=0,s=(r=t.getElementsByTagName("source")).length;n<s;n++)r[n].setAttribute("sizes",i);a.detail.dataAttr||m(e,a.detail)}),{_:function(){be=f.getElementsByClassName(g.autosizesClass),e("resize",_e)},checkElems:_e=R(function(){var e,t=be.length;if(t)for(e=0;e<t;e++)Ae(be[e])}),updateElem:Ae});function Ae(e,t,a){var i=e.parentNode;i&&(a=N(e,i,a),(t=x(e,"lazybeforesizes",{width:a,dataAttr:!!t})).defaultPrevented||(a=t.detail.width)&&a!==e._lazysizesWidth&&we(e,i,t,a))}function Ee(){!Ee.i&&f.getElementsByClassName&&(Ee.i=!0,Ce._(),fe._())}return v(function(){g.init&&Ee()}),h={cfg:g,autoSizer:Ce,loader:fe,init:Ee,uP:m,aC:c,rC:u,hC:E,fire:x,gW:N,rAF:S}});
  • easy-image-optimizer/trunk/phpcs.ruleset.xml

    r3328397 r3398277  
    3232            <element value="imgQuality"/>
    3333            <element value="parentNode"/>
     34            <element value="nodeName"/>
    3435            <element value="nextSibling"/>
    3536            <element value="documentElement"/>
  • easy-image-optimizer/trunk/readme.txt

    r3350722 r3398277  
    22Contributors: nosilver4u
    33Tags: image, resize, webp, lazy load, compress
    4 Tested up to: 6.8
    5 Stable tag: 4.2.1
     4Tested up to: 6.9
     5Stable tag: 4.3.0
    66License: GPLv3
    77
     
    5656* If you would like to help translate this plugin in your language, get started here: https://translate.wordpress.org/projects/wp-plugins/easy-image-optimizer/
    5757
     58= 4.3.0 =
     59*Release Date - November 18, 2025*
     60
     61* added: Lazy Load support for background images in external CSS files
     62* added: View CDN bandwidth usage on settings page
     63* changed: Lazy Load checks parent element for skip-lazy class
     64* changed: Lazy Load auto-sizing honors High DPI setting
     65* changed: Easy IO fills in 450px wide image when responsive (srcset) images have a gap
     66* improved: Lazy Load performance when searching for img elements
     67* improved: Lazy Load placeholder generation is faster and works better with Safari
     68* fixed: Lazy Load for iframes breaks WP Remote Users Sync plugin
     69
    5870= 4.2.1 =
    5971*Release Date - August 26, 2025*
Note: See TracChangeset for help on using the changeset viewer.