PluginProbe
Autoptimize / 3.1.16
Autoptimize v3.1.16
3.1.16 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 All 108 releases
← All changes | classes/autoptimizeImages.php +475 -155 2.7.6 → 3.1.16 View file →
@@ -21,8 +21,15 @@
21 21 *
22 22 * @var self|null
23 23 */
24 24 protected static $instance = null;
25 +
26 + /**
27 + * lazyload counter.
28 + *
29 + * @var int
30 + */
31 + protected $lazyload_counter = 0;
25 32
26 33 public function __construct( array $options = array() )
27 34 {
28 35 // If options are not provided, fetch them.
@@ -50,10 +57,30 @@
50 57
51 58 // get service availability and add it to the options-array.
52 59 $value['availabilities'] = autoptimizeOptionWrapper::get_option( 'autoptimize_service_availablity' );
53 60
54 - if ( empty( $value['availabilities'] ) ) {
55 - $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
61 + if ( empty( $value['availabilities'] ) || ! is_array( $value['availabilities'] ) ) {
62 + $value['availabilities'] = null;
63 +
64 + if ( true === autoptimizeImages::imgopt_active() ) {
65 + $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
66 + }
67 +
68 + if ( null === $value['availabilities'] ) {
69 + // We can't seem to check service availability, use mock result with imgopt status UP.
70 + $_mock_settings = array(
71 + 'extra_imgopt' => array(
72 + 'status' => 'up',
73 + 'hosts' => array(
74 + '1' => 'https://sp-ao.shortpixel.ai/',
75 + ),
76 + ),
77 + 'critcss' => array(
78 + 'status' => 'up',
79 + ),
80 + );
81 + $value['availabilities'] = $_mock_settings;
82 + }
56 83 }
57 84
58 85 return $value;
59 86 }
@@ -112,9 +139,11 @@
112 139 if ( ! $this->should_run() ) {
113 140 if ( $this->should_lazyload() ) {
114 141 add_filter(
115 142 'wp_lazy_loading_enabled',
116 - '__return_false'
143 + array( $this, 'should_disable_core_lazyload' ),
144 + 10,
145 + 3
117 146 );
118 147 add_filter(
119 148 'autoptimize_html_after_minify',
120 149 array( $this, 'filter_lazyload_images' ),
@@ -143,8 +172,9 @@
143 172 $active = true;
144 173 }
145 174
146 175 if ( apply_filters( 'autoptimize_filter_imgopt_do_css', true ) ) {
176 + // fixme: also act on already minified CSS!
147 177 add_filter(
148 178 'autoptimize_filter_base_replace_cdn',
149 179 array( $this, 'filter_optimize_css_images' ),
150 180 10,
@@ -149,8 +179,16 @@
149 179 array( $this, 'filter_optimize_css_images' ),
150 180 10,
151 181 1
152 182 );
183 +
184 + add_filter(
185 + 'autoptimize_html_after_minify',
186 + array( $this, 'filter_optimize_inline_css_images' ),
187 + 10,
188 + 1
189 + );
190 +
153 191 $active = true;
154 192 }
155 193
156 194 if ( $active ) {
@@ -164,9 +202,11 @@
164 202
165 203 if ( $this->should_lazyload() ) {
166 204 add_filter(
167 205 'wp_lazy_loading_enabled',
168 - '__return_false'
206 + array( $this, 'should_disable_core_lazyload' ),
207 + 10,
208 + 3
169 209 );
170 210 add_action(
171 211 'wp_footer',
172 212 array( $this, 'add_lazyload_js_footer' ),
@@ -176,8 +216,24 @@
176 216 }
177 217 }
178 218
179 219 /**
220 + * Disables core's native lazyload for images, not for iframes.
221 + *
222 + * @param bool $flag Incoming flag (mostly true).
223 + * @param string $tag Tag (img or iframe).
224 + * @param string $context Full context.
225 + *
226 + * @return bool
227 + */
228 + public function should_disable_core_lazyload( $flag = true, $tag = '', $context = '' ) {
229 + if ( 'img' === $tag ) {
230 + return false;
231 + }
232 + return $flag;
233 + }
234 +
235 + /**
180 236 * Basic checks before we can run.
181 237 *
182 238 * @return bool
183 239 */
@@ -209,9 +265,9 @@
209 265 {
210 266 static $imgopt_host = null;
211 267
212 268 if ( null === $imgopt_host ) {
213 - $imgopt_host = 'https://cdn.shortpixel.ai/';
269 + $imgopt_host = 'https://sp-ao.shortpixel.ai/';
214 270 $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
215 271 if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
216 272 $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
217 273 }
@@ -229,9 +285,9 @@
229 285 }
230 286
231 287 public static function get_service_url_suffix()
232 288 {
233 - $suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
289 + $suffix = '/af/ZQXTBY0109483/' . AUTOPTIMIZE_SITE_DOMAIN;
234 290
235 291 return $suffix;
236 292 }
237 293
@@ -344,11 +400,9 @@
344 400 // Default to (the trimmed version of) what was given to us.
345 401 $result = trim( $in );
346 402
347 403 // Some silly plugins wrap background images in html-encoded quotes, so remove those from the img url.
348 - if ( strpos( $result, '"' ) !== false ) {
349 - $result = str_replace( '"', '', $result );
350 - }
404 + $result = $this->fix_silly_bgimg_quotes( $result );
351 405
352 406 if ( autoptimizeUtils::is_protocol_relative( $result ) ) {
353 407 $result = $parsed_site_url['scheme'] . ':' . $result;
354 408 } elseif ( 0 === strpos( $result, '/' ) ) {
@@ -353,12 +407,18 @@
353 407 $result = $parsed_site_url['scheme'] . ':' . $result;
354 408 } elseif ( 0 === strpos( $result, '/' ) ) {
355 409 // Root-relative...
356 410 $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $result;
357 - } elseif ( ! empty( $cdn_domain ) && strpos( $result, $cdn_domain ) !== 0 ) {
411 + } elseif ( ! empty( $cdn_domain ) && false === strpos( $this->get_imgopt_host(), $cdn_domain ) && strpos( $result, $cdn_domain ) !== 0 ) {
412 + // remove CDN except if it is the image optimization one.
358 413 $result = str_replace( $cdn_domain, $parsed_site_url['host'], $result );
359 414 }
360 415
416 + // filter (default off) to remove QS from image URL's to avoid eating away optimization credits.
417 + if ( apply_filters( 'autoptimize_filter_imgopt_no_querystring', false ) && strpos( $result, '?' ) !== false ) {
418 + $result = strtok( $result, '?' );
419 + }
420 +
361 421 $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );
362 422
363 423 // Store in cache.
364 424 $cache[ $in ] = $result;
@@ -370,15 +430,41 @@
370 430 public function filter_optimize_css_images( $in )
371 431 {
372 432 $in = $this->normalize_img_url( $in );
373 433
374 - if ( $this->can_optimize_image( $in ) ) {
434 + if ( $this->can_optimize_image( $in ) && false === strpos( $in, $this->get_imgopt_host() ) ) {
375 435 return $this->build_imgopt_url( $in, '', '' );
376 436 } else {
377 437 return $in;
378 438 }
379 439 }
440 +
441 + public function filter_optimize_inline_css_images( $html ) {
442 + preg_match_all( '#<style[^>]*>([^<]*)</style>#Um', $html, $inline_css_blocks, PREG_SET_ORDER );
443 + foreach ( $inline_css_blocks as $inline_css_block ) {
444 + if ( false !== strpos( $inline_css_block[0], 'background' ) ) {
445 + $inline_css_block_new = $this->replace_background_img_css( $inline_css_block[0] );
446 + if ( $inline_css_block_new !== $inline_css_block[0] ) {
447 + $html = str_replace( $inline_css_block[0], $inline_css_block_new, $html );
448 + }
449 + }
450 + }
451 + return $html;
452 + }
380 453
454 + public static function replace_background_img_css( $css ) {
455 + // fixme; can/ should we cache these?
456 + preg_match_all( '#background[^;}]*url\((.*)\)#Ui', $css, $backgrounds, PREG_SET_ORDER );
457 + if ( is_array( $backgrounds ) && ! empty( $backgrounds ) ) {
458 + foreach ( $backgrounds as $background ) {
459 + if ( autoptimizeImages::can_optimize_image_wrapper( $background[1] ) ) {
460 + $css = str_replace( $background[1], autoptimizeImages::build_imgopt_url_wrapper( $background[1] ), $css );
461 + }
462 + }
463 + }
464 + return $css;
465 + }
466 +
381 467 private function get_imgopt_base_url()
382 468 {
383 469 static $imgopt_base_url = null;
384 470
@@ -385,9 +471,15 @@
385 471 if ( null === $imgopt_base_url ) {
386 472 $imgopt_host = $this->get_imgopt_host();
387 473 $quality = $this->get_img_quality_string();
388 474 $ret_val = apply_filters( 'autoptimize_filter_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
389 - $imgopt_base_url = $imgopt_host . 'client/' . $quality . ',' . $ret_val;
475 + if ( $this->should_ngimg() ) {
476 + $sp_to_string = 'to_auto';
477 + } else {
478 + $sp_to_string = 'to_webp';
479 + }
480 + $sp_to_string = apply_filters( 'autoptimize_filter_imgopt_format', $sp_to_string ); // values: empty (= jpeg), to_webp (smart; webp or fallback), to_avif (avif or fallback) or to_auto (smart avif, webp or fallback).
481 + $imgopt_base_url = $imgopt_host . 'client/' . $sp_to_string . ',' . $quality . ',' . $ret_val;
390 482 $imgopt_base_url = apply_filters( 'autoptimize_filter_imgopt_base_url', $imgopt_base_url );
391 483 }
392 484
393 485 return $imgopt_base_url;
@@ -392,9 +484,14 @@
392 484
393 485 return $imgopt_base_url;
394 486 }
395 487
396 - private function can_optimize_image( $url )
488 + public static function can_optimize_image_wrapper( $url, $tag = '', $testing = false ) {
489 + $self = new self();
490 + return $self->can_optimize_image( $url, $tag = '', $testing = false );
491 + }
492 +
493 + private function can_optimize_image( $url, $tag = '', $testing = false )
397 494 {
398 495 static $cdn_url = null;
399 496 static $nopti_images = null;
400 497
@@ -404,10 +501,13 @@
404 501 autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' )
405 502 );
406 503 }
407 504
408 - if ( null === $nopti_images ) {
409 - $nopti_images = apply_filters( 'autoptimize_filter_imgopt_noptimize', '' );
505 + if ( null === $nopti_images || $testing ) {
506 + if ( is_array( $this->options ) && array_key_exists( 'autoptimize_imgopt_text_field_6', $this->options ) ) {
507 + $nopti_images = $this->options['autoptimize_imgopt_text_field_6'];
508 + }
509 + $nopti_images = apply_filters( 'autoptimize_filter_imgopt_noptimize', $nopti_images );
410 510 }
411 511
412 512 $site_host = AUTOPTIMIZE_SITE_DOMAIN;
413 513 $url = $this->normalize_img_url( $url );
@@ -412,21 +512,25 @@
412 512 $site_host = AUTOPTIMIZE_SITE_DOMAIN;
413 513 $url = $this->normalize_img_url( $url );
414 514 $url_parsed = parse_url( $url );
415 515
416 - if ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
516 + if ( false === is_array( $url_parsed ) ) {
417 517 return false;
518 + } elseif ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
519 + return false;
520 + } elseif ( autoptimizeUtils::is_local_server() ) {
521 + return false;
418 522 } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
419 523 return false;
420 524 } elseif ( strpos( $url, '.php' ) !== false ) {
421 525 return false;
422 - } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
526 + } elseif ( false === array_key_exists( 'path', $url_parsed ) || str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp', '.avif' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
423 527 // fixme: better check against end of string.
424 528 return false;
425 529 } elseif ( ! empty( $nopti_images ) ) {
426 530 $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
427 531 foreach ( $nopti_images_array as $nopti_image ) {
428 - if ( strpos( $url, $nopti_image ) !== false ) {
532 + if ( strpos( $url, $nopti_image ) !== false || ( ( '' !== $tag && strpos( $tag, $nopti_image ) !== false ) ) ) {
429 533 return false;
430 534 }
431 535 }
432 536 }
@@ -432,8 +536,14 @@
432 536 }
433 537 return true;
434 538 }
435 539
540 + // wrapper for reuse in AOPro.
541 + public static function build_imgopt_url_wrapper( $orig_url, $width = 0, $height = 0 ) {
542 + $self = new self();
543 + return $self->build_imgopt_url( $orig_url, $width = 0, $height = 0 );
544 + }
545 +
436 546 private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
437 547 {
438 548 // sanitize width and height.
439 549 if ( strpos( $width, '%' ) !== false ) {
@@ -456,9 +566,15 @@
456 566 if ( $filtered_url !== $orig_url ) {
457 567 return $filtered_url;
458 568 }
459 569
460 - $orig_url = $this->normalize_img_url( $orig_url );
570 + $normalized_url = $this->normalize_img_url( $orig_url );
571 +
572 + // if the URL is ascii we check if we have a real URL with filter_var (which only works on ascii url's) and if not a real URL we return the original one.
573 + if ( apply_filters( 'autoptimize_filter_imgopt_check_normalized_url', true ) && ! preg_match( '/[^\x20-\x7e]/', $normalized_url ) && false === filter_var( $normalized_url, FILTER_VALIDATE_URL ) ) {
574 + return $orig_url;
575 + }
576 +
461 577 $imgopt_base_url = $this->get_imgopt_base_url();
462 578 $imgopt_size = '';
463 579
464 580 if ( $width && 0 !== $width ) {
@@ -468,9 +584,10 @@
468 584 if ( $height && 0 !== $height ) {
469 585 $imgopt_size .= ',h_' . $height;
470 586 }
471 587
472 - $url = $imgopt_base_url . $imgopt_size . '/' . $orig_url;
588 + $url = $imgopt_base_url . $imgopt_size . '/' . $normalized_url;
589 + $url = apply_filters( 'autoptimize_filter_imgopt_after_build_imgopt_url', $url );
473 590
474 591 return $url;
475 592 }
476 593
@@ -481,9 +598,9 @@
481 598
482 599 public function replace_img_callback( $matches, $width = 0, $height = 0 )
483 600 {
484 601 $_normalized_img_url = $this->normalize_img_url( $matches[1] );
485 - if ( $this->can_optimize_image( $matches[1] ) ) {
602 + if ( $this->can_optimize_image( $matches[1], $matches[0] ) ) {
486 603 return str_replace( $matches[1], $this->build_imgopt_url( $_normalized_img_url, $width, $height ), $matches[0] );
487 604 } else {
488 605 return $matches[0];
489 606 }
@@ -498,12 +615,18 @@
498 615 } else {
499 616 $width = 180;
500 617 $height = 180;
501 618 }
502 - return $this->replace_img_callback( $matches, $width, $height );
619 +
620 + // make sure we're not trying to optimize a *.ico file.
621 + if ( strpos( $matches[1], '.ico' ) === false ) {
622 + return $this->replace_img_callback( $matches, $width, $height );
623 + } else {
624 + return $matches[0];
625 + }
503 626 }
504 627
505 - public function filter_optimize_images( $in )
628 + public function filter_optimize_images( $in, $testing = false )
506 629 {
507 630 /*
508 631 * potential future functional improvements:
509 632 *
@@ -509,11 +632,12 @@
509 632 *
510 633 * filter for critical CSS.
511 634 */
512 635 $to_replace = array();
636 + $to_preload = '';
513 637
514 - // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
515 - if ( $this->should_lazyload() ) {
638 + // hide (no)script tags to avoid replacing (and potentially breaking) images in script tags.
639 + if ( apply_filters( 'autoptimize_filter_imgopt_hide_script', true ) || $this->should_lazyload() ) {
516 640 $in = autoptimizeBase::replace_contents_with_marker_if_exists(
517 641 'SCRIPT',
518 642 '<script',
519 643 '#<(?:no)?script.*?<\/(?:no)?script>#is',
@@ -520,11 +644,18 @@
520 644 $in
521 645 );
522 646 }
523 647
648 + // get img preloads as set in post metabox, exploding ", " instead of "," because LCP preload
649 + // could be a shortpixel URL, which has comma's and results in way too many preloads.
650 + $metabox_preloads = array_filter( array_map( 'trim', explode( ', ', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );
651 + $metabox_preloads = apply_filters( 'autoptimize_filter_images_metabox_preloads', $metabox_preloads );
652 +
524 653 // extract img tags.
525 654 if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
526 655 foreach ( $matches[0] as $tag ) {
656 + $tag = apply_filters( 'autoptimize_filter_imgopt_tag_preopt', $tag );
657 +
527 658 $orig_tag = $tag;
528 659 $imgopt_w = '';
529 660 $imgopt_h = '';
530 661
@@ -530,20 +661,22 @@
530 661
531 662 // first do (data-)srcsets.
532 663 if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
533 664 foreach ( $allsrcsets as $srcset ) {
534 - $srcset = $srcset[2];
535 - $srcsets = explode( ',', $srcset );
665 + $srcset = $srcset[2];
666 + $orig_srcset = $srcset;
667 + $srcsets = explode( ',', $srcset );
536 668 foreach ( $srcsets as $indiv_srcset ) {
537 669 $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
538 670 if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
539 671 $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
540 672 }
541 - if ( $this->can_optimize_image( $indiv_srcset_parts[0] ) ) {
673 + if ( $this->can_optimize_image( $indiv_srcset_parts[0], $tag, $testing ) && false === apply_filters( 'autoptimize_filter_imgopt_do_spai', false ) ) {
542 674 $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
543 - $tag = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
675 + $srcset = str_replace( $indiv_srcset_parts[0], $imgopt_url, $srcset );
544 676 }
545 677 }
678 + $tag = str_replace( $orig_srcset, $srcset, $tag );
546 679 }
547 680 }
548 681
549 682 // proceed with img src.
@@ -552,18 +685,29 @@
552 685 $imgopt_w = $_get_size['width'];
553 686 $imgopt_h = $_get_size['height'];
554 687
555 688 // then start replacing images src.
556 - if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
557 - foreach ( $urls as $url ) {
558 - $full_src_orig = $url[0];
559 - $url = $url[1];
560 - if ( $this->can_optimize_image( $url ) ) {
561 - $imgopt_url = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
562 - $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
563 - $tag = str_replace( $full_src_orig, $full_imgopt_src, $tag );
564 - }
689 + $tag = autoptimizeAttributeParser::modify_attributes( $tag, 'img', '/src$/i', function( $name, $url ) use ( $tag, $testing, $imgopt_w, $imgopt_h ) {
690 + // 1. Skip if empty or a data-URI
691 + if ( ! $url || strpos( $url, 'data:' ) === 0 ) {
692 + return $url;
565 693 }
694 +
695 + // 2. Run Autoptimize logic
696 + if ( $this->can_optimize_image( $url, $tag, $testing ) && ! apply_filters( 'autoptimize_filter_imgopt_do_spai', false ) ) {
697 + return $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
698 + }
699 +
700 + // 3. No changes needed
701 + return $url;
702 + }, false, true );
703 +
704 + $_url = autoptimizeAttributeParser::get( $tag, 'img', '/src$/i' );
705 + $url = $_url['src'];
706 +
707 + // check if the image needs to be prelaoded.
708 + if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && str_replace( $metabox_preloads, '', $tag ) !== $tag ) {
709 + $to_preload .= $this->create_img_preload_tag( $tag );
566 710 }
567 711
568 712 // do lazyload stuff.
569 713 if ( $this->should_lazyload( $in ) && ! empty( $url ) ) {
@@ -578,9 +722,9 @@
578 722
579 723 $_url = $this->normalize_img_url( $_url );
580 724
581 725 $placeholder = '';
582 - if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
726 + if ( $this->can_optimize_image( $_url, $tag ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', false, $_url ) && false === apply_filters( 'autoptimize_filter_imgopt_do_spai', false ) ) {
583 727 $lqip_w = '';
584 728 $lqip_h = '';
585 729 if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
586 730 $lqip_w = ',w_' . $imgopt_w;
@@ -593,8 +737,15 @@
593 737 // then call add_lazyload-function with lpiq placeholder if set.
594 738 $tag = $this->add_lazyload( $tag, $placeholder );
595 739 }
596 740
741 + // add decoding="async" behind filter, not sure if I'll make it default true yet.
742 + if ( true === apply_filters( 'autoptimize_filter_imgopt_add_decoding', true ) && false === strpos( $tag, ' decoding=' ) ) {
743 + $tag = str_replace( '<img ', '<img decoding="async" ', $tag );
744 + }
745 +
746 + $tag = apply_filters( 'autoptimize_filter_imgopt_tag_postopt', $tag );
747 +
597 748 // and add tag to array for later replacement.
598 749 if ( $tag !== $orig_tag ) {
599 750 $to_replace[ $orig_tag ] = $tag;
600 751 }
@@ -603,21 +754,27 @@
603 754
604 755 // and replace all.
605 756 $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
606 757
607 - // img thumbnails in e.g. woocommerce.
608 - if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_imgopt_datathumbs', true ) ) {
609 - $out = preg_replace_callback(
610 - '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
611 - array( $this, 'replace_data_thumbs' ),
612 - $out
613 - );
758 + // misc. node attributes that might hold image url's (incl. the previously separate data-thumb).
759 + $extra_attr_with_img = apply_filters( 'autoptimize_filter_imgopt_attr_with_img', array( array( 'div', 'data-thumb'), array( 'div', 'data-background' ), array( 'img', 'data-retina' ) ) );
760 + if ( ! empty( $extra_attr_with_img ) && is_array( $extra_attr_with_img ) ) {
761 + foreach ( $extra_attr_with_img as $candidate ) {
762 + if ( is_array( $candidate ) && strpos( $out, $candidate[1] ) !== false ) {
763 + $_regex = '/\<' . $candidate[0] . '(?:[^>]*)?\s' . $candidate[1] . '=(?:"|\')(.+?)(?:"|\')(?:[^>]*)?>/s';
764 + $out = preg_replace_callback(
765 + $_regex,
766 + array( $this, 'replace_img_callback' ),
767 + $out
768 + );
769 + }
770 + }
614 771 }
615 772
616 773 // background-image in inline style.
617 - if ( strpos( $out, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
774 + if ( ( strpos( $out, 'background-image:' ) !== false || strpos( $out, 'background:' ) !== false ) && strpos( $out, 'url(' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
618 775 $out = preg_replace_callback(
619 - '/style=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
776 + '/style=(?:"|\')[^<>]*?background(?:-image)?:[^;"\'()>]*url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
620 777 array( $this, 'replace_img_callback' ),
621 778 $out
622 779 );
623 780 }
@@ -624,31 +781,45 @@
624 781
625 782 // act on icon links.
626 783 if ( ( strpos( $out, '<link rel="icon"' ) !== false || ( strpos( $out, "<link rel='icon'" ) !== false ) ) && apply_filters( 'autoptimize_filter_imgopt_linkicon', true ) ) {
627 784 $out = preg_replace_callback(
628 - '/<link\srel=(?:"|\')(?:apple-touch-)?icon(?:"|\').*\shref=(?:"|\')(.*)(?:"|\')(?:\ssizes=(?:"|\')(\d*x\d*)(?:"|\'))?\s\/>/Um',
785 + '/<link\srel=(?:"|\')(?:apple-touch-)?icon(?:-precomposed)?(?:"|\').*\shref=(?:"|\')(.*)(?:"|\')(?:\ssizes=(?:"|\')(\d*x\d*)(?:"|\'))?\s?\/?>/Um',
629 786 array( $this, 'replace_icon_callback' ),
630 787 $out
631 788 );
632 789 }
633 790
634 - // lazyload: restore noscript tags + lazyload picture source tags and bgimage.
791 + // lazyload picture source tags and bgimage.
635 792 if ( $this->should_lazyload() ) {
793 + $out = $this->process_picture_tag( $out, true, true );
794 + $out = $this->process_bgimage( $out );
795 + } else {
796 + $out = $this->process_picture_tag( $out, true, false );
797 + }
798 +
799 + // restore (no)script tags.
800 + if ( apply_filters( 'autoptimize_filter_imgopt_hide_script', true ) || $this->should_lazyload() ) {
636 801 $out = autoptimizeBase::restore_marked_content(
637 802 'SCRIPT',
638 803 $out
639 804 );
805 + }
640 806
641 - $out = $this->process_picture_tag( $out, true, true );
642 - $out = $this->process_bgimage( $out );
643 - } else {
644 - $out = $this->process_picture_tag( $out, true, false );
807 + if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && empty( $to_preload ) && false !== apply_filters( 'autoptimize_filter_imgopt_dopreloads', true ) ) {
808 + // the preload was not in an img tag, so adding a non-responsive preload instead.
809 + foreach ( $metabox_preloads as $img_preload ) {
810 + $to_preload .= apply_filters( 'autoptimize_filter_imgopt_preload_tag_result', $this->kses_preload_link( '<link fetchpriority="high" rel="preload" href="' . $img_preload . '" as="image">' ) );
811 + }
645 812 }
646 813
814 + if ( ! empty( $to_preload ) ) {
815 + $out = autoptimizeExtra::inject_preloads( $to_preload, $out );
816 + }
817 +
647 818 return $out;
648 819 }
649 820
650 - public function get_size_from_tag( $tag ) {
821 + public static function get_size_from_tag( $tag ) {
651 822 // reusable function to extract widht and height from an image tag
652 823 // enforcing a filterable maximum width and height (default 4999X4999).
653 824 $width = '';
654 825 $height = '';
@@ -667,15 +838,19 @@
667 838 // check for and enforce (filterable) max sizes.
668 839 $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
669 840 if ( $width > $_max_width ) {
670 841 $_width = $_max_width;
671 - $height = $_width / $width * $height;
842 + if ( ! empty( $height ) && is_int( $height ) ) {
843 + $height = $_width / $width * $height;
844 + }
672 845 $width = $_width;
673 846 }
674 847 $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
675 848 if ( $height > $_max_height ) {
676 849 $_height = $_max_height;
677 - $width = $_height / $height * $width;
850 + if ( ! empty( $width ) && is_int( $width ) ) {
851 + $width = $_height / $height * $width;
852 + }
678 853 $height = $_height;
679 854 }
680 855
681 856 return array(
@@ -686,26 +861,32 @@
686 861
687 862 /**
688 863 * Lazyload functions
689 864 */
690 - public static function should_lazyload_wrapper() {
865 + public static function should_lazyload_wrapper( $no_meta = false ) {
691 866 // needed in autoptimizeMain.php.
692 867 $self = new self();
693 - return $self->should_lazyload();
868 + return $self->should_lazyload( '', $no_meta );
694 869 }
695 870
696 - public function should_lazyload( $context = '' ) {
871 + public function should_lazyload( $context = '', $no_meta = false ) {
697 872 if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && false === $this->check_nolazy() ) {
698 873 $lazyload_return = true;
699 874 } else {
700 875 $lazyload_return = false;
701 876 }
877 +
878 + // If page/ post check post_meta to see if lazyload is off for page.
879 + if ( false === $no_meta && false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_lazyload' ) ) {
880 + $lazyload_return = false;
881 + }
882 +
702 883 $lazyload_return = apply_filters( 'autoptimize_filter_imgopt_should_lazyload', $lazyload_return, $context );
703 884
704 885 return $lazyload_return;
705 886 }
706 887
707 - public function check_nolazy() {
888 + public static function check_nolazy() {
708 889 if ( array_key_exists( 'ao_nolazy', $_GET ) && '1' === $_GET['ao_nolazy'] ) {
709 890 return true;
710 891 } else {
711 892 return false;
@@ -715,8 +896,9 @@
715 896 public function filter_lazyload_images( $in )
716 897 {
717 898 // only used is image optimization is NOT active but lazyload is.
718 899 $to_replace = array();
900 + $to_preload = '';
719 901
720 902 // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
721 903 $out = autoptimizeBase::replace_contents_with_marker_if_exists(
722 904 'SCRIPT',
@@ -724,11 +906,20 @@
724 906 '#<(?:no)?script.*?<\/(?:no)?script>#is',
725 907 $in
726 908 );
727 909
728 - // extract img tags and add lazyload attribs.
910 + // get img preloads as set in post metabox.
911 + $metabox_preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );
912 +
913 + // extract img tags and add lazyload attribs/ add preloads.
729 914 if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
730 915 foreach ( $matches[0] as $tag ) {
916 + // check if image needs to be preloaded.
917 + if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && str_replace( $metabox_preloads, '', $tag ) !== $tag ) {
918 + $to_preload .= $this->create_img_preload_tag( $tag );
919 + }
920 +
921 + // and lazyloaded.
731 922 if ( $this->should_lazyload( $out ) ) {
732 923 $to_replace[ $tag ] = $this->add_lazyload( $tag );
733 924 }
734 925 }
@@ -746,14 +937,33 @@
746 937 'SCRIPT',
747 938 $out
748 939 );
749 940
941 + if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && empty( $to_preload ) && false !== apply_filters( 'autoptimize_filter_imgopt_dopreloads', true ) ) {
942 + // the preload was not in an img tag, so adding a non-responsive preload instead.
943 + foreach ( $metabox_preloads as $img_preload ) {
944 + $to_preload .= apply_filters( 'autoptimize_filter_imgopt_preload_tag_result', $this->kses_preload_link( '<link fetchpriority="high" rel="preload" href="' . $img_preload . '" as="image">' ) );
945 + }
946 + }
947 +
948 + if ( ! empty( $to_preload ) ) {
949 + $out = autoptimizeExtra::inject_preloads( $to_preload, $out );
950 + }
951 +
750 952 return $out;
751 953 }
752 954
753 955 public function add_lazyload( $tag, $placeholder = '' ) {
754 956 // adds actual lazyload-attributes to an image node.
755 - if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
957 + $this->lazyload_counter++;
958 +
959 + $_lazyload_from_nth = '';
960 + if ( array_key_exists( 'autoptimize_imgopt_number_field_7', $this->options ) ) {
961 + $_lazyload_from_nth = $this->options['autoptimize_imgopt_number_field_7'];
962 + }
963 + $_lazyload_from_nth = apply_filters( 'autoptimize_filter_imgopt_lazyload_from_nth', $_lazyload_from_nth );
964 +
965 + if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag && $this->lazyload_counter >= $_lazyload_from_nth ) {
756 966 $tag = $this->maybe_fix_missing_quotes( $tag );
757 967
758 968 // store original tag for use in noscript version.
759 969 $noscript_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>';
@@ -779,10 +989,11 @@
779 989 // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
780 990 $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
781 991 }
782 992
783 - $tag = preg_replace( '/(\s)src=/', ' src=\'' . $placeholder . '\' data-src=', $tag );
784 - $tag = preg_replace( '/(\s)srcset=/', ' data-srcset=', $tag );
993 + $tag = str_replace( ' src=', ' data-src=', $tag );
994 + $tag = str_replace( ' srcset=', ' data-srcset=', $tag );
995 + $tag = str_replace( '<img ', '<img src=\'' . $placeholder . '\' ', $tag );
785 996
786 997 // move sizes to data-sizes unless filter says no.
787 998 if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
788 999 $tag = str_replace( ' sizes=', ' data-sizes=', $tag );
@@ -790,8 +1001,10 @@
790 1001
791 1002 // add the noscript-tag from earlier.
792 1003 $tag = $noscript_tag . $tag;
793 1004 $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
1005 + } else {
1006 + $tag = apply_filters( 'autoptimize_filter_imgopt_not_lazyloaded_img', $tag );
794 1007 }
795 1008
796 1009 return $tag;
797 1010 }
@@ -796,9 +1009,9 @@
796 1009 return $tag;
797 1010 }
798 1011
799 1012 public function add_lazyload_js_footer() {
800 - if ( false === autoptimizeMain::should_buffer() ) {
1013 + if ( false === autoptimizeMain::should_buffer() || autoptimizeMain::is_amp_markup( '' ) ) {
801 1014 return;
802 1015 }
803 1016
804 1017 // The JS will by default be excluded form autoptimization but this can be changed with a filter.
@@ -806,9 +1019,16 @@
806 1019 if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
807 1020 $noptimize_flag = ' data-noptimize="1"';
808 1021 }
809 1022
810 - $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
1023 + $_extra = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings', '' );
1024 + if ( is_array( $_extra ) && array_key_exists( 'autoptimize_extra_checkbox_field_0', $_extra ) && ! empty( $_extra['autoptimize_extra_checkbox_field_0'] ) ) {
1025 + // if "remove query strings" is active in "extra", then let's be consistant and not add one ourselves :-) ?
1026 + $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js', __FILE__ );
1027 + } else {
1028 + $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
1029 + }
1030 +
811 1031 $cdn_url = $this->get_cdn_url();
812 1032 if ( ! empty( $cdn_url ) ) {
813 1033 $cdn_url = rtrim( $cdn_url, '/' );
814 1034 $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
@@ -822,18 +1042,58 @@
822 1042 // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
823 1043 echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<noscript><style>.lazyload{display:none;}</style></noscript>' );
824 1044 echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
825 1045 echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
1046 + }
826 1047
827 - // And add webp detection and loading JS.
828 - if ( $this->should_webp() ) {
829 - $_webp_detect = "function c_webp(A){var n=new Image;n.onload=function(){var e=0<n.width&&0<n.height;A(e)},n.onerror=function(){A(!1)},n.src='data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=='}function s_webp(e){window.supportsWebP=e}c_webp(s_webp);";
830 - $_webp_load = "document.addEventListener('lazybeforeunveil',function({target:b}){window.supportsWebP&&['data-src','data-srcset'].forEach(function(c){attr=b.getAttribute(c),null!==attr&&-1==attr.indexOf('/client/to_webp')&&b.setAttribute(c,attr.replace(/\/client\//,'/client/to_webp,'))})});";
831 - echo apply_filters( 'autoptimize_filter_imgopt_webp_js', '<script' . $type_js . $noptimize_flag . '>' . $_webp_detect . $_webp_load . '</script>' );
1048 + public function create_img_preload_tag( $tag ) {
1049 + if ( false === apply_filters( 'autoptimize_filter_imgopt_dopreloads', true ) ) {
1050 + return '';
832 1051 }
1052 +
1053 + // clean up; remove tabs/ linebreaks/ spaces.
1054 + $tag = preg_replace( '/\s+/', ' ', $tag );
1055 +
1056 + // remove noscript.
1057 + if ( false !== strpos( $tag, '<noscript' ) ) {
1058 + $tag = preg_replace( '/<noscript.*<\/noscript>/mU', '', $tag );
1059 + }
1060 +
1061 + // rewrite img tag to link preload img.
1062 + $_from = array( '<img ', ' src=', ' sizes=', ' srcset=' );
1063 + $_to = array( '<link fetchpriority="high" rel="preload" as="image" ', ' href=', ' imagesizes=', ' imagesrcset=' );
1064 + $tag = str_replace( $_from, $_to, $tag );
1065 +
1066 + // sanitize output
1067 + $tag = $this->kses_preload_link( $tag );
1068 +
1069 + // and provide filter for late changes.
1070 + $tag = apply_filters( 'autoptimize_filter_imgopt_preload_tag_result', $tag );
1071 +
1072 + return $tag;
833 1073 }
834 1074
835 - public function get_cdn_url() {
1075 + public function kses_preload_link( $_preload ) {
1076 + // using kses, remove all unneeded attributes
1077 + // keeping only those we *know* are OK and/ or needed.
1078 + $allowed_html = array(
1079 + 'link' => array(
1080 + 'rel' => true,
1081 + 'as' => true,
1082 + 'href' => true,
1083 + 'imagesizes' => true,
1084 + 'imagesrcset' => true,
1085 + 'type' => true,
1086 + 'media' => true,
1087 + 'fetchpriority' => true,
1088 + ),
1089 + );
1090 + $_preload = wp_kses( $_preload, $allowed_html );
1091 +
1092 + return $_preload;
1093 + }
1094 +
1095 + public static function get_cdn_url() {
836 1096 // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
837 1097 static $cdn_url = null;
838 1098
839 1099 if ( null === $cdn_url ) {
@@ -852,9 +1112,9 @@
852 1112 if ( null === $exclude_lazyload_array ) {
853 1113 $options = $this->options;
854 1114
855 1115 // set default exclusions.
856 - $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset', 'data:image/', 'data-lazyload', 'rev-slidebg', 'loading="eager"' );
1116 + $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset', 'data:image/', 'data-lazyload', 'rev-slidebg', 'loading="eager"', 'fetchpriority="high"' );
857 1117
858 1118 // add from setting.
859 1119 if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
860 1120 $exclude_lazyload_option = $options['autoptimize_imgopt_text_field_5'];
@@ -873,9 +1133,9 @@
873 1133 public function inject_classes_in_tag( $tag, $target_class ) {
874 1134 if ( strpos( $tag, 'class=' ) !== false ) {
875 1135 $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
876 1136 } else {
877 - $tag = preg_replace( '/(<img)\s/', '$1 class="' . trim( $target_class ) . '" ', $tag );
1137 + $tag = preg_replace( '/(<[a-zA-Z]*)\s/', '$1 class="' . trim( $target_class ) . '" ', $tag );
878 1138 }
879 1139
880 1140 return $tag;
881 1141 }
@@ -883,21 +1143,21 @@
883 1143 public function get_default_lazyload_placeholder( $imgopt_w, $imgopt_h ) {
884 1144 return 'data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20' . $imgopt_w . '%20' . $imgopt_h . '%22%3E%3C/svg%3E';
885 1145 }
886 1146
887 - public function should_webp() {
888 - static $webp_return = null;
1147 + public function should_ngimg() {
1148 + static $ngimg_return = null;
889 1149
890 - if ( is_null( $webp_return ) ) {
891 - // webp only works if imgopt and lazyload are also active.
892 - if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && $this->imgopt_active() ) {
893 - $webp_return = true;
1150 + if ( is_null( $ngimg_return ) ) {
1151 + // nextgen img only works if imgopt is active.
1152 + if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && $this->imgopt_active() ) {
1153 + $ngimg_return = true;
894 1154 } else {
895 - $webp_return = false;
1155 + $ngimg_return = false;
896 1156 }
897 1157 }
898 1158
899 - return $webp_return;
1159 + return $ngimg_return;
900 1160 }
901 1161
902 1162 public function process_picture_tag( $in, $imgopt = false, $lazy = false ) {
903 1163 // check if "<picture" is present and if filter allows us to process <picture>.
@@ -916,9 +1176,9 @@
916 1176 foreach ( $_sources as $_source ) {
917 1177 $_picture_replacement = $_source[0];
918 1178
919 1179 // should we optimize the image?
920 - if ( $imgopt && $this->can_optimize_image( $_source[1] ) ) {
1180 + if ( $imgopt && $this->can_optimize_image( $_source[1], $_picture[0] ) ) {
921 1181 $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
922 1182 }
923 1183 // should we lazy-load?
924 1184 if ( $lazy && $this->should_lazyload() && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
@@ -937,9 +1197,9 @@
937 1197
938 1198 public function process_bgimage( $in ) {
939 1199 if ( strpos( $in, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_lazyload_backgroundimages', true ) ) {
940 1200 $out = preg_replace_callback(
941 - '/(<(?:article|aside|body|div|footer|header|p|section|table)[^>]*)\sstyle=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)[^>]*/',
1201 + '/(<(?:article|aside|body|div|footer|header|p|section|span|table)[^>]*)\sstyle=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)[^>]*/',
942 1202 array( $this, 'lazyload_bgimg_callback' ),
943 1203 $in
944 1204 );
945 1205 return $out;
@@ -951,13 +1211,17 @@
951 1211 if ( str_ireplace( $this->get_lazyload_exclusions(), '', $matches[0] ) === $matches[0] ) {
952 1212 // get placeholder & lazyload class strings.
953 1213 $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( 500, 300 ) );
954 1214 $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
1215 + // remove quotes from url() to be able to replace in next step.
1216 + $out = str_replace( array( "url('" . $matches[2] . "')", 'url("' . $matches[2] . '")' ), 'url(' . $matches[2] . ')', $matches[0] );
955 1217 // replace background-image URL with SVG placeholder.
956 - $out = str_replace( $matches[2], $placeholder, $matches[0] );
1218 + $out = str_replace( 'url(' . $matches[2], 'url(' . $placeholder, $out );
1219 + // sanitize bgimg src for quote sillyness.
1220 + $bgimg_src = $this->fix_silly_bgimg_quotes( $matches[2] );
957 1221 // add data-bg attribute with real background-image URL for lazyload to pick up.
958 - $out = str_replace( $matches[1], $matches[1] . ' data-bg="' . trim( str_replace( array( "\r\n", '&quot;' ), '', $matches[2] ) ) . '"', $out );
959 - // add lazyload class to tag.
1222 + $out = str_replace( $matches[1], $matches[1] . ' data-bg="' . $bgimg_src . '"', $out );
1223 + // and finally add lazyload class to tag.
960 1224 $out = $this->inject_classes_in_tag( $out, "$lazyload_class " );
961 1225 return $out;
962 1226 }
963 1227 return $matches[0];
@@ -962,13 +1226,17 @@
962 1226 }
963 1227 return $matches[0];
964 1228 }
965 1229
1230 + public function fix_silly_bgimg_quotes( $tag_in ) {
1231 + // some themes/ pagebuilders wrap backgroundimages in HTML-encoded quotes (or linebreaks) which breaks imgopt/ lazyloading, this removes them.
1232 + return trim( str_replace( array( "\r\n", '"', '&quot;', '&#034;', '&apos;', '&#039;' ), '', $tag_in ) );
1233 + }
1234 +
966 1235 public function maybe_fix_missing_quotes( $tag_in ) {
967 1236 // W3TC's Minify_HTML class removes quotes around attribute value, this re-adds them for the class and width/height attributes so we can lazyload properly.
968 - if ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) {
969 - $tag_out = preg_replace( '/class\s?=([^("|\')]*)(\s|>)/U', 'class=\'$1\'$2', $tag_in );
970 - $tag_out = preg_replace( '/\s(width|height)=(?:"|\')?([^\s"\'>]*)(?:"|\')?/', ' $1=\'$2\'', $tag_out );
1237 + if ( ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) ) {
1238 + $tag_out = autoptimizeAttributeParser::rebuild( $tag_in );
971 1239 return $tag_out;
972 1240 } else {
973 1241 return $tag_in;
974 1242 }
@@ -981,9 +1249,9 @@
981 1249 {
982 1250 // no acces if multisite and not network admin and no site config allowed.
983 1251 if ( autoptimizeConfig::should_show_menu_tabs() ) {
984 1252 add_submenu_page(
985 - null,
1253 + '',
986 1254 'autoptimize_imgopt',
987 1255 'autoptimize_imgopt',
988 1256 'manage_options',
989 1257 'autoptimize_imgopt',
@@ -995,9 +1263,9 @@
995 1263
996 1264 public function add_imgopt_tab( $in )
997 1265 {
998 1266 if ( autoptimizeConfig::should_show_menu_tabs() ) {
999 - $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
1267 + $in = array_merge( $in, array( 'autoptimize_imgopt' => apply_filters( 'autoptimize_filter_imgopt_tab_text', esc_html__( 'Images', 'autoptimize' ) ) ) );
1000 1268 }
1001 1269
1002 1270 return $in;
1003 1271 }
@@ -1003,11 +1271,14 @@
1003 1271 }
1004 1272
1005 1273 public function imgopt_options_page()
1006 1274 {
1275 + // phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace
1276 + // phpcs:disable Generic.Formatting.DisallowMultipleStatements.SameLine
1277 +
1007 1278 // Check querystring for "refreshCacheChecker" and call cachechecker if so.
1008 1279 if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
1009 - $this->query_img_provider_stats();
1280 + $this->query_img_provider_stats( true );
1010 1281 }
1011 1282
1012 1283 $options = $this->fetch_options();
1013 1284 $sp_url_suffix = $this->get_service_url_suffix();
@@ -1015,18 +1286,26 @@
1015 1286 <style>
1016 1287 #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
1017 1288 #ao_settings_form .form-table th {font-weight: normal;}
1018 1289 #autoptimize_imgopt_descr{font-size: 120%;}
1290 + #ao_settings_form td p.condition-warning {color: orange;}
1019 1291 </style>
1020 - <script>document.title = "Autoptimize: <?php _e( 'Images', 'autoptimize' ); ?> " + document.title;</script>
1292 + <script>document.title = "Autoptimize: <?php esc_html_e( 'Images', 'autoptimize' ); ?> " + document.title;</script>
1021 1293 <div class="wrap">
1022 - <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
1294 + <h1><?php apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html_e( 'Autoptimize Pro Settings', 'autoptimize' ) : esc_html_e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
1023 1295 <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
1296 + <?php if ( autoptimizeUtils::is_local_server() ) { ?>
1297 + <div class="notice-warning notice"><p>
1298 + <?php
1299 + echo esc_html__( 'The image optimization service does not work on locally hosted sites or when the server is on a private network.', 'autoptimize' );
1300 + ?>
1301 + </p></div>
1302 + <?php } ?>
1024 1303 <?php if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) { ?>
1025 1304 <div class="notice-warning notice"><p>
1026 1305 <?php
1027 1306 // translators: "Autoptimize support forum" will appear in a "a href".
1028 - echo sprintf( __( 'The image optimization service is currently down, image optimization will be skipped until further notice. Check the %1$sAutoptimize support forum%2$s for more info.', 'autoptimize' ), '<a href="https://wordpress.org/support/plugin/autoptimize/" target="_blank">', '</a>' );
1307 + echo sprintf( esc_html__( 'The image optimization service is currently down, image optimization will be skipped until further notice. Check the %1$sAutoptimize support forum%2$s for more info.', 'autoptimize' ), '<a href="https://wordpress.org/support/plugin/autoptimize/" target="_blank">', '</a>' );
1029 1308 ?>
1030 1309 </p></div>
1031 1310 <?php } ?>
1032 1311
@@ -1031,9 +1310,9 @@
1031 1310 <?php } ?>
1032 1311
1033 1312 <?php if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] && ! autoptimizeImages::instance()->launch_ok() ) { ?>
1034 1313 <div class="notice-warning notice"><p>
1035 - <?php _e( 'The image optimization service is launching, but not yet available for this domain, it should become available in the next couple of days.', 'autoptimize' ); ?>
1314 + <?php esc_html_e( 'The image optimization service is launching, but not yet available for this domain, it should become available in the next couple of days.', 'autoptimize' ); ?>
1036 1315 </p></div>
1037 1316 <?php } ?>
1038 1317
1039 1318 <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
@@ -1039,21 +1318,21 @@
1039 1318 <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
1040 1319 <div class="notice-warning notice"><p>
1041 1320 <?php
1042 1321 // translators: "disable Jetpack's site accelerator for images" will appear in a "a href" linking to the jetpack settings page.
1043 - echo sprintf( __( 'Please %1$sdisable Jetpack\'s site accelerator for images%2$s to be able to use Autoptomize\'s advanced image optimization features below.', 'autoptimize' ), '<a href="admin.php?page=jetpack#/settings">', '</a>' );
1322 + echo sprintf( esc_html__( 'Please %1$sdisable Jetpack\'s site accelerator for images%2$s to be able to use Autoptomize\'s advanced image optimization features below.', 'autoptimize' ), '<a href="admin.php?page=jetpack#/settings">', '</a>' );
1044 1323 ?>
1045 1324 </p></div>
1046 1325 <?php } ?>
1047 1326 <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
1048 1327 <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
1049 - <h2><?php _e( 'Image optimization', 'autoptimize' ); ?></h2>
1050 - <span id='autoptimize_imgopt_descr'><?php _e( 'Make your site significantly faster by just ticking a couple of checkboxes to optimize and lazy load your images, WebP support included!', 'autoptimize' ); ?></span>
1328 + <h2><?php esc_html_e( 'Image optimization', 'autoptimize' ); ?></h2>
1329 + <span id='autoptimize_imgopt_descr'><?php echo apply_filters( 'autoptimize_filter_imgopt_intro_copy', esc_html__( 'Make your site significantly faster by simply ticking a few boxes and start serving CDN powered, optimized images in the next-gen WebP image format! No additional plugins or services needed.', 'autoptimize' ) ); ?></span>
1051 1330 <table class="form-table">
1052 1331 <tr>
1053 - <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
1332 + <th scope="row"><?php esc_html_e( 'Image optimization & CDN', 'autoptimize' ); ?></th>
1054 1333 <td>
1055 - <label><input id='autoptimize_imgopt_checkbox' type='checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Optimize images on the fly and serve them from Shortpixel\'s global CDN.', 'autoptimize' ); ?></label>
1334 + <label><input id='autoptimize_imgopt_checkbox' type='checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php echo apply_filters( 'autoptimize_filter_imgopt_main_setting_copy', esc_html__( 'On-the-fly image optimization and fast delivery via the Shortpixel global CDN.', 'autoptimize' ) ); ?></label>
1056 1335 <?php
1057 1336 // show shortpixel status.
1058 1337 $_notice = autoptimizeImages::instance()->get_imgopt_status_notice();
1059 1338 if ( $_notice ) {
@@ -1071,31 +1350,37 @@
1071 1350 break;
1072 1351 default:
1073 1352 $_notice_color = 'green';
1074 1353 }
1075 - echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
1354 + echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . esc_html__( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
1076 1355 } else {
1077 1356 // translators: link points to shortpixel.
1078 - $upsell_msg_1 = '<p>' . sprintf( __( 'Get more Google love and improve your website\'s loading speed by having your publicly available images optimized on the fly (also in the "next-gen" WebP image format) by %1$sShortPixel%2$s and then cached and served fast from Shortpixel\'s global CDN.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
1357 + $upsell_msg_1 = '<p>' . sprintf( esc_html__( 'Get more Google love by speeding up your website. Start serving on-the-fly optimized images (also in the "next-gen" %4$sWebP%5$s image format) by %1$sShortPixel%2$s. No additional image optimization plugins are needed: your images are optimized, cached and served from %3$sShortPixel\'s global CDN%2$s.', 'autoptimize' ), '<a href="https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell" target="_blank">', '</a>', '<a href="https://help.shortpixel.com/article/62-where-does-the-cdn-has-pops" target="_blank">', '<strong>', '</strong>' );
1079 1358 if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
1080 - $upsell_msg_2 = __( 'For a limited time only, this service is offered free for all Autoptimize users, <b>don\'t miss the chance to test it</b> and see how much it could improve your site\'s speed.', 'autoptimize' );
1359 + $upsell_msg_2 = sprintf( esc_html__( 'For a limited time only, this service is offered free for all Autoptimize users, %1$sdon\'t miss the chance to test it%2$s and see how much it could improve your site\'s speed.', 'autoptimize' ), '<strong>', '</strong>' );
1081 1360 } else {
1082 - // translators: link points to shortpixel.
1083 - $upsell_msg_2 = sprintf( __( '%1$sSign-up now%2$s to receive a 1 000 bonus + 50&#37; more image optimization credits regardless of the traffic used. More image optimizations can be purchased starting with $4.99.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
1361 + // translators: 1st link points to autoptimize.com.pro, 2nd to shortpixel.
1362 + $upsell_msg_2 = sprintf( esc_html__( 'For (nearly) %5$sunlimited image optimizations %1$sbuy Autoptimize Pro%2$s%6$s which also includes Critical CSS and extra "booster" options or %3$ssign up at Shortpixel%4$s.', 'autoptimize' ), '<a href="https://autoptimize.com/pro/" target="_blank">', '</a>', '<a href="https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell" target="_blank">', '</a>', '<strong>', '</strong>' );
1084 1363 }
1085 - echo apply_filters( 'autoptimize_imgopt_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
1364 + echo apply_filters( 'autoptimize_filter_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
1086 1365 }
1087 1366 // translators: link points to shortpixel FAQ.
1088 - $faqcopy = sprintf( __( '<strong>Questions</strong>? Have a look at the %1$sShortPixel FAQ%2$s!', 'autoptimize' ), '<strong><a href="https://shortpixel.helpscoutdocs.com/category/60-shortpixel-ai-cdn" target="_blank">', '</strong></a>' );
1089 - $faqcopy = $faqcopy . ' ' . __( 'Only works for sites/ images that are publicly available.', 'autoptimize' );
1367 + $faqcopy = sprintf( esc_html__( '%3$sQuestions%4$s? Take a look at the %1$sAutoptimize + ShortPixel FAQ%2$s!', 'autoptimize' ), '<strong><a href="https://help.shortpixel.com/category/405-autoptimize" target="_blank">', '</strong></a>', '<strong>', '</strong>' );
1368 + $faqcopy = $faqcopy . ' ' . esc_html__( 'Only works for websites and images that are publicly available.', 'autoptimize' );
1090 1369 // translators: links points to shortpixel TOS & Privacy Policy.
1091 - $toscopy = sprintf( __( 'Usage of this feature is subject to Shortpixel\'s %1$sTerms of Use%2$s and %3$sPrivacy policy%4$s.', 'autoptimize' ), '<a href="https://shortpixel.com/tos' . $sp_url_suffix . '" target="_blank">', '</a>', '<a href="https://shortpixel.com/pp' . $sp_url_suffix . '" target="_blank">', '</a>' );
1092 - echo apply_filters( 'autoptimize_imgopt_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
1370 + $toscopy = sprintf( esc_html__( 'Usage of this feature is subject to Shortpixel\'s %1$sTerms of Use%2$s and %3$sPrivacy policy%4$s.', 'autoptimize' ), '<a href="https://shortpixel.com/tos' . $sp_url_suffix . '" target="_blank">', '</a>', '<a href="https://shortpixel.com/privacy' . $sp_url_suffix . '" target="_blank">', '</a>' );
1371 + echo apply_filters( 'autoptimize_filter_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
1093 1372 ?>
1094 1373 </td>
1095 1374 </tr>
1375 + <tr id='autoptimize_imgopt_optimization_exclusions' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
1376 + <th scope="row"><?php esc_html_e( 'Optimization exclusions', 'autoptimize' ); ?></th>
1377 + <td>
1378 + <label><input type='text' style='width:80%' id='autoptimize_imgopt_optimization_exclusions' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_6]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_6'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_6'] ); } ?>'><br /><?php esc_html_e( 'Comma-separated list of image classes or filenames that should not be optimized.', 'autoptimize' ); ?></label>
1379 + </td>
1380 + </tr>
1096 1381 <tr id='autoptimize_imgopt_quality' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
1097 - <th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
1382 + <th scope="row"><?php esc_html_e( 'Image Optimization quality', 'autoptimize' ); ?></th>
1098 1383 <td>
1099 1384 <label>
1100 1385 <select name='autoptimize_imgopt_settings[autoptimize_imgopt_select_field_2]'>
1101 1386 <?php
@@ -1115,33 +1400,60 @@
1115 1400 </label>
1116 1401 <p>
1117 1402 <?php
1118 1403 // translators: link points to shortpixel image test page.
1119 - echo apply_filters( 'autoptimize_imgopt_imgopt_quality_copy', sprintf( __( 'You can %1$stest compression levels here%2$s.', 'autoptimize' ), '<a href="https://shortpixel.com/oic' . $sp_url_suffix . '" target="_blank">', '</a>' ) );
1404 + echo apply_filters( 'autoptimize_filter_imgopt_quality_copy', sprintf( esc_html__( 'You can %1$stest compression levels here%2$s.', 'autoptimize' ), '<a href="https://shortpixel.com/online-image-compression' . $sp_url_suffix . '" target="_blank">', '</a>' ) );
1120 1405 ?>
1121 1406 </p>
1122 1407 </td>
1123 1408 </tr>
1124 - <tr id='autoptimize_imgopt_webp' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
1125 - <th scope="row"><?php _e( 'Load WebP in supported browsers?', 'autoptimize' ); ?></th>
1409 + <?php
1410 + if ( apply_filters( 'autoptimize_filter_imgopt_settings_show_avif', false ) || $this->should_ngimg() ) {
1411 + ?>
1412 + <tr id='autoptimize_imgopt_ngimg' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
1413 + <th scope="row">
1414 + <?php esc_html_e( 'Load AVIF in supported browsers?', 'autoptimize' ); ?>
1415 + </th>
1416 + <td>
1417 + <p class="condition-warning">
1418 + <?php
1419 + if ( '3' === autoptimizeImages::instance()->get_img_quality_setting() && $this->should_ngimg() ) {
1420 + _e( 'Your Image optimization quality is set to "lossless", but AVIF files can end up being bigger than jpeg or webp for lossless images, so consider choosing a different quality or disable the AVIF option.', 'autoptimize' );
1421 + }
1422 + ?>
1423 + </p>
1424 + <label><input type='checkbox' id='autoptimize_imgopt_ngimg_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_4]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_4'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_4'] ) { echo 'checked="checked"'; } ?> value='1'>
1425 + <?php esc_html_e( 'Automatically serve AVIF image format to any browser that supports it.', 'autoptimize' ); ?> <?php printf( esc_html__( '%1$sImportant caveat%2$s; although it %5$scan result in even smaller image sizes for lossy images%4$s it should be taken into account that %3$s%1$sAVIF can require more resources%2$s to decode/ display%4$s and as such %1$smight be slower%2$s on low-end (mobile) devices.', 'autoptimize' ), '<strong>', '</strong>', '<a href="https://storage.googleapis.com/avif-comparison/decode-timing.html" target="_blank">', '</a>', '<a href="https://tsev.dev/posts/2023-11-10-should-avif-be-the-dominant-image-format/" target="_blank">' ); ?>
1426 + </label>
1427 + </td>
1428 + </tr>
1429 + <?php
1430 + } else {
1431 + ?>
1432 + <input type='hidden' id='autoptimize_imgopt_ngimg_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_4]' value='0'>
1433 + <?php
1434 + }
1435 + ?>
1436 + <tr>
1437 + <th scope="row"><?php esc_html_e( 'Lazy-load images?', 'autoptimize' ); ?></th>
1126 1438 <td>
1127 - <label><input type='checkbox' id='autoptimize_imgopt_webp_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_4]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_4'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Automatically serve "next-gen" WebP image format to any browser that supports it (requires lazy load to be active).', 'autoptimize' ); ?></label>
1439 + <label><input type='checkbox' id='autoptimize_imgopt_lazyload_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_3]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php esc_html_e( 'Image lazy-loading will delay the loading of non-visible images to allow the browser to optimally load all resources for the "above the fold"-page first.', 'autoptimize' ); ?></label>
1128 1440 </td>
1129 1441 </tr>
1130 - <tr>
1131 - <th scope="row"><?php _e( 'Lazy-load images?', 'autoptimize' ); ?></th>
1442 + <tr id='autoptimize_imgopt_lazyload_exclusions' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_3', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_3'] ) ) { echo 'class="autoptimize_lazyload_child hidden"'; } else { echo 'class="autoptimize_lazyload_child"'; } ?>>
1443 + <th scope="row"><?php esc_html_e( 'Lazy-load exclusions', 'autoptimize' ); ?></th>
1132 1444 <td>
1133 - <label><input type='checkbox' id='autoptimize_imgopt_lazyload_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_3]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Image lazy-loading will delay the loading of non-visible images to allow the browser to optimally load all resources for the "above the fold"-page first.', 'autoptimize' ); ?></label>
1445 + <label><input type='text' style='width:80%' id='autoptimize_imgopt_lazyload_exclusions_text' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_5]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_5'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_5'] ); } ?>'><br /><?php esc_html_e( 'Comma-separated list of to be excluded image classes or filenames.', 'autoptimize' ); ?></label>
1134 1446 </td>
1135 1447 </tr>
1136 - <tr id='autoptimize_imgopt_lazyload_exclusions' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_3', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_3'] ) ) { echo 'class="hidden"'; } ?>>
1137 - <th scope="row"><?php _e( 'Lazy-load exclusions', 'autoptimize' ); ?></th>
1448 + <tr id='autoptimize_imgopt_lazyload_from_nth_image' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_3', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_3'] ) ) { echo 'class="autoptimize_lazyload_child hidden"'; } else { echo 'class="autoptimize_lazyload_child"'; } ?>>
1449 + <th scope="row"><?php esc_html_e( 'Lazy-load from nth image', 'autoptimize' ); ?></th>
1138 1450 <td>
1139 - <label><input type='text' style='width:80%' id='autoptimize_imgopt_lazyload_exclusions' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_5]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_5'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_5'] ); } ?>'><br /><?php _e( 'Comma-separated list of to be excluded image classes or filenames.', 'autoptimize' ); ?></label>
1451 + <label><input type='number' min='0' max='50' style='width:80%' id='autoptimize_imgopt_lazyload_from_nth_image_number' name='autoptimize_imgopt_settings[autoptimize_imgopt_number_field_7]' value='<?php if ( ! empty( $options['autoptimize_imgopt_number_field_7'] ) ) { echo esc_attr( $options['autoptimize_imgopt_number_field_7'] ); } else { echo '1'; } ?>'><br /><?php esc_html_e( 'Don\'t lazyload the first X images, \'1\' lazyloads all.', 'autoptimize' ); ?></label>
1140 1452 </td>
1141 1453 </tr>
1142 1454 </table>
1143 - <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
1455 + <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php esc_html_e( 'Save Changes', 'autoptimize' ); ?>" /></p>
1144 1456 </form>
1145 1457 <script>
1146 1458 jQuery(document).ready(function() {
1147 1459 jQuery("#autoptimize_imgopt_checkbox").change(function() {
@@ -1146,26 +1458,21 @@
1146 1458 jQuery(document).ready(function() {
1147 1459 jQuery("#autoptimize_imgopt_checkbox").change(function() {
1148 1460 if (this.checked) {
1149 1461 jQuery("#autoptimize_imgopt_quality").show("slow");
1150 - jQuery("#autoptimize_imgopt_webp").show("slow");
1462 + jQuery("#autoptimize_imgopt_ngimg").show("slow");
1463 + jQuery("#autoptimize_imgopt_optimization_exclusions").show("slow");
1151 1464 } else {
1152 1465 jQuery("#autoptimize_imgopt_quality").hide("slow");
1153 - jQuery("#autoptimize_imgopt_webp").hide("slow");
1466 + jQuery("#autoptimize_imgopt_ngimg").hide("slow");
1467 + jQuery("#autoptimize_imgopt_optimization_exclusions").hide("slow");
1154 1468 }
1155 1469 });
1156 - jQuery("#autoptimize_imgopt_webp_checkbox").change(function() {
1157 - if (this.checked) {
1158 - jQuery("#autoptimize_imgopt_lazyload_checkbox")[0].checked = true;
1159 - jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1160 - }
1161 - });
1162 1470 jQuery("#autoptimize_imgopt_lazyload_checkbox").change(function() {
1163 1471 if (this.checked) {
1164 - jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1472 + jQuery(".autoptimize_lazyload_child").show("slow");
1165 1473 } else {
1166 - jQuery("#autoptimize_imgopt_lazyload_exclusions").hide("slow");
1167 - jQuery("#autoptimize_imgopt_webp_checkbox")[0].checked = false;
1474 + jQuery(".autoptimize_lazyload_child").hide("slow");
1168 1475 }
1169 1476 });
1170 1477 });
1171 1478 </script>
@@ -1175,13 +1482,13 @@
1175 1482 /**
1176 1483 * Ïmg opt status as used on dashboard.
1177 1484 */
1178 1485 public function get_imgopt_status_notice() {
1179 - if ( $this->imgopt_active() ) {
1486 + if ( $this->imgopt_active() && apply_filters( 'autoptimize_filter_imgopt_status_shortpixel', true ) ) {
1180 1487 $_imgopt_notice = '';
1181 1488 $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1182 1489 $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
1183 - $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
1490 + $_imgopt_upsell = 'https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell';
1184 1491 $_imgopt_assoc = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1185 1492 $_imgopt_unreach = 'https://shortpixel.helpscoutdocs.com/article/148-why-are-my-images-redirected-from-cdn-shortpixel-ai';
1186 1493
1187 1494 if ( is_array( $_stat ) ) {
@@ -1186,37 +1493,40 @@
1186 1493
1187 1494 if ( is_array( $_stat ) ) {
1188 1495 if ( 1 == $_stat['Status'] ) {
1189 1496 // translators: "add more credits" will appear in a "a href".
1190 - $_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota is almost used, make sure you %1$sadd more credits%2$s to avoid slowing down your website.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1497 + $_imgopt_notice = sprintf( esc_html__( 'Your ShortPixel image optimization and CDN quota is almost used, make sure you %1$sadd more credits%2$s to avoid slowing down your website %4$sor consider using %3$sAutoptimize Pro%2$s which comes with (nearly) unlimited image optimization%5$s but also automated critical CSS and extra booster options.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>', '<a href="https://autoptimize.com/pro/" target="_blank">', '<strong>', '</strong>' );
1191 1498 } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1192 1499 // translators: "add more credits" will appear in a "a href".
1193 - $_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota was used, %1$sadd more credits%2$s to keep fast serving optimized images on your site', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1500 + $_imgopt_notice = sprintf( esc_html__( 'Your ShortPixel image optimization and CDN quota has been exhausted, %1$sadd more credits%2$s to continue to quickly deliver optimized images on your website %4$sor consider using %3$sAutoptimize Pro%2$s which comes with (nearly) unlimited image optimization%5$s but also automated critical CSS and extra booster options.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>', '<a href="https://autoptimize.com/pro/" target="_blank">', '<strong>', '</strong>' );
1194 1501 // translators: "associate your domain" will appear in a "a href".
1195 - $_imgopt_notice = $_imgopt_notice . ' ' . sprintf( __( 'If you already have enough credits then you may need to %1$sassociate your domain%2$s to your Shortpixel account.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $_imgopt_assoc . '" target="_blank">', '</a>' );
1502 + $_imgopt_notice = $_imgopt_notice . ' ' . sprintf( esc_html__( 'If you have enough CDN quota remaining, then you may need to %1$sassociate your domain%2$s to your Shortpixel account.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $_imgopt_assoc . '" target="_blank">', '</a>' );
1196 1503 } elseif ( -3 == $_stat['Status'] ) {
1197 1504 // translators: "check the documentation here" will appear in a "a href".
1198 - $_imgopt_notice = sprintf( __( 'It seems ShortPixel image optimization is not able to fetch images from your site, %1$scheck the documentation here%2$s for more information', 'autoptimize' ), '<a href="' . $_imgopt_unreach . '" target="_blank">', '</a>' );
1505 + $_imgopt_notice = sprintf( esc_html__( 'It seems ShortPixel image optimization is not able to fetch images from your site, %1$scheck the documentation here%2$s for more information', 'autoptimize' ), '<a href="' . $_imgopt_unreach . '" target="_blank">', '</a>' );
1199 1506 } else {
1200 - $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1507 + $_imgopt_upsell = 'https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell';
1201 1508 // translators: "log in to check your account" will appear in a "a href".
1202 - $_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1509 + $_imgopt_notice = sprintf( esc_html__( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1203 1510 }
1204 1511
1205 1512 // add info on freshness + refresh link if status is not 2 (good shape).
1206 1513 if ( 2 != $_stat['Status'] ) {
1207 - $_imgopt_stats_refresh_url = add_query_arg( array(
1208 - 'page' => 'autoptimize_imgopt',
1209 - 'refreshImgProvStats' => '1',
1210 - ), admin_url( 'options-general.php' ) );
1514 + $_imgopt_stats_refresh_url = add_query_arg(
1515 + array(
1516 + 'page' => 'autoptimize_imgopt',
1517 + 'refreshImgProvStats' => '1',
1518 + ),
1519 + admin_url( 'options-general.php' )
1520 + );
1211 1521 if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
1212 - $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( autoptimizeOptionWrapper::get_option( 'time_format' ), $_stat['timestamp'] );
1522 + $_imgopt_stats_last_run = esc_html__( 'based on status at ', 'autoptimize' ) . date_i18n( autoptimizeOptionWrapper::get_option( 'time_format' ), $_stat['timestamp'] );
1213 1523 } else {
1214 - $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1524 + $_imgopt_stats_last_run = esc_html__( 'based on previously fetched data', 'autoptimize' );
1215 1525 }
1216 1526 $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1217 1527 // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
1218 - $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1528 + $_imgopt_notice .= sprintf( esc_html__( 'you can click %1$shere to refresh your quota status%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1219 1529 }
1220 1530
1221 1531 // and make the full notice filterable.
1222 1532 $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
@@ -1237,18 +1547,24 @@
1237 1547 }
1238 1548
1239 1549 /**
1240 1550 * Get img provider stats (used to display notice).
1551 + *
1552 + * @param bool $_refresh Should the stats be forcefully refreshed or not.
1241 1553 */
1242 - public function query_img_provider_stats() {
1243 - if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) ) {
1554 + public function query_img_provider_stats( $_refresh = false ) {
1555 + if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) && apply_filters( 'autoptimize_filter_imgopt_status_shortpixel', true ) ) {
1244 1556 $url = '';
1245 - $endpoint = $this->get_imgopt_host() . 'read-domain/';
1557 + $stat_dom = 'https://no-cdn.shortpixel.ai/';
1558 + $endpoint = $stat_dom . 'read-domain/';
1246 1559 $domain = AUTOPTIMIZE_SITE_DOMAIN;
1247 1560
1248 1561 // make sure parse_url result makes sense, keeping $url empty if not.
1249 1562 if ( $domain && ! empty( $domain ) ) {
1250 1563 $url = $endpoint . $domain;
1564 + if ( true === $_refresh ) {
1565 + $url = $url . '/refresh';
1566 + }
1251 1567 }
1252 1568
1253 1569 $url = apply_filters(
1254 1570 'autoptimize_filter_imgopt_stat_url',
@@ -1285,9 +1601,13 @@
1285 1601 {
1286 1602 static $launch_status = null;
1287 1603
1288 1604 if ( null === $launch_status ) {
1289 - $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
1605 + $avail_imgopt = '';
1606 + if ( is_array( $this->options ) && array_key_exists( 'availabilities', $this->options ) && is_array( $this->options['availabilities'] ) && array_key_exists( 'extra_imgopt', $this->options['availabilities'] ) ) {
1607 + $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
1608 + }
1609 +
1290 1610 $magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
1291 1611 $has_launched = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_launched', '' );
1292 1612 $launch_status = false;
1293 1613 if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {