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 +737 -254 2.5.0 → 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.
@@ -41,9 +48,9 @@
41 48 }
42 49
43 50 public static function fetch_options()
44 51 {
45 - $value = get_option( 'autoptimize_imgopt_settings' );
52 + $value = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings' );
46 53 if ( empty( $value ) ) {
47 54 // Fallback to returning defaults when no stored option exists yet.
48 55 $value = autoptimizeConfig::get_ao_imgopt_default_options();
49 56 }
@@ -48,12 +55,32 @@
48 55 $value = autoptimizeConfig::get_ao_imgopt_default_options();
49 56 }
50 57
51 58 // get service availability and add it to the options-array.
52 - $value['availabilities'] = get_option( 'autoptimize_service_availablity' );
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 }
@@ -65,9 +92,9 @@
65 92 // and does not use/ request the availablity data (which could slow things down).
66 93 static $imgopt_active = null;
67 94
68 95 if ( null === $imgopt_active ) {
69 - $opts = get_option( 'autoptimize_imgopt_settings', '' );
96 + $opts = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings', '' );
70 97 if ( ! empty( $opts ) && is_array( $opts ) && array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $opts ) && ! empty( $opts['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $opts['autoptimize_imgopt_checkbox_field_1'] ) {
71 98 $imgopt_active = true;
72 99 } else {
73 100 $imgopt_active = false;
@@ -96,12 +123,16 @@
96 123
97 124 public function run()
98 125 {
99 126 if ( is_admin() ) {
100 - add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
127 + if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
128 + add_action( 'network_admin_menu', array( $this, 'imgopt_admin_menu' ) );
129 + } else {
130 + add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
131 + }
101 132 add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_imgopt_tab' ), 9 );
102 133 } else {
103 - $this->run_on_frontend();
134 + add_action( 'wp', array( $this, 'run_on_frontend' ) );
104 135 }
105 136 }
106 137
107 138 public function run_on_frontend() {
@@ -107,8 +138,14 @@
107 138 public function run_on_frontend() {
108 139 if ( ! $this->should_run() ) {
109 140 if ( $this->should_lazyload() ) {
110 141 add_filter(
142 + 'wp_lazy_loading_enabled',
143 + array( $this, 'should_disable_core_lazyload' ),
144 + 10,
145 + 3
146 + );
147 + add_filter(
111 148 'autoptimize_html_after_minify',
112 149 array( $this, 'filter_lazyload_images' ),
113 150 10,
114 151 1
@@ -135,8 +172,9 @@
135 172 $active = true;
136 173 }
137 174
138 175 if ( apply_filters( 'autoptimize_filter_imgopt_do_css', true ) ) {
176 + // fixme: also act on already minified CSS!
139 177 add_filter(
140 178 'autoptimize_filter_base_replace_cdn',
141 179 array( $this, 'filter_optimize_css_images' ),
142 180 10,
@@ -141,8 +179,16 @@
141 179 array( $this, 'filter_optimize_css_images' ),
142 180 10,
143 181 1
144 182 );
183 +
184 + add_filter(
185 + 'autoptimize_html_after_minify',
186 + array( $this, 'filter_optimize_inline_css_images' ),
187 + 10,
188 + 1
189 + );
190 +
145 191 $active = true;
146 192 }
147 193
148 194 if ( $active ) {
@@ -154,16 +200,40 @@
154 200 );
155 201 }
156 202
157 203 if ( $this->should_lazyload() ) {
204 + add_filter(
205 + 'wp_lazy_loading_enabled',
206 + array( $this, 'should_disable_core_lazyload' ),
207 + 10,
208 + 3
209 + );
158 210 add_action(
159 211 'wp_footer',
160 - array( $this, 'add_lazyload_js_footer' )
212 + array( $this, 'add_lazyload_js_footer' ),
213 + 10,
214 + 0
161 215 );
162 216 }
163 217 }
164 218
165 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 + /**
166 236 * Basic checks before we can run.
167 237 *
168 238 * @return bool
169 239 */
@@ -174,9 +244,10 @@
174 244 $not_launch_status = ( 'launch' !== $opts['availabilities']['extra_imgopt']['status'] );
175 245
176 246 $do_cdn = true;
177 247 $_userstatus = $this->get_imgopt_provider_userstatus();
178 - if ( -2 == $_userstatus['Status'] ) {
248 + if ( isset( $_userstatus['Status'] ) && ( -2 == $_userstatus['Status'] || -3 == $_userstatus['Status'] ) ) {
249 + // don't even attempt to put images on CDN if heavily exceeded threshold or if site not reachable.
179 250 $do_cdn = false;
180 251 }
181 252
182 253 if (
@@ -194,13 +265,14 @@
194 265 {
195 266 static $imgopt_host = null;
196 267
197 268 if ( null === $imgopt_host ) {
198 - $imgopt_host = 'https://cdn.shortpixel.ai/';
269 + $imgopt_host = 'https://sp-ao.shortpixel.ai/';
199 270 $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
200 271 if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
201 272 $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
202 273 }
274 + $imgopt_host = apply_filters( 'autoptimize_filter_imgopt_host', $imgopt_host );
203 275 }
204 276
205 277 return $imgopt_host;
206 278 }
@@ -213,9 +285,9 @@
213 285 }
214 286
215 287 public static function get_service_url_suffix()
216 288 {
217 - $suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
289 + $suffix = '/af/ZQXTBY0109483/' . AUTOPTIMIZE_SITE_DOMAIN;
218 290
219 291 return $suffix;
220 292 }
221 293
@@ -300,9 +372,9 @@
300 372
301 373 // get CDN domain once.
302 374 static $cdn_domain = null;
303 375 if ( is_null( $cdn_domain ) ) {
304 - $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
376 + $cdn_url = $this->get_cdn_url();
305 377 if ( ! empty( $cdn_url ) ) {
306 378 $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
307 379 } else {
308 380 $cdn_domain = '';
@@ -324,24 +396,29 @@
324 396 }
325 397
326 398 // Do the work on cache miss only.
327 399 if ( ! isset( $cache[ $in ] ) ) {
328 - // Default to what was given to us.
329 - $result = $in;
330 - if ( autoptimizeUtils::is_protocol_relative( $in ) ) {
331 - $result = $parsed_site_url['scheme'] . ':' . $in;
332 - } elseif ( 0 === strpos( $in, '/' ) ) {
400 + // Default to (the trimmed version of) what was given to us.
401 + $result = trim( $in );
402 +
403 + // Some silly plugins wrap background images in html-encoded quotes, so remove those from the img url.
404 + $result = $this->fix_silly_bgimg_quotes( $result );
405 +
406 + if ( autoptimizeUtils::is_protocol_relative( $result ) ) {
407 + $result = $parsed_site_url['scheme'] . ':' . $result;
408 + } elseif ( 0 === strpos( $result, '/' ) ) {
333 409 // Root-relative...
334 - $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'];
335 - // Add the path for subfolder installs.
336 - if ( isset( $parsed_site_url['path'] ) ) {
337 - $result .= $parsed_site_url['path'];
338 - }
339 - $result .= $in;
340 - } elseif ( ! empty( $cdn_domain ) && strpos( $in, $cdn_domain ) !== 0 ) {
341 - $result = str_replace( $cdn_domain, $parsed_site_url['host'], $in );
410 + $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $result;
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.
413 + $result = str_replace( $cdn_domain, $parsed_site_url['host'], $result );
342 414 }
343 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 +
344 421 $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );
345 422
346 423 // Store in cache.
347 424 $cache[ $in ] = $result;
@@ -353,15 +430,41 @@
353 430 public function filter_optimize_css_images( $in )
354 431 {
355 432 $in = $this->normalize_img_url( $in );
356 433
357 - if ( $this->can_optimize_image( $in ) ) {
434 + if ( $this->can_optimize_image( $in ) && false === strpos( $in, $this->get_imgopt_host() ) ) {
358 435 return $this->build_imgopt_url( $in, '', '' );
359 436 } else {
360 437 return $in;
361 438 }
362 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 + }
363 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 +
364 467 private function get_imgopt_base_url()
365 468 {
366 469 static $imgopt_base_url = null;
367 470
@@ -368,9 +471,15 @@
368 471 if ( null === $imgopt_base_url ) {
369 472 $imgopt_host = $this->get_imgopt_host();
370 473 $quality = $this->get_img_quality_string();
371 474 $ret_val = apply_filters( 'autoptimize_filter_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
372 - $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;
373 482 $imgopt_base_url = apply_filters( 'autoptimize_filter_imgopt_base_url', $imgopt_base_url );
374 483 }
375 484
376 485 return $imgopt_base_url;
@@ -375,9 +484,14 @@
375 484
376 485 return $imgopt_base_url;
377 486 }
378 487
379 - 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 )
380 494 {
381 495 static $cdn_url = null;
382 496 static $nopti_images = null;
383 497
@@ -383,14 +497,17 @@
383 497
384 498 if ( null === $cdn_url ) {
385 499 $cdn_url = apply_filters(
386 500 'autoptimize_filter_base_cdnurl',
387 - get_option( 'autoptimize_cdn_url', '' )
501 + autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' )
388 502 );
389 503 }
390 504
391 - if ( null === $nopti_images ) {
392 - $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 );
393 510 }
394 511
395 512 $site_host = AUTOPTIMIZE_SITE_DOMAIN;
396 513 $url = $this->normalize_img_url( $url );
@@ -395,21 +512,25 @@
395 512 $site_host = AUTOPTIMIZE_SITE_DOMAIN;
396 513 $url = $this->normalize_img_url( $url );
397 514 $url_parsed = parse_url( $url );
398 515
399 - if ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
516 + if ( false === is_array( $url_parsed ) ) {
400 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;
401 522 } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
402 523 return false;
403 524 } elseif ( strpos( $url, '.php' ) !== false ) {
404 525 return false;
405 - } 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'] ) {
406 527 // fixme: better check against end of string.
407 528 return false;
408 529 } elseif ( ! empty( $nopti_images ) ) {
409 530 $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
410 531 foreach ( $nopti_images_array as $nopti_image ) {
411 - if ( strpos( $url, $nopti_image ) !== false ) {
532 + if ( strpos( $url, $nopti_image ) !== false || ( ( '' !== $tag && strpos( $tag, $nopti_image ) !== false ) ) ) {
412 533 return false;
413 534 }
414 535 }
415 536 }
@@ -415,8 +536,14 @@
415 536 }
416 537 return true;
417 538 }
418 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 +
419 546 private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
420 547 {
421 548 // sanitize width and height.
422 549 if ( strpos( $width, '%' ) !== false ) {
@@ -439,9 +566,15 @@
439 566 if ( $filtered_url !== $orig_url ) {
440 567 return $filtered_url;
441 568 }
442 569
443 - $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 +
444 577 $imgopt_base_url = $this->get_imgopt_base_url();
445 578 $imgopt_size = '';
446 579
447 580 if ( $width && 0 !== $width ) {
@@ -451,9 +584,10 @@
451 584 if ( $height && 0 !== $height ) {
452 585 $imgopt_size .= ',h_' . $height;
453 586 }
454 587
455 - $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 );
456 590
457 591 return $url;
458 592 }
459 593
@@ -463,38 +597,65 @@
463 597 }
464 598
465 599 public function replace_img_callback( $matches, $width = 0, $height = 0 )
466 600 {
467 - if ( $this->can_optimize_image( $matches[1] ) ) {
468 - return str_replace( $matches[1], $this->build_imgopt_url( $matches[1], $width, $height ), $matches[0] );
601 + $_normalized_img_url = $this->normalize_img_url( $matches[1] );
602 + if ( $this->can_optimize_image( $matches[1], $matches[0] ) ) {
603 + return str_replace( $matches[1], $this->build_imgopt_url( $_normalized_img_url, $width, $height ), $matches[0] );
469 604 } else {
470 605 return $matches[0];
471 606 }
472 607 }
473 608
474 - public function filter_optimize_images( $in )
609 + public function replace_icon_callback( $matches )
475 610 {
611 + if ( array_key_exists( '2', $matches ) ) {
612 + $sizes = explode( 'x', $matches[2] );
613 + $width = $sizes[0];
614 + $height = $sizes[1];
615 + } else {
616 + $width = 180;
617 + $height = 180;
618 + }
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 + }
626 + }
627 +
628 + public function filter_optimize_images( $in, $testing = false )
629 + {
476 630 /*
477 631 * potential future functional improvements:
478 632 *
479 - * picture element.
480 633 * filter for critical CSS.
481 634 */
482 635 $to_replace = array();
636 + $to_preload = '';
483 637
484 - // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
485 - 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() ) {
486 640 $in = autoptimizeBase::replace_contents_with_marker_if_exists(
487 - 'NOSCRIPT',
488 - '<noscript',
489 - '#<noscript.*?<\/noscript>#is',
641 + 'SCRIPT',
642 + '<script',
643 + '#<(?:no)?script.*?<\/(?:no)?script>#is',
490 644 $in
491 645 );
492 646 }
493 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 +
494 653 // extract img tags.
495 654 if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
496 655 foreach ( $matches[0] as $tag ) {
656 + $tag = apply_filters( 'autoptimize_filter_imgopt_tag_preopt', $tag );
657 +
497 658 $orig_tag = $tag;
498 659 $imgopt_w = '';
499 660 $imgopt_h = '';
500 661
@@ -500,20 +661,22 @@
500 661
501 662 // first do (data-)srcsets.
502 663 if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
503 664 foreach ( $allsrcsets as $srcset ) {
504 - $srcset = $srcset[2];
505 - $srcsets = explode( ',', $srcset );
665 + $srcset = $srcset[2];
666 + $orig_srcset = $srcset;
667 + $srcsets = explode( ',', $srcset );
506 668 foreach ( $srcsets as $indiv_srcset ) {
507 669 $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
508 670 if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
509 671 $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
510 672 }
511 - 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 ) ) {
512 674 $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
513 - $tag = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
675 + $srcset = str_replace( $indiv_srcset_parts[0], $imgopt_url, $srcset );
514 676 }
515 677 }
678 + $tag = str_replace( $orig_srcset, $srcset, $tag );
516 679 }
517 680 }
518 681
519 682 // proceed with img src.
@@ -522,29 +685,34 @@
522 685 $imgopt_w = $_get_size['width'];
523 686 $imgopt_h = $_get_size['height'];
524 687
525 688 // then start replacing images src.
526 - if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
527 - foreach ( $urls as $url ) {
528 - $full_src_orig = $url[0];
529 - $url = $url[1];
530 - if ( $this->can_optimize_image( $url ) ) {
531 - $imgopt_url = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
532 - $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
533 - $tag = str_replace( $full_src_orig, $full_imgopt_src, $tag );
534 - }
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;
535 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 );
536 710 }
537 711
538 712 // do lazyload stuff.
539 - if ( $this->should_lazyload() && str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
540 - $noscript_tag = '<noscript>' . $tag . '</noscript>';
541 - $tag = str_replace( 'srcset=', 'data-srcset=', $tag );
542 -
543 - // add lazyload class.
544 - $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
545 -
546 - // set placeholder.
713 + if ( $this->should_lazyload( $in ) && ! empty( $url ) ) {
714 + // first do lpiq placeholder logic.
547 715 if ( strpos( $url, $this->get_imgopt_host() ) === 0 ) {
548 716 // if all img src have been replaced during srcset, we have to extract the
549 717 // origin url from the imgopt one to be able to set a lqip placeholder.
550 718 $_url = substr( $url, strpos( $url, '/http' ) + 1 );
@@ -550,27 +718,35 @@
550 718 $_url = substr( $url, strpos( $url, '/http' ) + 1 );
551 719 } else {
552 720 $_url = $url;
553 721 }
554 - if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
555 - $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait,w_' . $imgopt_w . ',h_' . $imgopt_h . '/' . $_url;
556 - } else {
557 - $placeholder = $this->get_default_lazyload_placeholder( $imgopt_w, $imgopt_h );
558 - }
559 - $placeholder = ' src=\'' . apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $placeholder );
560 722
561 - // add min-heigth off by default as it can deform images, can be enabled with filter.
562 - $min_height = '';
563 - if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_addminheight', false ) ) {
564 - $min_height = ' style="min-height:' . $imgopt_h . 'px;"';
723 + $_url = $this->normalize_img_url( $_url );
724 +
725 + $placeholder = '';
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 ) ) {
727 + $lqip_w = '';
728 + $lqip_h = '';
729 + if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
730 + $lqip_w = ',w_' . $imgopt_w;
731 + }
732 + if ( isset( $imgopt_h ) && ! empty( $imgopt_h ) ) {
733 + $lqip_h = ',h_' . $imgopt_h;
734 + }
735 + $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait' . $lqip_w . $lqip_h . '/' . $_url;
565 736 }
737 + // then call add_lazyload-function with lpiq placeholder if set.
738 + $tag = $this->add_lazyload( $tag, $placeholder );
739 + }
566 740
567 - // add noscript & placeholder.
568 - $tag = $noscript_tag . str_replace( ' src=', $min_height . $placeholder . '\' data-src=', $tag );
569 - $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
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 );
570 744 }
571 745
572 - // add tag to array for later replacement.
746 + $tag = apply_filters( 'autoptimize_filter_imgopt_tag_postopt', $tag );
747 +
748 + // and add tag to array for later replacement.
573 749 if ( $tag !== $orig_tag ) {
574 750 $to_replace[ $orig_tag ] = $tag;
575 751 }
576 752 }
@@ -578,38 +754,72 @@
578 754
579 755 // and replace all.
580 756 $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
581 757
582 - // img thumbnails in e.g. woocommerce.
583 - if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_imgopt_datathumbs', true ) ) {
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 + }
771 + }
772 +
773 + // background-image in inline style.
774 + if ( ( strpos( $out, 'background-image:' ) !== false || strpos( $out, 'background:' ) !== false ) && strpos( $out, 'url(' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
584 775 $out = preg_replace_callback(
585 - '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
586 - array( $this, 'replace_data_thumbs' ),
776 + '/style=(?:"|\')[^<>]*?background(?:-image)?:[^;"\'()>]*url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
777 + array( $this, 'replace_img_callback' ),
587 778 $out
588 779 );
589 780 }
590 781
591 - // background-image in inline style.
592 - if ( strpos( $out, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
782 + // act on icon links.
783 + if ( ( strpos( $out, '<link rel="icon"' ) !== false || ( strpos( $out, "<link rel='icon'" ) !== false ) ) && apply_filters( 'autoptimize_filter_imgopt_linkicon', true ) ) {
593 784 $out = preg_replace_callback(
594 - '/style=(?:"|\').*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
595 - array( $this, 'replace_img_callback' ),
785 + '/<link\srel=(?:"|\')(?:apple-touch-)?icon(?:-precomposed)?(?:"|\').*\shref=(?:"|\')(.*)(?:"|\')(?:\ssizes=(?:"|\')(\d*x\d*)(?:"|\'))?\s?\/?>/Um',
786 + array( $this, 'replace_icon_callback' ),
596 787 $out
597 788 );
598 789 }
599 790
600 - // and restore noscript tags if these were hidden for lazyload purposes.
791 + // lazyload picture source tags and bgimage.
601 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() ) {
602 801 $out = autoptimizeBase::restore_marked_content(
603 - 'NOSCRIPT',
802 + 'SCRIPT',
604 803 $out
605 804 );
606 805 }
607 806
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 + }
812 + }
813 +
814 + if ( ! empty( $to_preload ) ) {
815 + $out = autoptimizeExtra::inject_preloads( $to_preload, $out );
816 + }
817 +
608 818 return $out;
609 819 }
610 820
611 - public function get_size_from_tag( $tag ) {
821 + public static function get_size_from_tag( $tag ) {
612 822 // reusable function to extract widht and height from an image tag
613 823 // enforcing a filterable maximum width and height (default 4999X4999).
614 824 $width = '';
615 825 $height = '';
@@ -628,15 +838,19 @@
628 838 // check for and enforce (filterable) max sizes.
629 839 $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
630 840 if ( $width > $_max_width ) {
631 841 $_width = $_max_width;
632 - $height = $_width / $width * $height;
842 + if ( ! empty( $height ) && is_int( $height ) ) {
843 + $height = $_width / $width * $height;
844 + }
633 845 $width = $_width;
634 846 }
635 847 $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
636 848 if ( $height > $_max_height ) {
637 849 $_height = $_max_height;
638 - $width = $_height / $height * $width;
850 + if ( ! empty( $width ) && is_int( $width ) ) {
851 + $width = $_height / $height * $width;
852 + }
639 853 $height = $_height;
640 854 }
641 855
642 856 return array(
@@ -647,22 +861,37 @@
647 861
648 862 /**
649 863 * Lazyload functions
650 864 */
651 - public function should_lazyload() {
652 - if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) ) {
865 + public static function should_lazyload_wrapper( $no_meta = false ) {
866 + // needed in autoptimizeMain.php.
867 + $self = new self();
868 + return $self->should_lazyload( '', $no_meta );
869 + }
870 +
871 + public function should_lazyload( $context = '', $no_meta = false ) {
872 + if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && false === $this->check_nolazy() ) {
653 873 $lazyload_return = true;
654 874 } else {
655 875 $lazyload_return = false;
656 876 }
657 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 +
883 + $lazyload_return = apply_filters( 'autoptimize_filter_imgopt_should_lazyload', $lazyload_return, $context );
884 +
658 885 return $lazyload_return;
659 886 }
660 887
661 - public static function should_lazyload_wrapper() {
662 - // needed in autoptimizeMain.php.
663 - $self = new self();
664 - return $self->should_lazyload();
888 + public static function check_nolazy() {
889 + if ( array_key_exists( 'ao_nolazy', $_GET ) && '1' === $_GET['ao_nolazy'] ) {
890 + return true;
891 + } else {
892 + return false;
893 + }
665 894 }
666 895
667 896 public function filter_lazyload_images( $in )
668 897 {
@@ -667,63 +896,115 @@
667 896 public function filter_lazyload_images( $in )
668 897 {
669 898 // only used is image optimization is NOT active but lazyload is.
670 899 $to_replace = array();
900 + $to_preload = '';
671 901
672 - // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
902 + // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
673 903 $out = autoptimizeBase::replace_contents_with_marker_if_exists(
674 - 'NOSCRIPT',
675 - '<noscript',
676 - '#<noscript.*?<\/noscript>#is',
904 + 'SCRIPT',
905 + '<script',
906 + '#<(?:no)?script.*?<\/(?:no)?script>#is',
677 907 $in
678 908 );
679 909
680 - // 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.
681 914 if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
682 915 foreach ( $matches[0] as $tag ) {
683 - $to_replace[ $tag ] = $this->add_lazyload( $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.
922 + if ( $this->should_lazyload( $out ) ) {
923 + $to_replace[ $tag ] = $this->add_lazyload( $tag );
924 + }
684 925 }
685 926 $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
686 927 }
687 928
929 + // and also lazyload picture tag.
930 + $out = $this->process_picture_tag( $out, false, true );
931 +
932 + // and inline style blocks with background-image.
933 + $out = $this->process_bgimage( $out );
934 +
688 935 // restore noscript tags.
689 936 $out = autoptimizeBase::restore_marked_content(
690 - 'NOSCRIPT',
937 + 'SCRIPT',
691 938 $out
692 939 );
693 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 +
694 952 return $out;
695 953 }
696 954
697 - public function add_lazyload( $tag ) {
955 + public function add_lazyload( $tag, $placeholder = '' ) {
698 956 // adds actual lazyload-attributes to an image node.
699 - 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 ) {
966 + $tag = $this->maybe_fix_missing_quotes( $tag );
967 +
700 968 // store original tag for use in noscript version.
701 - $noscript_tag = '<noscript>' . $tag . '</noscript>';
969 + $noscript_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>';
702 970
971 + $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
972 +
703 973 // insert lazyload class.
704 - $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
974 + $tag = $this->inject_classes_in_tag( $tag, "$lazyload_class " );
705 975
706 - // get image width & heigth for placeholder fun (and to prevent content reflow).
707 - $_get_size = $this->get_size_from_tag( $tag );
708 - $width = $_get_size['width'];
709 - $height = $_get_size['height'];
710 - if ( false === $width ) {
711 - $widht = 210; // default width for SVG placeholder.
976 + if ( ! $placeholder || empty( $placeholder ) ) {
977 + // get image width & heigth for placeholder fun (and to prevent content reflow).
978 + $_get_size = $this->get_size_from_tag( $tag );
979 + $width = $_get_size['width'];
980 + $height = $_get_size['height'];
981 + if ( false === $width || empty( $width ) ) {
982 + $width = 210; // default width for SVG placeholder.
983 + }
984 + if ( false === $height || empty( $height ) ) {
985 + $height = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
986 + }
987 +
988 + // insert the actual lazyload stuff.
989 + // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
990 + $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
712 991 }
713 - if ( false === $height ) {
714 - $heigth = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
992 +
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 );
996 +
997 + // move sizes to data-sizes unless filter says no.
998 + if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
999 + $tag = str_replace( ' sizes=', ' data-sizes=', $tag );
715 1000 }
716 1001
717 - // insert the actual lazyload stuff.
718 - // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
719 - $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
720 - $tag = str_replace( ' src=', ' src=\'' . $placeholder . '\' data-src=', $tag );
721 - $tag = str_replace( ' srcset=', ' data-srcset=', $tag );
722 -
723 1002 // add the noscript-tag from earlier.
724 1003 $tag = $noscript_tag . $tag;
725 1004 $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
1005 + } else {
1006 + $tag = apply_filters( 'autoptimize_filter_imgopt_not_lazyloaded_img', $tag );
726 1007 }
727 1008
728 1009 return $tag;
729 1010 }
@@ -728,8 +1009,12 @@
728 1009 return $tag;
729 1010 }
730 1011
731 1012 public function add_lazyload_js_footer() {
1013 + if ( false === autoptimizeMain::should_buffer() || autoptimizeMain::is_amp_markup( '' ) ) {
1014 + return;
1015 + }
1016 +
732 1017 // The JS will by default be excluded form autoptimization but this can be changed with a filter.
733 1018 $noptimize_flag = '';
734 1019 if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
735 1020 $noptimize_flag = ' data-noptimize="1"';
@@ -734,21 +1019,93 @@
734 1019 if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
735 1020 $noptimize_flag = ' data-noptimize="1"';
736 1021 }
737 1022
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 +
1031 + $cdn_url = $this->get_cdn_url();
1032 + if ( ! empty( $cdn_url ) ) {
1033 + $cdn_url = rtrim( $cdn_url, '/' );
1034 + $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
1035 + }
1036 +
1037 + $type_js = '';
1038 + if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
1039 + $type_js = ' type="text/javascript"';
1040 + }
1041 +
738 1042 // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
739 - echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<style>.lazyload,.lazyloading{opacity:0;}.lazyloaded{opacity:1;transition:opacity 300ms;}</style><noscript><style>.lazyload{display:none;}</style></noscript>' );
740 - echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
741 - echo '<script async' . $noptimize_flag . ' src=\'' . plugins_url( 'external/js/lazysizes.min.js', __FILE__ ) . '\'></script>';
1043 + echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<noscript><style>.lazyload{display:none;}</style></noscript>' );
1044 + echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
1045 + echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
1046 + }
742 1047
743 - // And add webp detection and loading JS.
744 - if ( $this->should_webp() ) {
745 - $_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);";
746 - $_webp_load = "document.addEventListener('lazybeforeunveil',function({target:c}){supportsWebP&&['data-src','data-srcset'].forEach(function(a){attr=c.getAttribute(a),null!==attr&&c.setAttribute(a,attr.replace(/\/client\//,'/client/to_webp,'))})});";
747 - echo apply_filters( 'autoptimize_filter_imgopt_webp_js', '<script' . $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 '';
748 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;
749 1073 }
750 1074
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() {
1096 + // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
1097 + static $cdn_url = null;
1098 +
1099 + if ( null === $cdn_url ) {
1100 + $cdn_url = autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' );
1101 + $cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $cdn_url );
1102 + $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url );
1103 + }
1104 +
1105 + return $cdn_url;
1106 + }
1107 +
751 1108 public function get_lazyload_exclusions() {
752 1109 // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
753 1110 static $exclude_lazyload_array = null;
754 1111
@@ -755,9 +1112,9 @@
755 1112 if ( null === $exclude_lazyload_array ) {
756 1113 $options = $this->options;
757 1114
758 1115 // set default exclusions.
759 - $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset' );
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"' );
760 1117
761 1118 // add from setting.
762 1119 if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
763 1120 $exclude_lazyload_option = $options['autoptimize_imgopt_text_field_5'];
@@ -776,9 +1133,9 @@
776 1133 public function inject_classes_in_tag( $tag, $target_class ) {
777 1134 if ( strpos( $tag, 'class=' ) !== false ) {
778 1135 $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
779 1136 } else {
780 - $tag = str_replace( '<img ', '<img class="' . trim( $target_class ) . '" ', $tag );
1137 + $tag = preg_replace( '/(<[a-zA-Z]*)\s/', '$1 class="' . trim( $target_class ) . '" ', $tag );
781 1138 }
782 1139
783 1140 return $tag;
784 1141 }
@@ -786,42 +1143,130 @@
786 1143 public function get_default_lazyload_placeholder( $imgopt_w, $imgopt_h ) {
787 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';
788 1145 }
789 1146
790 - public function should_webp() {
791 - static $webp_return = null;
1147 + public function should_ngimg() {
1148 + static $ngimg_return = null;
792 1149
793 - if ( is_null( $webp_return ) ) {
794 - // webp only works if imgopt and lazyload are also active.
795 - if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && $this->imgopt_active() ) {
796 - $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;
797 1154 } else {
798 - $webp_return = false;
1155 + $ngimg_return = false;
799 1156 }
800 1157 }
801 1158
802 - return $webp_return;
1159 + return $ngimg_return;
803 1160 }
804 1161
1162 + public function process_picture_tag( $in, $imgopt = false, $lazy = false ) {
1163 + // check if "<picture" is present and if filter allows us to process <picture>.
1164 + if ( strpos( $in, '<picture' ) === false || apply_filters( 'autoptimize_filter_imgopt_dopicture', true ) === false ) {
1165 + return $in;
1166 + }
1167 +
1168 + $_exclusions = $this->get_lazyload_exclusions();
1169 + $to_replace_pict = array();
1170 +
1171 + // extract and process each picture-node.
1172 + preg_match_all( '#<picture.*</picture>#Usmi', $in, $_pictures, PREG_SET_ORDER );
1173 + foreach ( $_pictures as $_picture ) {
1174 + $_picture = $this->maybe_fix_missing_quotes( $_picture );
1175 + if ( strpos( $_picture[0], '<source ' ) !== false && preg_match_all( '#<source .*srcset=(?:"|\')(?!data)(.*)(?:"|\').*>#Usmi', $_picture[0], $_sources, PREG_SET_ORDER ) !== false ) {
1176 + foreach ( $_sources as $_source ) {
1177 + $_picture_replacement = $_source[0];
1178 +
1179 + // should we optimize the image?
1180 + if ( $imgopt && $this->can_optimize_image( $_source[1], $_picture[0] ) ) {
1181 + $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
1182 + }
1183 + // should we lazy-load?
1184 + if ( $lazy && $this->should_lazyload() && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
1185 + $_picture_replacement = str_replace( ' srcset=', ' data-srcset=', $_picture_replacement );
1186 + }
1187 + $to_replace_pict[ $_source[0] ] = $_picture_replacement;
1188 + }
1189 + }
1190 + }
1191 +
1192 + // and return the fully procesed $in.
1193 + $out = str_replace( array_keys( $to_replace_pict ), array_values( $to_replace_pict ), $in );
1194 +
1195 + return $out;
1196 + }
1197 +
1198 + public function process_bgimage( $in ) {
1199 + if ( strpos( $in, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_lazyload_backgroundimages', true ) ) {
1200 + $out = preg_replace_callback(
1201 + '/(<(?:article|aside|body|div|footer|header|p|section|span|table)[^>]*)\sstyle=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)[^>]*/',
1202 + array( $this, 'lazyload_bgimg_callback' ),
1203 + $in
1204 + );
1205 + return $out;
1206 + }
1207 + return $in;
1208 + }
1209 +
1210 + public function lazyload_bgimg_callback( $matches ) {
1211 + if ( str_ireplace( $this->get_lazyload_exclusions(), '', $matches[0] ) === $matches[0] ) {
1212 + // get placeholder & lazyload class strings.
1213 + $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( 500, 300 ) );
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] );
1217 + // replace background-image URL with SVG placeholder.
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] );
1221 + // add data-bg attribute with real background-image URL for lazyload to pick up.
1222 + $out = str_replace( $matches[1], $matches[1] . ' data-bg="' . $bgimg_src . '"', $out );
1223 + // and finally add lazyload class to tag.
1224 + $out = $this->inject_classes_in_tag( $out, "$lazyload_class " );
1225 + return $out;
1226 + }
1227 + return $matches[0];
1228 + }
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 +
1235 + public function maybe_fix_missing_quotes( $tag_in ) {
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.
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 );
1239 + return $tag_out;
1240 + } else {
1241 + return $tag_in;
1242 + }
1243 + }
1244 +
805 1245 /**
806 1246 * Admin page logic and related functions below.
807 1247 */
808 1248 public function imgopt_admin_menu()
809 1249 {
810 - add_submenu_page(
811 - null,
812 - 'autoptimize_imgopt',
813 - 'autoptimize_imgopt',
814 - 'manage_options',
815 - 'autoptimize_imgopt',
816 - array( $this, 'imgopt_options_page' )
817 - );
1250 + // no acces if multisite and not network admin and no site config allowed.
1251 + if ( autoptimizeConfig::should_show_menu_tabs() ) {
1252 + add_submenu_page(
1253 + '',
1254 + 'autoptimize_imgopt',
1255 + 'autoptimize_imgopt',
1256 + 'manage_options',
1257 + 'autoptimize_imgopt',
1258 + array( $this, 'imgopt_options_page' )
1259 + );
1260 + }
818 1261 register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
819 1262 }
820 1263
821 1264 public function add_imgopt_tab( $in )
822 1265 {
823 - $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
1266 + if ( autoptimizeConfig::should_show_menu_tabs() ) {
1267 + $in = array_merge( $in, array( 'autoptimize_imgopt' => apply_filters( 'autoptimize_filter_imgopt_tab_text', esc_html__( 'Images', 'autoptimize' ) ) ) );
1268 + }
824 1269
825 1270 return $in;
826 1271 }
827 1272
@@ -826,11 +1271,14 @@
826 1271 }
827 1272
828 1273 public function imgopt_options_page()
829 1274 {
1275 + // phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace
1276 + // phpcs:disable Generic.Formatting.DisallowMultipleStatements.SameLine
1277 +
830 1278 // Check querystring for "refreshCacheChecker" and call cachechecker if so.
831 1279 if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
832 - $this->query_img_provider_stats();
1280 + $this->query_img_provider_stats( true );
833 1281 }
834 1282
835 1283 $options = $this->fetch_options();
836 1284 $sp_url_suffix = $this->get_service_url_suffix();
@@ -838,17 +1286,26 @@
838 1286 <style>
839 1287 #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
840 1288 #ao_settings_form .form-table th {font-weight: normal;}
841 1289 #autoptimize_imgopt_descr{font-size: 120%;}
1290 + #ao_settings_form td p.condition-warning {color: orange;}
842 1291 </style>
1292 + <script>document.title = "Autoptimize: <?php esc_html_e( 'Images', 'autoptimize' ); ?> " + document.title;</script>
843 1293 <div class="wrap">
844 - <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>
845 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 } ?>
846 1303 <?php if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) { ?>
847 1304 <div class="notice-warning notice"><p>
848 1305 <?php
849 1306 // translators: "Autoptimize support forum" will appear in a "a href".
850 - 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>' );
851 1308 ?>
852 1309 </p></div>
853 1310 <?php } ?>
854 1311
@@ -853,9 +1310,9 @@
853 1310 <?php } ?>
854 1311
855 1312 <?php if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] && ! autoptimizeImages::instance()->launch_ok() ) { ?>
856 1313 <div class="notice-warning notice"><p>
857 - <?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' ); ?>
858 1315 </p></div>
859 1316 <?php } ?>
860 1317
861 1318 <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
@@ -861,24 +1318,24 @@
861 1318 <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
862 1319 <div class="notice-warning notice"><p>
863 1320 <?php
864 1321 // translators: "disable Jetpack's site accelerator for images" will appear in a "a href" linking to the jetpack settings page.
865 - 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>' );
866 1323 ?>
867 1324 </p></div>
868 1325 <?php } ?>
869 - <form id='ao_settings_form' action='options.php' method='post'>
1326 + <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
870 1327 <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
871 - <h2><?php _e( 'Image optimization', 'autoptimize' ); ?></h2>
872 - <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>
873 1330 <table class="form-table">
874 1331 <tr>
875 - <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
1332 + <th scope="row"><?php esc_html_e( 'Image optimization & CDN', 'autoptimize' ); ?></th>
876 1333 <td>
877 - <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>
878 1335 <?php
879 1336 // show shortpixel status.
880 - $_notice = autoptimizeImages::instance()->get_status_notice();
1337 + $_notice = autoptimizeImages::instance()->get_imgopt_status_notice();
881 1338 if ( $_notice ) {
882 1339 switch ( $_notice['status'] ) {
883 1340 case 2:
884 1341 $_notice_color = 'green';
@@ -886,38 +1343,44 @@
886 1343 case 1:
887 1344 $_notice_color = 'orange';
888 1345 break;
889 1346 case -1:
890 - $_notice_color = 'red';
891 - break;
892 1347 case -2:
1348 + case -3:
893 1349 $_notice_color = 'red';
894 1350 break;
895 1351 default:
896 1352 $_notice_color = 'green';
897 1353 }
898 - 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>' );
899 1355 } else {
900 1356 // translators: link points to shortpixel.
901 - $upsell_msg_1 = '<p>' . sprintf( __( 'Get more Google love and improve your website\'s loading speed by having the 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>' );
902 1358 if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
903 - $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>' );
904 1360 } else {
905 - // translators: link points to shortpixel.
906 - $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>' );
907 1363 }
908 - 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>' );
909 1365 }
910 1366 // translators: link points to shortpixel FAQ.
911 - $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>' );
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' );
912 1369 // translators: links points to shortpixel TOS & Privacy Policy.
913 - $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>' );
914 - 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>' );
915 1372 ?>
916 1373 </td>
917 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>
918 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"'; } ?>>
919 - <th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
1382 + <th scope="row"><?php esc_html_e( 'Image Optimization quality', 'autoptimize' ); ?></th>
920 1383 <td>
921 1384 <label>
922 1385 <select name='autoptimize_imgopt_settings[autoptimize_imgopt_select_field_2]'>
923 1386 <?php
@@ -937,33 +1400,60 @@
937 1400 </label>
938 1401 <p>
939 1402 <?php
940 1403 // translators: link points to shortpixel image test page.
941 - 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>' ) );
942 1405 ?>
943 1406 </p>
944 1407 </td>
945 1408 </tr>
946 - <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"'; } ?>>
947 - <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>
948 1438 <td>
949 - <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>
950 1440 </td>
951 1441 </tr>
952 - <tr>
953 - <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>
954 1444 <td>
955 - <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>
956 1446 </td>
957 1447 </tr>
958 - <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"'; } ?>>
959 - <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>
960 1450 <td>
961 - <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>
962 1452 </td>
963 1453 </tr>
964 1454 </table>
965 - <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>
966 1456 </form>
967 1457 <script>
968 1458 jQuery(document).ready(function() {
969 1459 jQuery("#autoptimize_imgopt_checkbox").change(function() {
@@ -968,26 +1458,21 @@
968 1458 jQuery(document).ready(function() {
969 1459 jQuery("#autoptimize_imgopt_checkbox").change(function() {
970 1460 if (this.checked) {
971 1461 jQuery("#autoptimize_imgopt_quality").show("slow");
972 - jQuery("#autoptimize_imgopt_webp").show("slow");
1462 + jQuery("#autoptimize_imgopt_ngimg").show("slow");
1463 + jQuery("#autoptimize_imgopt_optimization_exclusions").show("slow");
973 1464 } else {
974 1465 jQuery("#autoptimize_imgopt_quality").hide("slow");
975 - jQuery("#autoptimize_imgopt_webp").hide("slow");
1466 + jQuery("#autoptimize_imgopt_ngimg").hide("slow");
1467 + jQuery("#autoptimize_imgopt_optimization_exclusions").hide("slow");
976 1468 }
977 1469 });
978 - jQuery("#autoptimize_imgopt_webp_checkbox").change(function() {
979 - if (this.checked) {
980 - jQuery("#autoptimize_imgopt_lazyload_checkbox")[0].checked = true;
981 - jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
982 - }
983 - });
984 1470 jQuery("#autoptimize_imgopt_lazyload_checkbox").change(function() {
985 1471 if (this.checked) {
986 - jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1472 + jQuery(".autoptimize_lazyload_child").show("slow");
987 1473 } else {
988 - jQuery("#autoptimize_imgopt_lazyload_exclusions").hide("slow");
989 - jQuery("#autoptimize_imgopt_webp_checkbox")[0].checked = false;
1474 + jQuery(".autoptimize_lazyload_child").hide("slow");
990 1475 }
991 1476 });
992 1477 });
993 1478 </script>
@@ -997,38 +1482,54 @@
997 1482 /**
998 1483 * Ïmg opt status as used on dashboard.
999 1484 */
1000 1485 public function get_imgopt_status_notice() {
1001 - if ( $this->imgopt_active() ) {
1002 - $_imgopt_notice = '';
1003 - $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
1004 - $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
1005 - $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
1486 + if ( $this->imgopt_active() && apply_filters( 'autoptimize_filter_imgopt_status_shortpixel', true ) ) {
1487 + $_imgopt_notice = '';
1488 + $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1489 + $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
1490 + $_imgopt_upsell = 'https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell';
1491 + $_imgopt_assoc = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1492 + $_imgopt_unreach = 'https://shortpixel.helpscoutdocs.com/article/148-why-are-my-images-redirected-from-cdn-shortpixel-ai';
1006 1493
1007 1494 if ( is_array( $_stat ) ) {
1008 1495 if ( 1 == $_stat['Status'] ) {
1009 1496 // translators: "add more credits" will appear in a "a href".
1010 - $_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>' );
1011 1498 } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1012 1499 // translators: "add more credits" will appear in a "a href".
1013 - $_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>' );
1014 - $_imgopt_stats_refresh_url = add_query_arg( array(
1015 - 'page' => 'autoptimize_imgopt',
1016 - 'refreshImgProvStats' => '1',
1017 - ), admin_url( 'options-general.php' ) );
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>' );
1501 + // translators: "associate your domain" will appear in a "a href".
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>' );
1503 + } elseif ( -3 == $_stat['Status'] ) {
1504 + // translators: "check the documentation here" will appear in a "a href".
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>' );
1506 + } else {
1507 + $_imgopt_upsell = 'https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell';
1508 + // translators: "log in to check your account" will appear in a "a href".
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>' );
1510 + }
1511 +
1512 + // add info on freshness + refresh link if status is not 2 (good shape).
1513 + if ( 2 != $_stat['Status'] ) {
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 + );
1018 1521 if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
1019 - $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( 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'] );
1020 1523 } else {
1021 - $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1524 + $_imgopt_stats_last_run = esc_html__( 'based on previously fetched data', 'autoptimize' );
1022 1525 }
1023 1526 $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1024 1527 // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
1025 - $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1026 - } else {
1027 - $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1028 - // translators: "log in to check your account" will appear in a "a href".
1029 - $_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>' );
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>).' );
1030 1529 }
1530 +
1531 + // and make the full notice filterable.
1031 1532 $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
1032 1533
1033 1534 return array(
1034 1535 'status' => $_stat['Status'],
@@ -1046,18 +1547,24 @@
1046 1547 }
1047 1548
1048 1549 /**
1049 1550 * Get img provider stats (used to display notice).
1551 + *
1552 + * @param bool $_refresh Should the stats be forcefully refreshed or not.
1050 1553 */
1051 - public function query_img_provider_stats() {
1052 - 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 ) ) {
1053 1556 $url = '';
1054 - $endpoint = $this->get_imgopt_host() . 'read-domain/';
1557 + $stat_dom = 'https://no-cdn.shortpixel.ai/';
1558 + $endpoint = $stat_dom . 'read-domain/';
1055 1559 $domain = AUTOPTIMIZE_SITE_DOMAIN;
1056 1560
1057 1561 // make sure parse_url result makes sense, keeping $url empty if not.
1058 1562 if ( $domain && ! empty( $domain ) ) {
1059 1563 $url = $endpoint . $domain;
1564 + if ( true === $_refresh ) {
1565 + $url = $url . '/refresh';
1566 + }
1060 1567 }
1061 1568
1062 1569 $url = apply_filters(
1063 1570 'autoptimize_filter_imgopt_stat_url',
@@ -1070,9 +1577,9 @@
1070 1577 $response = wp_remote_get( $url );
1071 1578 if ( ! is_wp_error( $response ) ) {
1072 1579 if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
1073 1580 $stats = json_decode( wp_remote_retrieve_body( $response ), true );
1074 - update_option( 'autoptimize_imgopt_provider_stat', $stats );
1581 + autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_provider_stat', $stats );
1075 1582 }
1076 1583 }
1077 1584 }
1078 1585 }
@@ -1094,16 +1601,20 @@
1094 1601 {
1095 1602 static $launch_status = null;
1096 1603
1097 1604 if ( null === $launch_status ) {
1098 - $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 +
1099 1610 $magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
1100 - $has_launched = get_option( 'autoptimize_imgopt_launched', '' );
1611 + $has_launched = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_launched', '' );
1101 1612 $launch_status = false;
1102 1613 if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
1103 1614 $launch_status = true;
1104 1615 if ( ! $has_launched ) {
1105 - update_option( 'autoptimize_imgopt_launched', 'on' );
1616 + autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_launched', 'on' );
1106 1617 }
1107 1618 }
1108 1619 }
1109 1620
@@ -1119,9 +1630,9 @@
1119 1630 public function get_imgopt_provider_userstatus() {
1120 1631 static $_provider_userstatus = null;
1121 1632
1122 1633 if ( is_null( $_provider_userstatus ) ) {
1123 - $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
1634 + $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1124 1635 if ( is_array( $_stat ) ) {
1125 1636 if ( array_key_exists( 'Status', $_stat ) ) {
1126 1637 $_provider_userstatus['Status'] = $_stat['Status'];
1127 1638 } else {
@@ -1133,42 +1644,14 @@
1133 1644 } else {
1134 1645 // if no timestamp then we return "".
1135 1646 $_provider_userstatus['timestamp'] = '';
1136 1647 }
1648 + } else {
1649 + // no provider_stat yet, assume/ return all OK.
1650 + $_provider_userstatus['Status'] = 2;
1651 + $_provider_userstatus['timestamp'] = '';
1137 1652 }
1138 1653 }
1139 1654
1140 1655 return $_provider_userstatus;
1141 - }
1142 -
1143 - public function get_status_notice() {
1144 - if ( $this->imgopt_active() ) {
1145 - $notice = '';
1146 - $stat = $this->get_imgopt_provider_userstatus();
1147 - $upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
1148 - $assoc = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1149 -
1150 - if ( is_array( $stat ) ) {
1151 - if ( 1 == $stat['Status'] ) {
1152 - // translators: "add more credits" will appear in a "a href".
1153 - $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 rel="noopener noreferrer" href="' . $upsell . '" target="_blank">', '</a>' );
1154 - } elseif ( -1 == $stat['Status'] || -2 == $stat['Status'] ) {
1155 - // translators: "add more credits" will appear in a "a href".
1156 - $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 rel="noopener noreferrer" href="' . $upsell . '" target="_blank">', '</a>' );
1157 - // translators: "associate your domain" will appear in a "a href".
1158 - $notice = $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="' . $assoc . '" target="_blank">', '</a>' );
1159 - } else {
1160 - $upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1161 - // translators: "log in to check your account" will appear in a "a href".
1162 - $notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $upsell . '" target="_blank">', '</a>' );
1163 - }
1164 - $notice = apply_filters( 'autoptimize_filter_imgopt_notice', $notice );
1165 -
1166 - return array(
1167 - 'status' => $stat['Status'],
1168 - 'notice' => $notice,
1169 - );
1170 - }
1171 - }
1172 - return false;
1173 1656 }
1174 1657 }