Autoptimize - Version 2.4.2

Version Description

  • misc. improvements to image optimization logic
  • update PAnD framework to latest version to fix issues with notices not staying dismissed
  • patched JS minifying component to not break template literals as reported by Alex Kozack
  • bugfix for Google fonts subset concatenation by Rocco Aliberti, thanks!
  • bugfix not to remove querystrings if that option is not on in "Extra", kudo's to Diego Versiani for pointing out the inconsistency
  • tested and confirmed working with WordPress 5.0 (beta 5)
Download this release

Release Info

Developer futtta
Plugin Icon 128x128 Autoptimize
Version 2.4.2
Comparing to
See all releases

Code changes from version 2.4.1 to 2.4.2

autoptimize.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Autoptimize
4
  Plugin URI: https://autoptimize.com/
5
  Description: Optimize your website's performance: JS, CSS, HTML, images, Google Fonts and more!
6
- Version: 2.4.1
7
  Author: Frank Goossens (futtta)
8
  Author URI: https://autoptimize.com/
9
  Text Domain: autoptimize
@@ -20,7 +20,7 @@ if ( ! defined( 'ABSPATH' ) ) {
20
  exit;
21
  }
22
 
23
- define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.4.0' );
24
 
25
  // plugin_dir_path() returns the trailing slash!
26
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
3
  Plugin Name: Autoptimize
4
  Plugin URI: https://autoptimize.com/
5
  Description: Optimize your website's performance: JS, CSS, HTML, images, Google Fonts and more!
6
+ Version: 2.4.2
7
  Author: Frank Goossens (futtta)
8
  Author URI: https://autoptimize.com/
9
  Text Domain: autoptimize
20
  exit;
21
  }
22
 
23
+ define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.4.2' );
24
 
25
  // plugin_dir_path() returns the trailing slash!
26
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
classes/autoptimizeCache.php CHANGED
@@ -282,12 +282,14 @@ class autoptimizeCache
282
 
283
  // Returns the list of files without '.' and '..' elements.
284
  $files = self::get_dir_contents( $parent );
285
- foreach ( $files as $file ) {
286
- $path = $parent . '/' . $file;
287
- $prefixed = ( false !== strpos( $path, $prefix ) );
288
- // Removing only our own (prefixed) directories...
289
- if ( is_dir( $path ) && $prefixed ) {
290
- $ok = self::rmdir( $path );
 
 
291
  }
292
  }
293
 
282
 
283
  // Returns the list of files without '.' and '..' elements.
284
  $files = self::get_dir_contents( $parent );
285
+ if ( is_array( $files ) && ! empty( $files ) ) {
286
+ foreach ( $files as $file ) {
287
+ $path = $parent . '/' . $file;
288
+ $prefixed = ( false !== strpos( $path, $prefix ) );
289
+ // Removing only our own (prefixed) directories...
290
+ if ( is_dir( $path ) && $prefixed ) {
291
+ $ok = self::rmdir( $path );
292
+ }
293
  }
294
  }
295
 
classes/autoptimizeCacheChecker.php CHANGED
@@ -36,9 +36,9 @@ class autoptimizeCacheChecker
36
  {
37
  $do_cache_check = (bool) apply_filters( 'autoptimize_filter_cachecheck_do', true );
38
  $schedule = wp_get_schedule( self::SCHEDULE_HOOK );
39
- $frequency = apply_filters( 'autoptimize_filter_cachecheck_frequency', 'daily' );
40
- if ( ! in_array( $frequency, array( 'hourly', 'daily', 'weekly', 'monthly' ) ) ) {
41
- $frequency = 'daily';
42
  }
43
  if ( $do_cache_check && ( ! $schedule || $schedule !== $frequency ) ) {
44
  wp_schedule_event( time(), $frequency, self::SCHEDULE_HOOK );
@@ -85,7 +85,7 @@ class autoptimizeCacheChecker
85
  public function show_admin_notice()
86
  {
87
  // fixme: make notices dismissable.
88
- if ( (bool) get_option( 'autoptimize_cachesize_notice', false ) ) {
89
  echo '<div class="notice notice-warning"><p>';
90
  _e( '<strong>Autoptimize\'s cache size is getting big</strong>, consider purging the cache. Have a look at <a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank" rel="noopener noreferrer">the Autoptimize FAQ</a> to see how you can keep the cache size under control.', 'autoptimize' );
91
  echo '</p></div>';
@@ -94,7 +94,7 @@ class autoptimizeCacheChecker
94
 
95
  // Notice for image proxy usage.
96
  $_imgopt_notice = autoptimizeExtra::get_imgopt_status_notice_wrapper();
97
- if ( is_array( $_imgopt_notice ) && array_key_exists( 'status', $_imgopt_notice ) && in_array( $_imgopt_notice['status'], array( 1, -1 ) ) ) {
98
  $_dismissible = 'ao-img-opt-notice-';
99
  $_hide_notice = '7';
100
 
36
  {
37
  $do_cache_check = (bool) apply_filters( 'autoptimize_filter_cachecheck_do', true );
38
  $schedule = wp_get_schedule( self::SCHEDULE_HOOK );
39
+ $frequency = apply_filters( 'autoptimize_filter_cachecheck_frequency', 'twicedaily' );
40
+ if ( ! in_array( $frequency, array( 'hourly', 'twicedaily', 'daily', 'weekly', 'monthly' ) ) ) {
41
+ $frequency = 'twicedaily';
42
  }
43
  if ( $do_cache_check && ( ! $schedule || $schedule !== $frequency ) ) {
44
  wp_schedule_event( time(), $frequency, self::SCHEDULE_HOOK );
85
  public function show_admin_notice()
86
  {
87
  // fixme: make notices dismissable.
88
+ if ( (bool) get_option( 'autoptimize_cachesize_notice', false ) && current_user_can( 'manage_options' ) ) {
89
  echo '<div class="notice notice-warning"><p>';
90
  _e( '<strong>Autoptimize\'s cache size is getting big</strong>, consider purging the cache. Have a look at <a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank" rel="noopener noreferrer">the Autoptimize FAQ</a> to see how you can keep the cache size under control.', 'autoptimize' );
91
  echo '</p></div>';
94
 
95
  // Notice for image proxy usage.
96
  $_imgopt_notice = autoptimizeExtra::get_imgopt_status_notice_wrapper();
97
+ if ( current_user_can( 'manage_options' ) && is_array( $_imgopt_notice ) && array_key_exists( 'status', $_imgopt_notice ) && in_array( $_imgopt_notice['status'], array( 1, -1, -2 ) ) ) {
98
  $_dismissible = 'ao-img-opt-notice-';
99
  $_hide_notice = '7';
100
 
classes/autoptimizeExtra.php CHANGED
@@ -153,8 +153,18 @@ class autoptimizeExtra
153
  add_filter( 'wp_resource_hints', array( $this, 'filter_preconnect' ), 10, 2 );
154
  }
155
 
156
- // Optimize Images!
157
- if ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && 'down' !== $options['availabilities']['extra_imgopt']['status'] && ( 'launch' !== $options['availabilities']['extra_imgopt']['status'] || $this->imgopt_launch_ok() ) ) {
 
 
 
 
 
 
 
 
 
 
158
  if ( apply_filters( 'autoptimize_filter_extra_imgopt_do', true ) ) {
159
  add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_optimize_images' ), 10, 1 );
160
  $_imgopt_active = true;
@@ -222,6 +232,7 @@ class autoptimizeExtra
222
  // And add subset if any!
223
  $subset = ( is_array( $font ) ) ? end( $font ) : '';
224
  if ( false !== strpos( $subset, 'subset=' ) ) {
 
225
  $subset = explode( 'subset=', $subset );
226
  $fonts_collection[ $i ]['subsets'] = explode( ',', $subset[1] );
227
  }
@@ -245,12 +256,13 @@ class autoptimizeExtra
245
  foreach ( $fonts_collection as $font ) {
246
  $fonts_string .= '|' . trim( implode( '|', $font['fonts'] ), '|' );
247
  if ( ! empty( $font['subsets'] ) ) {
248
- $subset_string .= implode( ',', $font['subsets'] );
249
  }
250
  }
251
 
252
  if ( ! empty( $subset_string ) ) {
253
- $fonts_string = $fonts_string . '&#038;subset=' . $subset_string;
 
254
  }
255
 
256
  $fonts_string = str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) );
@@ -479,7 +491,7 @@ class autoptimizeExtra
479
  return false;
480
  } elseif ( strpos( $url, '.php' ) !== false ) {
481
  return false;
482
- } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
483
  // fixme: better check against end of string.
484
  return false;
485
  } elseif ( ! empty( $nopti_images ) ) {
@@ -495,6 +507,16 @@ class autoptimizeExtra
495
 
496
  private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
497
  {
 
 
 
 
 
 
 
 
 
 
498
  $filtered_url = apply_filters( 'autoptimize_filter_extra_imgopt_build_url', $orig_url, $width, $height );
499
 
500
  if ( $filtered_url !== $orig_url ) {
@@ -537,12 +559,24 @@ class autoptimizeExtra
537
 
538
  private function normalize_img_urls( $in )
539
  {
 
 
 
 
 
 
 
 
 
 
540
  $parsed_site_url = parse_url( site_url() );
541
 
542
  if ( strpos( $in, 'http' ) !== 0 && strpos( $in, '//' ) === 0 ) {
543
  $in = $parsed_site_url['scheme'] . ':' . $in;
544
  } elseif ( strpos( $in, '/' ) === 0 ) {
545
  $in = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $in;
 
 
546
  }
547
 
548
  return apply_filters( 'autoptimize_filter_extra_imgopt_normalized_url', $in );
@@ -569,7 +603,9 @@ class autoptimizeExtra
569
  static $_img_q = null;
570
 
571
  if ( is_null( $_img_q ) ) {
572
- $_setting = $this->options['autoptimize_extra_select_field_6'];
 
 
573
 
574
  if ( ! $_setting || empty( $_setting ) || ( '1' !== $_setting && '3' !== $_setting ) ) {
575
  // default image opt. value is 2 ("glossy").
@@ -621,7 +657,8 @@ class autoptimizeExtra
621
  $_img_stat_resp = wp_remote_get( $_img_provider_stat_url );
622
  if ( ! is_wp_error( $_img_stat_resp ) ) {
623
  if ( '200' == wp_remote_retrieve_response_code( $_img_stat_resp ) ) {
624
- $_img_provider_stat = json_decode( wp_remote_retrieve_body( $_img_stat_resp ), true );
 
625
  update_option( 'autoptimize_imgopt_provider_stat', $_img_provider_stat );
626
  }
627
  }
@@ -637,7 +674,7 @@ class autoptimizeExtra
637
  $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
638
  $magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
639
  $has_launched = get_option( 'autoptimize_imgopt_launched', '' );
640
- if ( $has_launched || ( array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
641
  $launch_status = true;
642
  if ( ! $has_launched ) {
643
  update_option( 'autoptimize_imgopt_launched', 'on' );
@@ -684,7 +721,7 @@ class autoptimizeExtra
684
  $_extra_options = $this->options;
685
  if ( ! empty( $_extra_options ) && is_array( $_extra_options ) && array_key_exists( 'autoptimize_extra_checkbox_field_5', $_extra_options ) && ! empty( $_extra_options['autoptimize_extra_checkbox_field_5'] ) ) {
686
  $_imgopt_notice = '';
687
- $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
688
  $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
689
  $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
690
 
@@ -692,9 +729,21 @@ class autoptimizeExtra
692
  if ( 1 == $_stat['Status'] ) {
693
  // translators: "add more credits" will appear in a "a href".
694
  $_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>' );
695
- } elseif ( -1 == $_stat['Status'] ) {
696
  // translators: "add more credits" will appear in a "a href".
697
- $_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>' );
 
 
 
 
 
 
 
 
 
 
 
 
698
  } else {
699
  $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
700
  // translators: "log in to check your account" will appear in a "a href".
@@ -717,6 +766,30 @@ class autoptimizeExtra
717
  return $self->get_imgopt_status_notice();
718
  }
719
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
720
  public function admin_menu()
721
  {
722
  add_submenu_page( null, 'autoptimize_extra', 'autoptimize_extra', 'manage_options', 'autoptimize_extra', array( $this, 'options_page' ) );
@@ -732,6 +805,11 @@ class autoptimizeExtra
732
 
733
  public function options_page()
734
  {
 
 
 
 
 
735
  // Working with actual option values from the database here.
736
  // That way any saves are still processed as expected, but we can still
737
  // override behavior by using `new autoptimizeExtra($custom_options)` and not have that custom
@@ -814,6 +892,9 @@ class autoptimizeExtra
814
  case -1:
815
  $_notice_color = 'red';
816
  break;
 
 
 
817
  default:
818
  $_notice_color = 'green';
819
  }
@@ -822,9 +903,10 @@ class autoptimizeExtra
822
  // translators: link points to shortpixel.
823
  $upsell_msg_1 = '<p>' . sprintf( __( 'Get more Google love and improve your website\'s loading speed by having the images optimized on the fly by %1$sShortPixel%2$s and then cached and served fast from a CDN.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
824
  if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
825
- $upsell_msg_2 = __( 'For a limited time only, this service is offered free-for-all, <b>don\'t miss the chance to test it</b> and see how much it could improve your site\'s speed.', 'autoptimize' );
826
  } else {
827
- $upsell_msg_2 = __( 'The service is offered for free for 100 images/month regardless of the traffic used. More image optimizations can be purchased starting with $4.99.', 'autoptimize' );
 
828
  }
829
  echo apply_filters( 'autoptimize_extra_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
830
  }
@@ -879,7 +961,7 @@ class autoptimizeExtra
879
  <tr>
880
  <th scope="row"><?php _e( 'Preconnect to 3rd party domains <em>(advanced users)</em>', 'autoptimize' ); ?></th>
881
  <td>
882
- <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_2]' value='<?php echo esc_attr( $options['autoptimize_extra_text_field_2'] ); ?>'><br /><?php _e( 'Add 3rd party domains you want the browser to <a href="https://www.keycdn.com/support/preconnect/#primary" target="_blank">preconnect</a> to, separated by comma\'s. Make sure to include the correct protocol (HTTP or HTTPS).', 'autoptimize' ); ?></label>
883
  </td>
884
  </tr>
885
  <tr>
@@ -891,7 +973,7 @@ class autoptimizeExtra
891
  printf( __( 'You have "Async JavaScript" installed, %1$sconfiguration of async javascript is best done there%2$s.', 'autoptimize' ), '<a href="' . 'options-general.php?page=async-javascript' . '">', '</a>' );
892
  } else {
893
  ?>
894
- <input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_3]' value='<?php echo esc_attr( $options['autoptimize_extra_text_field_3'] ); ?>'>
895
  <br />
896
  <?php
897
  _e( 'Comma-separated list of local or 3rd party JS-files that should loaded with the <code>async</code> flag. JS-files from your own site will be automatically excluded if added here. ', 'autoptimize' );
153
  add_filter( 'wp_resource_hints', array( $this, 'filter_preconnect' ), 10, 2 );
154
  }
155
 
156
+ // Optimize Images kicks in if;
157
+ // * the option is activated by user.
158
+ // * imgopt user stats does not have status -2.
159
+ // * imgopt status is not "down".
160
+ // * imgopt status is not "launch" or imgopt_launch_ok() returns be true.
161
+ $_do_cdn = true;
162
+ $_userstatus = $this->get_imgopt_provider_userstatus();
163
+ if ( -2 == $_userstatus['Status'] ) {
164
+ $_do_cdn = false;
165
+ }
166
+
167
+ if ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && $_do_cdn && 'down' !== $options['availabilities']['extra_imgopt']['status'] && ( 'launch' !== $options['availabilities']['extra_imgopt']['status'] || $this->imgopt_launch_ok() ) ) {
168
  if ( apply_filters( 'autoptimize_filter_extra_imgopt_do', true ) ) {
169
  add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_optimize_images' ), 10, 1 );
170
  $_imgopt_active = true;
232
  // And add subset if any!
233
  $subset = ( is_array( $font ) ) ? end( $font ) : '';
234
  if ( false !== strpos( $subset, 'subset=' ) ) {
235
+ $subset = str_replace( array( '%2C', '%2c' ), ',', $subset );
236
  $subset = explode( 'subset=', $subset );
237
  $fonts_collection[ $i ]['subsets'] = explode( ',', $subset[1] );
238
  }
256
  foreach ( $fonts_collection as $font ) {
257
  $fonts_string .= '|' . trim( implode( '|', $font['fonts'] ), '|' );
258
  if ( ! empty( $font['subsets'] ) ) {
259
+ $subset_string .= ',' . trim( implode( ',', $font['subsets'] ), ',' );
260
  }
261
  }
262
 
263
  if ( ! empty( $subset_string ) ) {
264
+ $subset_string = str_replace( ',', '%2C', ltrim( $subset_string, ',' ) );
265
+ $fonts_string = $fonts_string . '&#038;subset=' . $subset_string;
266
  }
267
 
268
  $fonts_string = str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) );
491
  return false;
492
  } elseif ( strpos( $url, '.php' ) !== false ) {
493
  return false;
494
+ } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
495
  // fixme: better check against end of string.
496
  return false;
497
  } elseif ( ! empty( $nopti_images ) ) {
507
 
508
  private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
509
  {
510
+ // sanitize width and height.
511
+ if ( strpos( $width, '%' ) !== false ) {
512
+ $width = 0;
513
+ }
514
+ if ( strpos( $height, '%' ) !== false ) {
515
+ $height = 0;
516
+ }
517
+ $width = (int) $width;
518
+ $height = (int) $height;
519
+
520
  $filtered_url = apply_filters( 'autoptimize_filter_extra_imgopt_build_url', $orig_url, $width, $height );
521
 
522
  if ( $filtered_url !== $orig_url ) {
559
 
560
  private function normalize_img_urls( $in )
561
  {
562
+ static $cdn_domain = null;
563
+ if ( is_null( $cdn_domain ) ) {
564
+ $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
565
+ if ( ! empty( $cdn_url ) ) {
566
+ $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
567
+ } else {
568
+ $cdn_domain = '';
569
+ }
570
+ }
571
+
572
  $parsed_site_url = parse_url( site_url() );
573
 
574
  if ( strpos( $in, 'http' ) !== 0 && strpos( $in, '//' ) === 0 ) {
575
  $in = $parsed_site_url['scheme'] . ':' . $in;
576
  } elseif ( strpos( $in, '/' ) === 0 ) {
577
  $in = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $in;
578
+ } elseif ( ! empty( $cdn_domain ) && strpos( $in, $cdn_domain ) !== 0 ) {
579
+ $in = str_replace( $cdn_domain, $parsed_site_url['host'], $in );
580
  }
581
 
582
  return apply_filters( 'autoptimize_filter_extra_imgopt_normalized_url', $in );
603
  static $_img_q = null;
604
 
605
  if ( is_null( $_img_q ) ) {
606
+ if ( is_array( $this->options ) && array_key_exists( 'autoptimize_extra_select_field_6', $this->options ) ) {
607
+ $_setting = $this->options['autoptimize_extra_select_field_6'];
608
+ }
609
 
610
  if ( ! $_setting || empty( $_setting ) || ( '1' !== $_setting && '3' !== $_setting ) ) {
611
  // default image opt. value is 2 ("glossy").
657
  $_img_stat_resp = wp_remote_get( $_img_provider_stat_url );
658
  if ( ! is_wp_error( $_img_stat_resp ) ) {
659
  if ( '200' == wp_remote_retrieve_response_code( $_img_stat_resp ) ) {
660
+ $_img_provider_stat = json_decode( wp_remote_retrieve_body( $_img_stat_resp ), true );
661
+ $_img_provider_stat['timestamp'] = time();
662
  update_option( 'autoptimize_imgopt_provider_stat', $_img_provider_stat );
663
  }
664
  }
674
  $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
675
  $magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
676
  $has_launched = get_option( 'autoptimize_imgopt_launched', '' );
677
+ if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
678
  $launch_status = true;
679
  if ( ! $has_launched ) {
680
  update_option( 'autoptimize_imgopt_launched', 'on' );
721
  $_extra_options = $this->options;
722
  if ( ! empty( $_extra_options ) && is_array( $_extra_options ) && array_key_exists( 'autoptimize_extra_checkbox_field_5', $_extra_options ) && ! empty( $_extra_options['autoptimize_extra_checkbox_field_5'] ) ) {
723
  $_imgopt_notice = '';
724
+ $_stat = $this->get_imgopt_provider_userstatus();
725
  $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
726
  $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
727
 
729
  if ( 1 == $_stat['Status'] ) {
730
  // translators: "add more credits" will appear in a "a href".
731
  $_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>' );
732
+ } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
733
  // translators: "add more credits" will appear in a "a href".
734
+ $_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>' );
735
+ $_imgopt_stats_refresh_url = add_query_arg( array(
736
+ 'page' => 'autoptimize_extra',
737
+ 'refreshImgProvStats' => '1',
738
+ ), admin_url( 'options-general.php' ) );
739
+ if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
740
+ $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( get_option( 'time_format' ), $_stat['timestamp'] );
741
+ } else {
742
+ $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
743
+ }
744
+ $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
745
+ // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
746
+ $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
747
  } else {
748
  $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
749
  // translators: "log in to check your account" will appear in a "a href".
766
  return $self->get_imgopt_status_notice();
767
  }
768
 
769
+ public function get_imgopt_provider_userstatus() {
770
+ static $_provider_userstatus = null;
771
+
772
+ if ( is_null( $_provider_userstatus ) ) {
773
+ $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
774
+ if ( is_array( $_stat ) ) {
775
+ if ( array_key_exists( 'Status', $_stat ) ) {
776
+ $_provider_userstatus['Status'] = $_stat['Status'];
777
+ } else {
778
+ // if no stats then we assume all is well.
779
+ $_provider_userstatus['Status'] = 2;
780
+ }
781
+ if ( array_key_exists( 'timestamp', $_stat ) ) {
782
+ $_provider_userstatus['timestamp'] = $_stat['timestamp'];
783
+ } else {
784
+ // if no timestamp then we return "".
785
+ $_provider_userstatus['timestamp'] = '';
786
+ }
787
+ }
788
+ }
789
+
790
+ return $_provider_userstatus;
791
+ }
792
+
793
  public function admin_menu()
794
  {
795
  add_submenu_page( null, 'autoptimize_extra', 'autoptimize_extra', 'manage_options', 'autoptimize_extra', array( $this, 'options_page' ) );
805
 
806
  public function options_page()
807
  {
808
+ // Check querystring for "refreshCacheChecker" and call cachechecker if so.
809
+ if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
810
+ $this->query_img_provider_stats();
811
+ }
812
+
813
  // Working with actual option values from the database here.
814
  // That way any saves are still processed as expected, but we can still
815
  // override behavior by using `new autoptimizeExtra($custom_options)` and not have that custom
892
  case -1:
893
  $_notice_color = 'red';
894
  break;
895
+ case -2:
896
+ $_notice_color = 'red';
897
+ break;
898
  default:
899
  $_notice_color = 'green';
900
  }
903
  // translators: link points to shortpixel.
904
  $upsell_msg_1 = '<p>' . sprintf( __( 'Get more Google love and improve your website\'s loading speed by having the images optimized on the fly by %1$sShortPixel%2$s and then cached and served fast from a CDN.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
905
  if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
906
+ $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' );
907
  } else {
908
+ // translators: link points to shortpixel.
909
+ $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>' );
910
  }
911
  echo apply_filters( 'autoptimize_extra_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
912
  }
961
  <tr>
962
  <th scope="row"><?php _e( 'Preconnect to 3rd party domains <em>(advanced users)</em>', 'autoptimize' ); ?></th>
963
  <td>
964
+ <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_2]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_2', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_2'] ); } ?>'><br /><?php _e( 'Add 3rd party domains you want the browser to <a href="https://www.keycdn.com/support/preconnect/#primary" target="_blank">preconnect</a> to, separated by comma\'s. Make sure to include the correct protocol (HTTP or HTTPS).', 'autoptimize' ); ?></label>
965
  </td>
966
  </tr>
967
  <tr>
973
  printf( __( 'You have "Async JavaScript" installed, %1$sconfiguration of async javascript is best done there%2$s.', 'autoptimize' ), '<a href="' . 'options-general.php?page=async-javascript' . '">', '</a>' );
974
  } else {
975
  ?>
976
+ <input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_3]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_3', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_3'] ); } ?>'>
977
  <br />
978
  <?php
979
  _e( 'Comma-separated list of local or 3rd party JS-files that should loaded with the <code>async</code> flag. JS-files from your own site will be automatically excluded if added here. ', 'autoptimize' );
classes/autoptimizeMain.php CHANGED
@@ -351,7 +351,7 @@ class autoptimizeMain
351
 
352
  $has_no_html_tag = ( false === stripos( $content, '<html' ) );
353
  $has_xsl_stylesheet = ( false !== stripos( $content, '<xsl:stylesheet' ) );
354
- $has_html5_doctype = ( preg_match( '/^<!DOCTYPE.+html>/i', $content ) > 0 );
355
 
356
  if ( $has_no_html_tag ) {
357
  // Can't be valid amp markup without an html tag preceding it.
@@ -553,7 +553,7 @@ class autoptimizeMain
553
  $_ao_imgopt_active = true;
554
  }
555
 
556
- if ( '' !== $_ao_imgopt_plug_notice && ! $_ao_imgopt_active && $_ao_imgopt_launch_ok && PAnD::is_admin_notice_active( $_ao_imgopt_plug_dismissible ) ) {
557
  echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_imgopt_plug_dismissible . '"><p>';
558
  echo $_ao_imgopt_plug_notice;
559
  echo '</p></div>';
351
 
352
  $has_no_html_tag = ( false === stripos( $content, '<html' ) );
353
  $has_xsl_stylesheet = ( false !== stripos( $content, '<xsl:stylesheet' ) );
354
+ $has_html5_doctype = ( preg_match( '/^<!DOCTYPE.+html>/i', ltrim( $content ) ) > 0 );
355
 
356
  if ( $has_no_html_tag ) {
357
  // Can't be valid amp markup without an html tag preceding it.
553
  $_ao_imgopt_active = true;
554
  }
555
 
556
+ if ( current_user_can( 'manage_options' ) && '' !== $_ao_imgopt_plug_notice && ! $_ao_imgopt_active && $_ao_imgopt_launch_ok && PAnD::is_admin_notice_active( $_ao_imgopt_plug_dismissible ) ) {
557
  echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_imgopt_plug_dismissible . '"><p>';
558
  echo $_ao_imgopt_plug_notice;
559
  echo '</p></div>';
classes/autoptimizeScripts.php CHANGED
@@ -178,11 +178,6 @@ class autoptimizeScripts extends autoptimizeBase
178
  if ( ! empty( $minified_url ) ) {
179
  $newTag = str_replace( $url, $minified_url, $newTag );
180
  }
181
-
182
- // remove querystring from URL in newTag
183
- if ( ! empty( $explUrl[1] ) ) {
184
- $newTag = str_replace( '?' . $explUrl[1], '', $newTag );
185
- }
186
  }
187
 
188
  if ( $this->ismovable($newTag) ) {
178
  if ( ! empty( $minified_url ) ) {
179
  $newTag = str_replace( $url, $minified_url, $newTag );
180
  }
 
 
 
 
 
181
  }
182
 
183
  if ( $this->ismovable($newTag) ) {
classes/autoptimizeStyles.php CHANGED
@@ -212,11 +212,6 @@ class autoptimizeStyles extends autoptimizeBase
212
  $new_tag = $tag;
213
  }
214
 
215
- // Removes querystring from URL.
216
- if ( ! empty( $exploded_url[1] ) ) {
217
- $new_tag = str_replace( '?' . $exploded_url[1], '', $new_tag );
218
- }
219
-
220
  // Defer single CSS if "inline & defer" is ON and there is inline CSS.
221
  if ( $this->defer && ! empty( $this->defer_inline ) ) {
222
  // Get/ set (via filter) the JS to be triggers onload of the preloaded CSS.
212
  $new_tag = $tag;
213
  }
214
 
 
 
 
 
 
215
  // Defer single CSS if "inline & defer" is ON and there is inline CSS.
216
  if ( $this->defer && ! empty( $this->defer_inline ) ) {
217
  // Get/ set (via filter) the JS to be triggers onload of the preloaded CSS.
classes/external/php/jsmin.php CHANGED
@@ -196,7 +196,8 @@ class JSMin {
196
  // fallthrough intentional
197
  case self::ACTION_DELETE_A: // 2
198
  $this->a = $this->b;
199
- if ($this->a === "'" || $this->a === '"') { // string literal
 
200
  $str = $this->a; // in case needed for exception
201
  for(;;) {
202
  $this->output .= $this->a;
@@ -206,7 +207,9 @@ class JSMin {
206
  if ($this->a === $this->b) { // end quote
207
  break;
208
  }
209
- if ($this->isEOF($this->a)) {
 
 
210
  $byte = $this->inputIndex - 1;
211
  throw new JSMin_UnterminatedStringException(
212
  "JSMin: Unterminated String at byte {$byte}: {$str}");
@@ -216,7 +219,7 @@ class JSMin {
216
  $this->output .= $this->a;
217
  $this->lastByteOut = $this->a;
218
 
219
- $this->a = $this->get();
220
  $str .= $this->a;
221
  }
222
  }
196
  // fallthrough intentional
197
  case self::ACTION_DELETE_A: // 2
198
  $this->a = $this->b;
199
+ if ($this->a === "'" || $this->a === '"' || $this->a === '`') { // string/template literal
200
+ $delimiter = $this->a;
201
  $str = $this->a; // in case needed for exception
202
  for(;;) {
203
  $this->output .= $this->a;
207
  if ($this->a === $this->b) { // end quote
208
  break;
209
  }
210
+ if ($delimiter === '`' && $this->a === "\n") {
211
+ // leave the newline
212
+ } elseif ($this->isEOF($this->a)) {
213
  $byte = $this->inputIndex - 1;
214
  throw new JSMin_UnterminatedStringException(
215
  "JSMin: Unterminated String at byte {$byte}: {$str}");
219
  $this->output .= $this->a;
220
  $this->lastByteOut = $this->a;
221
 
222
+ $this->a = $this->get();
223
  $str .= $this->a;
224
  }
225
  }
classes/external/php/persist-admin-notices-dismissal/README.md CHANGED
@@ -42,7 +42,7 @@ function sample_admin_notice__success() {
42
  if ( ! PAnD::is_admin_notice_active( 'disable-done-notice-forever' ) ) {
43
  return;
44
  }
45
-
46
  ?>
47
  <div data-dismissible="disable-done-notice-forever" class="updated notice notice-success is-dismissible">
48
  <p><?php _e( 'Done!', 'sample-text-domain' ); ?></p>
@@ -61,7 +61,7 @@ Just add the following in your main plugin file.
61
  ```php
62
  add_action( 'admin_init', array( 'PAnD', 'init' ) );
63
  ```
64
-
65
  #### Usage Instructions and Examples
66
  If you have two notices displayed when certain actions are triggered; firstly, choose a string to uniquely identify them, e.g. `notice-one` and `notice-two`
67
 
@@ -104,5 +104,14 @@ add_action( 'admin_notices', 'sample_admin_notice__success1' );
104
  add_action( 'admin_notices', 'sample_admin_notice__success2' );
105
  ```
106
 
 
 
 
 
 
 
 
 
 
107
 
108
  Cool beans. Isn't it?
42
  if ( ! PAnD::is_admin_notice_active( 'disable-done-notice-forever' ) ) {
43
  return;
44
  }
45
+
46
  ?>
47
  <div data-dismissible="disable-done-notice-forever" class="updated notice notice-success is-dismissible">
48
  <p><?php _e( 'Done!', 'sample-text-domain' ); ?></p>
61
  ```php
62
  add_action( 'admin_init', array( 'PAnD', 'init' ) );
63
  ```
64
+
65
  #### Usage Instructions and Examples
66
  If you have two notices displayed when certain actions are triggered; firstly, choose a string to uniquely identify them, e.g. `notice-one` and `notice-two`
67
 
104
  add_action( 'admin_notices', 'sample_admin_notice__success2' );
105
  ```
106
 
107
+ You should be a good developer and add the following to your `uninstall.php` file so that we can clean up after ourselves and not leave unnecessary stuff in the options table.
108
+
109
+ ```php
110
+ global $wpdb;
111
+ $table = is_multisite() ? $wpdb->base_prefix . 'sitemeta' : $wpdb->base_prefix . 'options';
112
+ $column = is_multisite() ? 'meta_key' : 'option_name';
113
+ $delete_string = 'DELETE FROM ' . $table . ' WHERE ' . $column . ' LIKE %s LIMIT 1000';
114
+ $wpdb->query( $wpdb->prepare( $delete_string, array( '%pand-%' ) ) );
115
+ ```
116
 
117
  Cool beans. Isn't it?
classes/external/php/persist-admin-notices-dismissal/dismiss-notice.js CHANGED
@@ -1,29 +1,33 @@
1
  (function ($) {
2
- //shorthand for ready event.
3
- $(function () {
4
- $('div[data-dismissible] button.notice-dismiss').click(function (event) {
5
- event.preventDefault();
6
- var $this = $(this);
 
 
7
 
8
- var attr_value, option_name, dismissible_length, data;
9
 
10
- attr_value = $this.parent().attr('data-dismissible').split('-');
11
 
12
- // remove the dismissible length from the attribute value and rejoin the array.
13
- dismissible_length = attr_value.pop();
14
 
15
- option_name = attr_value.join('-');
16
 
17
- data = {
18
- 'action': 'dismiss_admin_notice',
19
- 'option_name': option_name,
20
- 'dismissible_length': dismissible_length,
21
- 'nonce': dismissible_notice.nonce
22
- };
23
 
24
- // We can also pass the url value separately from ajaxurl for front end AJAX implementations
25
- $.post(ajaxurl, data);
26
- });
27
- })
 
 
28
 
29
  }(jQuery));
1
  (function ($) {
2
+ //shorthand for ready event.
3
+ $(
4
+ function () {
5
+ $( 'div[data-dismissible] button.notice-dismiss' ).click(
6
+ function (event) {
7
+ event.preventDefault();
8
+ var $this = $( this );
9
 
10
+ var attr_value, option_name, dismissible_length, data;
11
 
12
+ attr_value = $this.parent().attr( 'data-dismissible' ).split( '-' );
13
 
14
+ // remove the dismissible length from the attribute value and rejoin the array.
15
+ dismissible_length = attr_value.pop();
16
 
17
+ option_name = attr_value.join( '-' );
18
 
19
+ data = {
20
+ 'action': 'dismiss_admin_notice',
21
+ 'option_name': option_name,
22
+ 'dismissible_length': dismissible_length,
23
+ 'nonce': dismissible_notice.nonce
24
+ };
25
 
26
+ // We can also pass the url value separately from ajaxurl for front end AJAX implementations
27
+ $.post( ajaxurl, data );
28
+ }
29
+ );
30
+ }
31
+ )
32
 
33
  }(jQuery));
classes/external/php/persist-admin-notices-dismissal/persist-admin-notices-dismissal.php CHANGED
@@ -3,7 +3,7 @@
3
  /**
4
  * Persist Admin notices Dismissal
5
  *
6
- * Copyright (C) 2016 Agbonghama Collins <http://w3guy.com>
7
  *
8
  * This program is free software: you can redistribute it and/or modify
9
  * it under the terms of the GNU General Public License as published by
@@ -19,10 +19,10 @@
19
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20
  *
21
  * @package Persist Admin notices Dismissal
22
- * @author Agbonghama Collins
23
  * @author Andy Fragen
24
  * @license http://www.gnu.org/licenses GNU General Public License
25
- * @version 1.3.2
26
  */
27
 
28
  /**
@@ -52,7 +52,9 @@ if ( ! class_exists( 'PAnD' ) ) {
52
  */
53
  public static function load_script() {
54
 
55
- if(is_customize_preview()) return;
 
 
56
 
57
  wp_enqueue_script(
58
  'dismissible-notices',
@@ -78,17 +80,15 @@ if ( ! class_exists( 'PAnD' ) ) {
78
  public static function dismiss_admin_notice() {
79
  $option_name = sanitize_text_field( $_POST['option_name'] );
80
  $dismissible_length = sanitize_text_field( $_POST['dismissible_length'] );
81
- $transient = 0;
82
 
83
  if ( 'forever' != $dismissible_length ) {
84
  // If $dismissible_length is not an integer default to 1
85
  $dismissible_length = ( 0 == absint( $dismissible_length ) ) ? 1 : $dismissible_length;
86
- $transient = absint( $dismissible_length ) * DAY_IN_SECONDS;
87
  $dismissible_length = strtotime( absint( $dismissible_length ) . ' days' );
88
  }
89
 
90
  check_ajax_referer( 'dismissible-notice', 'nonce' );
91
- set_site_transient( $option_name, $dismissible_length, $transient );
92
  wp_die();
93
  }
94
 
@@ -103,8 +103,7 @@ if ( ! class_exists( 'PAnD' ) ) {
103
  $array = explode( '-', $arg );
104
  $length = array_pop( $array );
105
  $option_name = implode( '-', $array );
106
- $db_record = get_site_transient( $option_name );
107
-
108
  if ( 'forever' == $db_record ) {
109
  return false;
110
  } elseif ( absint( $db_record ) >= time() ) {
@@ -114,6 +113,47 @@ if ( ! class_exists( 'PAnD' ) ) {
114
  }
115
  }
116
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
 
119
  }
3
  /**
4
  * Persist Admin notices Dismissal
5
  *
6
+ * Copyright (C) 2016 Collins Agbonghama <http://w3guy.com>
7
  *
8
  * This program is free software: you can redistribute it and/or modify
9
  * it under the terms of the GNU General Public License as published by
19
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20
  *
21
  * @package Persist Admin notices Dismissal
22
+ * @author Collins Agbonghama
23
  * @author Andy Fragen
24
  * @license http://www.gnu.org/licenses GNU General Public License
25
+ * @version 1.4.1
26
  */
27
 
28
  /**
52
  */
53
  public static function load_script() {
54
 
55
+ if ( is_customize_preview() ) {
56
+ return;
57
+ }
58
 
59
  wp_enqueue_script(
60
  'dismissible-notices',
80
  public static function dismiss_admin_notice() {
81
  $option_name = sanitize_text_field( $_POST['option_name'] );
82
  $dismissible_length = sanitize_text_field( $_POST['dismissible_length'] );
 
83
 
84
  if ( 'forever' != $dismissible_length ) {
85
  // If $dismissible_length is not an integer default to 1
86
  $dismissible_length = ( 0 == absint( $dismissible_length ) ) ? 1 : $dismissible_length;
 
87
  $dismissible_length = strtotime( absint( $dismissible_length ) . ' days' );
88
  }
89
 
90
  check_ajax_referer( 'dismissible-notice', 'nonce' );
91
+ self::set_admin_notice_cache( $option_name, $dismissible_length );
92
  wp_die();
93
  }
94
 
103
  $array = explode( '-', $arg );
104
  $length = array_pop( $array );
105
  $option_name = implode( '-', $array );
106
+ $db_record = self::get_admin_notice_cache( $option_name );
 
107
  if ( 'forever' == $db_record ) {
108
  return false;
109
  } elseif ( absint( $db_record ) >= time() ) {
113
  }
114
  }
115
 
116
+ /**
117
+ * Returns admin notice cached timeout.
118
+ *
119
+ * @access public
120
+ *
121
+ * @param string|bool $id admin notice name or false.
122
+ *
123
+ * @return array|bool The timeout. False if expired.
124
+ */
125
+ public static function get_admin_notice_cache( $id = false ) {
126
+ if ( ! $id ) {
127
+ return false;
128
+ }
129
+ $cache_key = 'pand-' . md5( $id );
130
+ $timeout = get_site_option( $cache_key );
131
+ $timeout = 'forever' === $timeout ? time() + 60 : $timeout;
132
+
133
+ if ( empty( $timeout ) || time() > $timeout ) {
134
+ return false;
135
+ }
136
+
137
+ return $timeout;
138
+ }
139
+
140
+ /**
141
+ * Sets admin notice timeout in site option.
142
+ *
143
+ * @access public
144
+ *
145
+ * @param string $id Data Identifier.
146
+ * @param string|bool $timeout Timeout for admin notice.
147
+ *
148
+ * @return bool
149
+ */
150
+ public static function set_admin_notice_cache( $id, $timeout ) {
151
+ $cache_key = 'pand-' . md5( $id );
152
+ update_site_option( $cache_key, $timeout );
153
+
154
+ return true;
155
+ }
156
+
157
  }
158
 
159
  }
readme.txt CHANGED
@@ -3,9 +3,9 @@ Contributors: futtta, optimizingmatters, zytzagoo, turl
3
  Tags: optimize, minify, performance, pagespeed, image optimization
4
  Donate link: http://blog.futtta.be/2013/10/21/do-not-donate-to-me/
5
  Requires at least: 4.0
6
- Tested up to: 4.9
7
  Requires PHP: 5.3
8
- Stable tag: 2.4.1
9
 
10
  Autoptimize speeds up your website by optimizing JS, CSS, HTML, Google Fonts and images, async-ing JS, removing emoji cruft and more.
11
 
@@ -283,6 +283,14 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
283
 
284
  == Changelog ==
285
 
 
 
 
 
 
 
 
 
286
  = 2.4.1 =
287
  * bugfix for CSS minifier throwing a HTTP 500 error when a `}` is missing.
288
  * bugfix for slowness when iconv was used for multibyte string replacements (ditched iconv).
3
  Tags: optimize, minify, performance, pagespeed, image optimization
4
  Donate link: http://blog.futtta.be/2013/10/21/do-not-donate-to-me/
5
  Requires at least: 4.0
6
+ Tested up to: 5.0
7
  Requires PHP: 5.3
8
+ Stable tag: 2.4.2
9
 
10
  Autoptimize speeds up your website by optimizing JS, CSS, HTML, Google Fonts and images, async-ing JS, removing emoji cruft and more.
11
 
283
 
284
  == Changelog ==
285
 
286
+ = 2.4.2 =
287
+ * misc. improvements to image optimization logic
288
+ * update PAnD framework to latest version to fix issues with notices not staying dismissed
289
+ * patched JS minifying component to not break template literals as reported by Alex Kozack
290
+ * bugfix for Google fonts subset concatenation by Rocco Aliberti, thanks!
291
+ * bugfix not to remove querystrings if that option is not on in "Extra", kudo's to Diego Versiani for pointing out the inconsistency
292
+ * tested and confirmed working with WordPress 5.0 (beta 5)
293
+
294
  = 2.4.1 =
295
  * bugfix for CSS minifier throwing a HTTP 500 error when a `}` is missing.
296
  * bugfix for slowness when iconv was used for multibyte string replacements (ditched iconv).