Autoptimize - Version 2.5.0

Version Description

  • moved image optimization to a separate tab and move all code to a separate file.
  • added lazyloading (using lazysizes)
  • added webp support (requires image optimization and lazyloading to be active)
  • added option to enable/ disable the minification of excluded JS/ CSS files (on by default)
  • misc. bugfixes and smaller improvements
Download this release

Release Info

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

Code changes from version 2.4.4 to 2.5.0

autoptimize.php CHANGED
@@ -2,8 +2,8 @@
2
  /*
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.4
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.4' );
24
 
25
  // plugin_dir_path() returns the trailing slash!
26
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
2
  /*
3
  Plugin Name: Autoptimize
4
  Plugin URI: https://autoptimize.com/
5
+ Description: Makes your site faster by optimizing CSS, JS, Images, Google fonts and more.
6
+ Version: 2.5.0
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.5.0' );
24
 
25
  // plugin_dir_path() returns the trailing slash!
26
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
classes/autoptimizeBase.php CHANGED
@@ -419,7 +419,7 @@ abstract class autoptimizeBase
419
  // Grab the parts we need.
420
  $parts = explode( '|', $matches[1] );
421
  if ( ! empty( $parts ) ) {
422
- $filepath = isset( $parts[0] ) ? base64_decode($parts[0]) : null;
423
  $filehash = isset( $parts[1] ) ? $parts[1] : null;
424
  }
425
 
@@ -559,7 +559,7 @@ abstract class autoptimizeBase
559
  *
560
  * @return string
561
  */
562
- protected function replace_contents_with_marker_if_exists( $marker, $search, $re_replace_pattern, $content )
563
  {
564
  $found = false;
565
 
@@ -591,7 +591,7 @@ abstract class autoptimizeBase
591
  *
592
  * @return string
593
  */
594
- protected function restore_marked_content( $marker, $content )
595
  {
596
  if ( false !== strpos( $content, $marker ) ) {
597
  $content = preg_replace_callback(
419
  // Grab the parts we need.
420
  $parts = explode( '|', $matches[1] );
421
  if ( ! empty( $parts ) ) {
422
+ $filepath = isset( $parts[0] ) ? base64_decode( $parts[0] ) : null;
423
  $filehash = isset( $parts[1] ) ? $parts[1] : null;
424
  }
425
 
559
  *
560
  * @return string
561
  */
562
+ public static function replace_contents_with_marker_if_exists( $marker, $search, $re_replace_pattern, $content )
563
  {
564
  $found = false;
565
 
591
  *
592
  * @return string
593
  */
594
+ public static function restore_marked_content( $marker, $content )
595
  {
596
  if ( false !== strpos( $content, $marker ) ) {
597
  $content = preg_replace_callback(
classes/autoptimizeCache.php CHANGED
@@ -90,6 +90,14 @@ class autoptimizeCache
90
  */
91
  public function cache( $data, $mime )
92
  {
 
 
 
 
 
 
 
 
93
  if ( false === $this->nogzip ) {
94
  // We handle gzipping ourselves.
95
  $file = 'default.php';
@@ -104,6 +112,10 @@ class autoptimizeCache
104
  if ( apply_filters( 'autoptimize_filter_cache_create_static_gzip', false ) ) {
105
  // Create an additional cached gzip file.
106
  file_put_contents( $this->cachedir . $this->filename . '.gz', gzencode( $data, 9, FORCE_GZIP ) );
 
 
 
 
107
  }
108
  }
109
  }
@@ -363,7 +375,8 @@ class autoptimizeCache
363
  }
364
 
365
  // Warm cache (part of speedupper)!
366
- if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
 
367
  $url = site_url() . '/?ao_speedup_cachebuster=' . rand( 1, 100000 );
368
  $cache = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
369
  unset( $cache );
@@ -481,17 +494,10 @@ class autoptimizeCache
481
  */
482
  public static function cacheavail()
483
  {
484
- if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
485
- // We didn't set a cache.
486
  return false;
487
  }
488
 
489
- foreach ( array( '', 'js', 'css' ) as $dir ) {
490
- if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
491
- return false;
492
- }
493
- }
494
-
495
  // Using .htaccess inside our cache folder to overrule wp-super-cache.
496
  $htaccess = AUTOPTIMIZE_CACHE_DIR . '/.htaccess';
497
  if ( ! is_file( $htaccess ) ) {
@@ -563,6 +569,26 @@ class autoptimizeCache
563
  return true;
564
  }
565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
  /**
567
  * Ensures the specified `$dir` exists and is writeable.
568
  * Returns false if that's not the case.
90
  */
91
  public function cache( $data, $mime )
92
  {
93
+ // off by default; check if cachedirs exist every time before caching
94
+ //
95
+ // to be activated for users that experience these ugly errors;
96
+ // PHP Warning: file_put_contents failed to open stream: No such file or directory.
97
+ if ( apply_filters( 'autoptimize_filter_cache_checkdirs_on_write', false ) ) {
98
+ $this->check_and_create_dirs();
99
+ }
100
+
101
  if ( false === $this->nogzip ) {
102
  // We handle gzipping ourselves.
103
  $file = 'default.php';
112
  if ( apply_filters( 'autoptimize_filter_cache_create_static_gzip', false ) ) {
113
  // Create an additional cached gzip file.
114
  file_put_contents( $this->cachedir . $this->filename . '.gz', gzencode( $data, 9, FORCE_GZIP ) );
115
+ // If PHP Brotli extension is installed, create an additional cached Brotli file.
116
+ if ( function_exists( 'brotli_compress' ) ) {
117
+ file_put_contents( $this->cachedir . $this->filename . '.br', brotli_compress( $data, 11, BROTLI_GENERIC ) );
118
+ }
119
  }
120
  }
121
  }
375
  }
376
 
377
  // Warm cache (part of speedupper)!
378
+ if ( apply_filters( 'autoptimize_filter_speedupper', true ) && false == get_transient( 'autoptimize_cache_warmer_protector' ) ) {
379
+ set_transient( 'autoptimize_cache_warmer_protector', 'I shall not warm cache for another 10 minutes.', 60 * 10 );
380
  $url = site_url() . '/?ao_speedup_cachebuster=' . rand( 1, 100000 );
381
  $cache = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
382
  unset( $cache );
494
  */
495
  public static function cacheavail()
496
  {
497
+ if ( false === autoptimizeCache::check_and_create_dirs() ) {
 
498
  return false;
499
  }
500
 
 
 
 
 
 
 
501
  // Using .htaccess inside our cache folder to overrule wp-super-cache.
502
  $htaccess = AUTOPTIMIZE_CACHE_DIR . '/.htaccess';
503
  if ( ! is_file( $htaccess ) ) {
569
  return true;
570
  }
571
 
572
+ /**
573
+ * Checks if cache dirs exist and create if not.
574
+ * Returns false if not succesful.
575
+ *
576
+ * @return bool
577
+ */
578
+ public static function check_and_create_dirs() {
579
+ if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
580
+ // We didn't set a cache.
581
+ return false;
582
+ }
583
+
584
+ foreach ( array( '', 'js', 'css' ) as $dir ) {
585
+ if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
586
+ return false;
587
+ }
588
+ }
589
+ return true;
590
+ }
591
+
592
  /**
593
  * Ensures the specified `$dir` exists and is writeable.
594
  * Returns false if that's not the case.
classes/autoptimizeCacheChecker.php CHANGED
@@ -60,11 +60,11 @@ class autoptimizeCacheChecker
60
  if ( ( $cache_size > $max_size ) && ( $do_cache_check ) ) {
61
  update_option( 'autoptimize_cachesize_notice', true );
62
  if ( apply_filters( 'autoptimize_filter_cachecheck_sendmail', true ) ) {
63
- $site_url = esc_url( site_url() );
64
  $ao_mailto = apply_filters( 'autoptimize_filter_cachecheck_mailto', get_option( 'admin_email', '' ) );
65
 
66
- $ao_mailsubject = __( 'Autoptimize cache size warning', 'autoptimize' ) . ' (' . $site_url . ')';
67
- $ao_mailbody = __( 'Autoptimize\'s cache size is getting big, consider purging the cache. Have a look at https://wordpress.org/plugins/autoptimize/faq/ to see how you can keep the cache size under control.', 'autoptimize' ) . ' (site: ' . $site_url . ')';
68
 
69
  if ( ! empty( $ao_mailto ) ) {
70
  $ao_mailresult = wp_mail( $ao_mailto, $ao_mailsubject, $ao_mailbody );
@@ -78,11 +78,11 @@ class autoptimizeCacheChecker
78
  // Check if 3rd party services (e.g. image proxy) are up.
79
  autoptimizeUtils::check_service_availability();
80
 
81
- // Check image optimization stats.
82
- autoptimizeExtra::get_img_provider_stats();
83
-
84
  // Nukes advanced cache clearing artifacts if they exists...
85
  autoptimizeCache::delete_advanced_cache_clear_artifacts();
 
 
 
86
  }
87
 
88
  public function show_admin_notice()
@@ -95,7 +95,7 @@ class autoptimizeCacheChecker
95
  }
96
 
97
  // Notice for image proxy usage.
98
- $_imgopt_notice = autoptimizeExtra::get_imgopt_status_notice_wrapper();
99
  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 ) ) ) {
100
  $_dismissible = 'ao-img-opt-notice-';
101
  $_hide_notice = '7';
60
  if ( ( $cache_size > $max_size ) && ( $do_cache_check ) ) {
61
  update_option( 'autoptimize_cachesize_notice', true );
62
  if ( apply_filters( 'autoptimize_filter_cachecheck_sendmail', true ) ) {
63
+ $home_url = esc_url( home_url() );
64
  $ao_mailto = apply_filters( 'autoptimize_filter_cachecheck_mailto', get_option( 'admin_email', '' ) );
65
 
66
+ $ao_mailsubject = __( 'Autoptimize cache size warning', 'autoptimize' ) . ' (' . $home_url . ')';
67
+ $ao_mailbody = __( 'Autoptimize\'s cache size is getting big, consider purging the cache. Have a look at https://wordpress.org/plugins/autoptimize/faq/ to see how you can keep the cache size under control.', 'autoptimize' ) . ' (site: ' . $home_url . ')';
68
 
69
  if ( ! empty( $ao_mailto ) ) {
70
  $ao_mailresult = wp_mail( $ao_mailto, $ao_mailsubject, $ao_mailbody );
78
  // Check if 3rd party services (e.g. image proxy) are up.
79
  autoptimizeUtils::check_service_availability();
80
 
 
 
 
81
  // Nukes advanced cache clearing artifacts if they exists...
82
  autoptimizeCache::delete_advanced_cache_clear_artifacts();
83
+
84
+ // Check image optimization stats.
85
+ autoptimizeImages::instance()->query_img_provider_stats();
86
  }
87
 
88
  public function show_admin_notice()
95
  }
96
 
97
  // Notice for image proxy usage.
98
+ $_imgopt_notice = autoptimizeImages::instance()->get_status_notice();
99
  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 ) ) ) {
100
  $_dismissible = 'ao-img-opt-notice-';
101
  $_hide_notice = '7';
classes/autoptimizeConfig.php CHANGED
@@ -209,21 +209,6 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
209
 
210
  <ul>
211
 
212
- <li class="itemDetail">
213
- <h2 class="itemTitle"><?php _e('HTML Options','autoptimize'); ?></h2>
214
- <table class="form-table">
215
- <tr valign="top">
216
- <th scope="row"><?php _e('Optimize HTML Code?','autoptimize'); ?></th>
217
- <td><input type="checkbox" id="autoptimize_html" name="autoptimize_html" <?php echo get_option('autoptimize_html')?'checked="checked" ':''; ?>/></td>
218
- </tr>
219
- <tr class="<?php echo $hiddenClass;?>html_sub ao_adv" valign="top">
220
- <th scope="row"><?php _e('Keep HTML comments?','autoptimize'); ?></th>
221
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_html_keepcomments" <?php echo get_option('autoptimize_html_keepcomments')?'checked="checked" ':''; ?>/>
222
- <?php _e('Enable this if you want HTML comments to remain in the page.','autoptimize'); ?></label></td>
223
- </tr>
224
- </table>
225
- </li>
226
-
227
  <li class="itemDetail">
228
  <h2 class="itemTitle"><?php _e('JavaScript Options','autoptimize'); ?></h2>
229
  <table class="form-table">
@@ -253,9 +238,9 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
253
  <?php _e('Mostly useful in combination with previous option when using jQuery-based templates, but might help keeping cache size under control.','autoptimize'); ?></label></td>
254
  </tr>
255
  <?php } ?>
256
- <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
257
  <th scope="row"><?php _e('Exclude scripts from Autoptimize:','autoptimize'); ?></th>
258
- <td><label><input type="text" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo get_option('autoptimize_js_exclude',"seal.js, js/jquery/jquery.js"); ?>"/><br />
259
  <?php _e('A comma-separated list of scripts you want to exclude from being optimized, for example \'whatever.js, another.js\' (without the quotes) to exclude those scripts from being aggregated by Autoptimize.','autoptimize'); ?></label></td>
260
  </tr>
261
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
@@ -317,7 +302,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
317
  <td><label class="cb_label"><input type="checkbox" id="autoptimize_css_inline" name="autoptimize_css_inline" <?php echo get_option('autoptimize_css_inline')?'checked="checked" ':''; ?>/>
318
  <?php _e('Inlining all CSS can improve performance for sites with a low pageviews/ visitor-rate, but may slow down performance otherwise.','autoptimize'); ?></label></td>
319
  </tr>
320
- <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv css_sub css_aggregate">
321
  <th scope="row"><?php _e('Exclude CSS from Autoptimize:','autoptimize'); ?></th>
322
  <td><label><input type="text" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo get_option('autoptimize_css_exclude','wp-content/cache/, wp-content/uploads/, admin-bar.min.css, dashicons.min.css'); ?>"/><br />
323
  <?php _e('A comma-separated list of CSS you want to exclude from being optimized.','autoptimize'); ?></label></td>
@@ -325,6 +310,21 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
325
  </table>
326
  </li>
327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  <li class="itemDetail">
329
  <h2 class="itemTitle"><?php _e('CDN Options','autoptimize'); ?></h2>
330
  <table class="form-table">
@@ -372,6 +372,17 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
372
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_nogzip" <?php echo get_option('autoptimize_cache_nogzip','1')?'checked="checked" ':''; ?>/>
373
  <?php _e('By default files saved are static css/js, uncheck this option if your webserver doesn\'t properly handle the compression and expiry.','autoptimize'); ?></label></td>
374
  </tr>
 
 
 
 
 
 
 
 
 
 
 
375
  <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
376
  <th scope="row"><?php _e('Also optimize for logged in users?','autoptimize'); ?></th>
377
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_logged" <?php echo get_option('autoptimize_optimize_logged','1')?'checked="checked" ':''; ?>/>
@@ -517,8 +528,12 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
517
  jQuery( "#autoptimize_js_aggregate" ).change(function() {
518
  if (this.checked && jQuery("#autoptimize_js").attr('checked')) {
519
  jQuery(".js_aggregate:visible").fadeTo("fast",1);
 
520
  } else {
521
  jQuery(".js_aggregate:visible").fadeTo("fast",.33);
 
 
 
522
  }
523
  });
524
 
@@ -533,8 +548,12 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
533
  jQuery( "#autoptimize_css_aggregate" ).change(function() {
534
  if (this.checked && jQuery("#autoptimize_css").attr('checked')) {
535
  jQuery(".css_aggregate:visible").fadeTo("fast",1);
 
536
  } else {
537
  jQuery(".css_aggregate:visible").fadeTo("fast",.33);
 
 
 
538
  }
539
  });
540
 
@@ -649,6 +668,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
649
  register_setting( 'autoptimize', 'autoptimize_show_adv' );
650
  register_setting( 'autoptimize', 'autoptimize_optimize_logged' );
651
  register_setting( 'autoptimize', 'autoptimize_optimize_checkout' );
 
652
  }
653
 
654
  public function setmeta($links, $file = null)
@@ -686,7 +706,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
686
  'autoptimize_html_keepcomments' => 0,
687
  'autoptimize_js' => 0,
688
  'autoptimize_js_aggregate' => 1,
689
- 'autoptimize_js_exclude' => 'seal.js, js/jquery/jquery.js',
690
  'autoptimize_js_trycatch' => 0,
691
  'autoptimize_js_justhead' => 0,
692
  'autoptimize_js_include_inline' => 0,
@@ -704,7 +724,8 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
704
  'autoptimize_cache_nogzip' => 1,
705
  'autoptimize_show_adv' => 0,
706
  'autoptimize_optimize_logged' => 1,
707
- 'autoptimize_optimize_checkout' => 1
 
708
  );
709
 
710
  return $config;
@@ -723,13 +744,28 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
723
  'autoptimize_extra_radio_field_4' => '1',
724
  'autoptimize_extra_text_field_2' => '',
725
  'autoptimize_extra_text_field_3' => '',
726
- 'autoptimize_extra_checkbox_field_5' => '0',
727
- 'autoptimize_extra_select_field_6' => '2',
728
  );
729
 
730
  return $defaults;
731
  }
732
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
733
  /**
734
  * Returns preload polyfill JS.
735
  *
@@ -809,7 +845,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
809
  // based on http://wordpress.stackexchange.com/a/58826
810
  static function ao_admin_tabs()
811
  {
812
- $tabs = apply_filters( 'autoptimize_filter_settingsscreen_tabs' ,array( 'autoptimize' => __( 'Main', 'autoptimize' ) ) );
813
  $tabContent = '';
814
  $tabs_count = count($tabs);
815
  if ( $tabs_count > 1 ) {
209
 
210
  <ul>
211
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  <li class="itemDetail">
213
  <h2 class="itemTitle"><?php _e('JavaScript Options','autoptimize'); ?></h2>
214
  <table class="form-table">
238
  <?php _e('Mostly useful in combination with previous option when using jQuery-based templates, but might help keeping cache size under control.','autoptimize'); ?></label></td>
239
  </tr>
240
  <?php } ?>
241
+ <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
242
  <th scope="row"><?php _e('Exclude scripts from Autoptimize:','autoptimize'); ?></th>
243
+ <td><label><input type="text" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo get_option('autoptimize_js_exclude',"wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.js"); ?>"/><br />
244
  <?php _e('A comma-separated list of scripts you want to exclude from being optimized, for example \'whatever.js, another.js\' (without the quotes) to exclude those scripts from being aggregated by Autoptimize.','autoptimize'); ?></label></td>
245
  </tr>
246
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
302
  <td><label class="cb_label"><input type="checkbox" id="autoptimize_css_inline" name="autoptimize_css_inline" <?php echo get_option('autoptimize_css_inline')?'checked="checked" ':''; ?>/>
303
  <?php _e('Inlining all CSS can improve performance for sites with a low pageviews/ visitor-rate, but may slow down performance otherwise.','autoptimize'); ?></label></td>
304
  </tr>
305
+ <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv css_sub">
306
  <th scope="row"><?php _e('Exclude CSS from Autoptimize:','autoptimize'); ?></th>
307
  <td><label><input type="text" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo get_option('autoptimize_css_exclude','wp-content/cache/, wp-content/uploads/, admin-bar.min.css, dashicons.min.css'); ?>"/><br />
308
  <?php _e('A comma-separated list of CSS you want to exclude from being optimized.','autoptimize'); ?></label></td>
310
  </table>
311
  </li>
312
 
313
+ <li class="itemDetail">
314
+ <h2 class="itemTitle"><?php _e('HTML Options','autoptimize'); ?></h2>
315
+ <table class="form-table">
316
+ <tr valign="top">
317
+ <th scope="row"><?php _e('Optimize HTML Code?','autoptimize'); ?></th>
318
+ <td><input type="checkbox" id="autoptimize_html" name="autoptimize_html" <?php echo get_option('autoptimize_html')?'checked="checked" ':''; ?>/></td>
319
+ </tr>
320
+ <tr class="<?php echo $hiddenClass;?>html_sub ao_adv" valign="top">
321
+ <th scope="row"><?php _e('Keep HTML comments?','autoptimize'); ?></th>
322
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_html_keepcomments" <?php echo get_option('autoptimize_html_keepcomments')?'checked="checked" ':''; ?>/>
323
+ <?php _e('Enable this if you want HTML comments to remain in the page.','autoptimize'); ?></label></td>
324
+ </tr>
325
+ </table>
326
+ </li>
327
+
328
  <li class="itemDetail">
329
  <h2 class="itemTitle"><?php _e('CDN Options','autoptimize'); ?></h2>
330
  <table class="form-table">
372
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_nogzip" <?php echo get_option('autoptimize_cache_nogzip','1')?'checked="checked" ':''; ?>/>
373
  <?php _e('By default files saved are static css/js, uncheck this option if your webserver doesn\'t properly handle the compression and expiry.','autoptimize'); ?></label></td>
374
  </tr>
375
+ <?php
376
+ $_min_excl_class = 'ao_adv';
377
+ if ( !$conf->get( 'autoptimize_css_aggregate' ) && !$conf->get( 'autoptimize_js_aggregate' ) ) {
378
+ $_min_excl_class = ' hidden';
379
+ }
380
+ ?>
381
+ <tr valign="top" id="min_excl_row" class="<?php echo $hiddenClass.$_min_excl_class; ?>">
382
+ <th scope="row"><?php _e('Minify excluded CSS and JS files?','autoptimize'); ?></th>
383
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_minify_excluded" <?php echo get_option('autoptimize_minify_excluded','1')?'checked="checked" ':''; ?>/>
384
+ <?php _e('When aggregating JS or CSS, excluded files that are not minified (based on filename) are by default minified by Autoptimize despite being excluded. Uncheck this option if anything breaks despite excluding.','autoptimize'); ?></label></td>
385
+ </tr>
386
  <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
387
  <th scope="row"><?php _e('Also optimize for logged in users?','autoptimize'); ?></th>
388
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_logged" <?php echo get_option('autoptimize_optimize_logged','1')?'checked="checked" ':''; ?>/>
528
  jQuery( "#autoptimize_js_aggregate" ).change(function() {
529
  if (this.checked && jQuery("#autoptimize_js").attr('checked')) {
530
  jQuery(".js_aggregate:visible").fadeTo("fast",1);
531
+ jQuery( "#min_excl_row" ).show();
532
  } else {
533
  jQuery(".js_aggregate:visible").fadeTo("fast",.33);
534
+ if ( jQuery( "#autoptimize_css_aggregate" ).prop('checked') == false ) {
535
+ jQuery( "#min_excl_row" ).hide();
536
+ }
537
  }
538
  });
539
 
548
  jQuery( "#autoptimize_css_aggregate" ).change(function() {
549
  if (this.checked && jQuery("#autoptimize_css").attr('checked')) {
550
  jQuery(".css_aggregate:visible").fadeTo("fast",1);
551
+ jQuery( "#min_excl_row" ).show();
552
  } else {
553
  jQuery(".css_aggregate:visible").fadeTo("fast",.33);
554
+ if ( jQuery( "#autoptimize_js_aggregate" ).prop('checked') == false ) {
555
+ jQuery( "#min_excl_row" ).hide();
556
+ }
557
  }
558
  });
559
 
668
  register_setting( 'autoptimize', 'autoptimize_show_adv' );
669
  register_setting( 'autoptimize', 'autoptimize_optimize_logged' );
670
  register_setting( 'autoptimize', 'autoptimize_optimize_checkout' );
671
+ register_setting( 'autoptimize', 'autoptimize_minify_excluded' );
672
  }
673
 
674
  public function setmeta($links, $file = null)
706
  'autoptimize_html_keepcomments' => 0,
707
  'autoptimize_js' => 0,
708
  'autoptimize_js_aggregate' => 1,
709
+ 'autoptimize_js_exclude' => 'wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.js',
710
  'autoptimize_js_trycatch' => 0,
711
  'autoptimize_js_justhead' => 0,
712
  'autoptimize_js_include_inline' => 0,
724
  'autoptimize_cache_nogzip' => 1,
725
  'autoptimize_show_adv' => 0,
726
  'autoptimize_optimize_logged' => 1,
727
+ 'autoptimize_optimize_checkout' => 1,
728
+ 'autoptimize_minify_excluded' => 1,
729
  );
730
 
731
  return $config;
744
  'autoptimize_extra_radio_field_4' => '1',
745
  'autoptimize_extra_text_field_2' => '',
746
  'autoptimize_extra_text_field_3' => '',
 
 
747
  );
748
 
749
  return $defaults;
750
  }
751
 
752
+ /**
753
+ * Returns default option values for autoptimizeExtra.
754
+ *
755
+ * @return array
756
+ */
757
+ public static function get_ao_imgopt_default_options()
758
+ {
759
+ $defaults = array(
760
+ 'autoptimize_imgopt_checkbox_field_1' => '0', // imgopt off.
761
+ 'autoptimize_imgopt_select_field_2' => '2', // quality glossy.
762
+ 'autoptimize_imgopt_checkbox_field_3' => '0', // lazy load off.
763
+ 'autoptimize_imgopt_checkbox_field_4' => '0', // webp off (might be removed).
764
+ 'autoptimize_imgopt_text_field_5' => '', // lazy load exclusions empty.
765
+ );
766
+ return $defaults;
767
+ }
768
+
769
  /**
770
  * Returns preload polyfill JS.
771
  *
845
  // based on http://wordpress.stackexchange.com/a/58826
846
  static function ao_admin_tabs()
847
  {
848
+ $tabs = apply_filters( 'autoptimize_filter_settingsscreen_tabs' ,array( 'autoptimize' => __( 'JS, CSS &amp; HTML', 'autoptimize' ) ) );
849
  $tabContent = '';
850
  $tabs_count = count($tabs);
851
  if ( $tabs_count > 1 ) {
classes/autoptimizeExtra.php CHANGED
@@ -24,7 +24,7 @@ class autoptimizeExtra
24
  public function __construct( $options = array() )
25
  {
26
  if ( empty( $options ) ) {
27
- $options = $this->fetch_options();
28
  }
29
 
30
  $this->options = $options;
@@ -40,7 +40,7 @@ class autoptimizeExtra
40
  }
41
  }
42
 
43
- protected function fetch_options()
44
  {
45
  $value = get_option( 'autoptimize_extra_settings' );
46
  if ( empty( $value ) ) {
@@ -48,13 +48,6 @@ class autoptimizeExtra
48
  $value = autoptimizeConfig::get_ao_extra_default_options();
49
  }
50
 
51
- // get service availability.
52
- $value['availabilities'] = get_option( 'autoptimize_service_availablity' );
53
-
54
- if ( empty( $value['availabilities'] ) ) {
55
- $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
56
- }
57
-
58
  return $value;
59
  }
60
 
@@ -85,7 +78,8 @@ class autoptimizeExtra
85
  }
86
  }
87
 
88
- public function filter_remove_qs( $src ) {
 
89
  if ( strpos( $src, '?ver=' ) ) {
90
  $src = remove_query_arg( 'ver', $src );
91
  }
@@ -128,15 +122,8 @@ class autoptimizeExtra
128
  add_filter( 'style_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
129
  }
130
 
131
- // Making sure is_plugin_active() exists when we need it.
132
- if ( ! function_exists( 'is_plugin_active' ) ) {
133
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
134
- }
135
  // Avoiding conflicts of interest when async-javascript plugin is active!
136
- $async_js_plugin_active = false;
137
- if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'async-javascript/async-javascript.php' ) ) {
138
- $async_js_plugin_active = true;
139
- }
140
  if ( ! empty( $options['autoptimize_extra_text_field_3'] ) && ! $async_js_plugin_active ) {
141
  add_filter( 'autoptimize_filter_js_exclude', array( $this, 'extra_async_js' ), 10, 1 );
142
  }
@@ -152,31 +139,6 @@ class autoptimizeExtra
152
  if ( ! empty( $options['autoptimize_extra_text_field_2'] ) || has_filter( 'autoptimize_extra_filter_tobepreconn' ) ) {
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;
171
- }
172
- if ( apply_filters( 'autoptimize_filter_extra_imgopt_do_css', true ) ) {
173
- add_filter( 'autoptimize_filter_base_replace_cdn', array( $this, 'filter_optimize_css_images' ), 10, 1 );
174
- $_imgopt_active = true;
175
- }
176
- if ( $_imgopt_active ) {
177
- add_filter( 'autoptimize_extra_filter_tobepreconn', array( $this, 'filter_preconnect_imgopt_url' ), 10, 1 );
178
- }
179
- }
180
  }
181
 
182
  public function filter_remove_emoji_dns_prefetch( $urls, $relation_type )
@@ -296,7 +258,8 @@ class autoptimizeExtra
296
  }
297
 
298
  // Replace back in markup.
299
- $out = substr_replace( $in, $fonts_markup . '<link', strpos( $in, '<link' ), strlen( '<link' ) );
 
300
  unset( $fonts_collection );
301
 
302
  // and insert preload polyfill if "link preload" and if the polyfill isn't there yet (courtesy of inline&defer).
@@ -318,7 +281,6 @@ class autoptimizeExtra
318
  // Walk array, extract domain and add to new array with crossorigin attribute.
319
  foreach ( $preconns as $preconn ) {
320
  $parsed = parse_url( $preconn );
321
-
322
  if ( is_array( $parsed ) && empty( $parsed['scheme'] ) ) {
323
  $domain = '//' . $parsed['host'];
324
  } elseif ( is_array( $parsed ) ) {
@@ -361,436 +323,16 @@ class autoptimizeExtra
361
  return $in;
362
  }
363
 
364
- public function filter_optimize_images( $in )
365
- {
366
- /*
367
- * potential future functional improvements:
368
- *
369
- * picture element.
370
- * filter for critical CSS.
371
- */
372
-
373
- $imgopt_base_url = $this->get_imgopt_base_url();
374
- $to_replace = array();
375
-
376
- // extract img tags.
377
- if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
378
- foreach ( $matches[0] as $tag ) {
379
- $orig_tag = $tag;
380
- $imgopt_w = '';
381
- $imgopt_h = '';
382
-
383
- // first do (data-)srcsets.
384
- if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
385
- foreach ( $allsrcsets as $srcset ) {
386
- $srcset = $srcset[2];
387
- $srcsets = explode( ',', $srcset );
388
- foreach ( $srcsets as $indiv_srcset ) {
389
- $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
390
- if ( $indiv_srcset_parts[1] && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
391
- $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
392
- }
393
- if ( $this->can_optimize_image( $indiv_srcset_parts[0] ) ) {
394
- $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
395
- $tag = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
396
- $to_replace[ $orig_tag ] = $tag;
397
- }
398
- }
399
- }
400
- }
401
-
402
- // proceed with img src.
403
- // first reset and then get width and height and add to $imgopt_size.
404
- $imgopt_w = '';
405
- $imgopt_h = '';
406
- if ( preg_match( '#width=("|\')(.*)("|\')#Usmi', $tag, $width ) ) {
407
- $imgopt_w = $width[2];
408
- }
409
- if ( preg_match( '#height=("|\')(.*)("|\')#Usmi', $tag, $height ) ) {
410
- $imgopt_h = $height[2];
411
- }
412
-
413
- // then start replacing images src.
414
- if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
415
- foreach ( $urls as $url ) {
416
- $full_src_orig = $url[0];
417
- $url = $url[1];
418
- if ( $this->can_optimize_image( $url ) ) {
419
- $imgopt_url = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
420
- $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
421
- $tag = str_replace( $full_src_orig, $full_imgopt_src, $tag );
422
- $to_replace[ $orig_tag ] = $tag;
423
- }
424
- }
425
- }
426
- }
427
- }
428
- $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
429
-
430
- // img thumbnails in e.g. woocommerce.
431
- if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_extra_imgopt_datathumbs', true ) ) {
432
- $out = preg_replace_callback(
433
- '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
434
- array( $this, 'replace_data_thumbs' ),
435
- $out
436
- );
437
- }
438
-
439
- return $out;
440
- }
441
-
442
- public function filter_optimize_css_images( $in )
443
- {
444
- $imgopt_base_url = $this->get_imgopt_base_url();
445
- $in = $this->normalize_img_urls( $in );
446
-
447
- if ( $this->can_optimize_image( $in ) ) {
448
- return $this->build_imgopt_url( $in, '', '' );
449
- } else {
450
- return $in;
451
- }
452
- }
453
-
454
- private function get_imgopt_base_url()
455
- {
456
- static $imgopt_base_url = null;
457
-
458
- if ( is_null( $imgopt_base_url ) ) {
459
- $imgopt_host = $this->get_imgopt_host();
460
- $quality = $this->get_img_quality_string();
461
- $ret_val = apply_filters( 'autoptimize_filter_extra_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
462
- $imgopt_base_url = $imgopt_host . 'client/' . $quality . ',' . $ret_val;
463
- $imgopt_base_url = apply_filters( 'autoptimize_filter_extra_imgopt_base_url', $imgopt_base_url );
464
- }
465
-
466
- return $imgopt_base_url;
467
- }
468
-
469
- private function can_optimize_image( $url )
470
- {
471
- static $cdn_url = null;
472
- static $nopti_images = null;
473
-
474
- if ( is_null( $cdn_url ) ) {
475
- $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
476
- }
477
-
478
- if ( is_null( $nopti_images ) ) {
479
- $nopti_images = apply_filters( 'autoptimize_filter_extra_imgopt_noptimize', '' );
480
- }
481
-
482
- $imgopt_base_url = $this->get_imgopt_base_url();
483
- $site_host = AUTOPTIMIZE_SITE_DOMAIN;
484
- $url_parsed = parse_url( $url );
485
-
486
- if ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
487
- return false;
488
- } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
489
- return false;
490
- } elseif ( strpos( $url, '.php' ) !== false ) {
491
- return false;
492
- } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
493
- // fixme: better check against end of string.
494
- return false;
495
- } elseif ( ! empty( $nopti_images ) ) {
496
- $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
497
- foreach ( $nopti_images_array as $nopti_image ) {
498
- if ( strpos( $url, $nopti_image ) !== false ) {
499
- return false;
500
- }
501
- }
502
- }
503
- return true;
504
- }
505
-
506
- private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
507
- {
508
- // sanitize width and height.
509
- if ( strpos( $width, '%' ) !== false ) {
510
- $width = 0;
511
- }
512
- if ( strpos( $height, '%' ) !== false ) {
513
- $height = 0;
514
- }
515
- $width = (int) $width;
516
- $height = (int) $height;
517
-
518
- $filtered_url = apply_filters( 'autoptimize_filter_extra_imgopt_build_url', $orig_url, $width, $height );
519
-
520
- if ( $filtered_url !== $orig_url ) {
521
- return $filtered_url;
522
- }
523
-
524
- $orig_url = $this->normalize_img_urls( $orig_url );
525
- $imgopt_base_url = $this->get_imgopt_base_url();
526
- $imgopt_size = '';
527
-
528
- if ( $width && 0 !== $width ) {
529
- $imgopt_size = ',w_' . $width;
530
- }
531
-
532
- if ( $height && 0 !== $height ) {
533
- $imgopt_size .= ',h_' . $height;
534
- }
535
-
536
- $url = $imgopt_base_url . $imgopt_size . '/' . $orig_url;
537
-
538
- return $url;
539
- }
540
-
541
- public function replace_data_thumbs( $matches )
542
- {
543
- if ( $this->can_optimize_image( $matches[1] ) ) {
544
- return str_replace( $matches[1], $this->build_imgopt_url( $matches[1], 150, 150 ), $matches[0] );
545
- } else {
546
- return $matches[0];
547
- }
548
- }
549
-
550
- public function filter_preconnect_imgopt_url( $in )
551
- {
552
- $imgopt_url_array = parse_url( $this->get_imgopt_base_url() );
553
- $in[] = $imgopt_url_array['scheme'] . '://' . $imgopt_url_array['host'];
554
-
555
- return $in;
556
- }
557
-
558
- private function normalize_img_urls( $in )
559
- {
560
- static $cdn_domain = null;
561
- if ( is_null( $cdn_domain ) ) {
562
- $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
563
- if ( ! empty( $cdn_url ) ) {
564
- $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
565
- } else {
566
- $cdn_domain = '';
567
- }
568
- }
569
-
570
- $parsed_site_url = parse_url( site_url() );
571
-
572
- if ( strpos( $in, 'http' ) !== 0 && strpos( $in, '//' ) === 0 ) {
573
- $in = $parsed_site_url['scheme'] . ':' . $in;
574
- } elseif ( strpos( $in, '/' ) === 0 ) {
575
- $in = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $in;
576
- } elseif ( ! empty( $cdn_domain ) && strpos( $in, $cdn_domain ) !== 0 ) {
577
- $in = str_replace( $cdn_domain, $parsed_site_url['host'], $in );
578
- }
579
-
580
- return apply_filters( 'autoptimize_filter_extra_imgopt_normalized_url', $in );
581
- }
582
-
583
- private function get_img_quality_array()
584
- {
585
- static $img_quality_array = null;
586
-
587
- if ( is_null( $img_quality_array ) ) {
588
- $img_quality_array = array(
589
- '1' => 'lossy',
590
- '2' => 'glossy',
591
- '3' => 'lossless',
592
- );
593
- $img_quality_array = apply_filters( 'autoptimize_filter_extra_imgopt_quality_array', $img_quality_array );
594
- }
595
-
596
- return $img_quality_array;
597
- }
598
-
599
- private function get_img_quality_setting()
600
- {
601
- static $_img_q = null;
602
-
603
- if ( is_null( $_img_q ) ) {
604
- if ( is_array( $this->options ) && array_key_exists( 'autoptimize_extra_select_field_6', $this->options ) ) {
605
- $_setting = $this->options['autoptimize_extra_select_field_6'];
606
- }
607
-
608
- if ( ! isset( $_setting ) || empty( $_setting ) || ( '1' !== $_setting && '3' !== $_setting ) ) {
609
- // default image opt. value is 2 ("glossy").
610
- $_img_q = '2';
611
- } else {
612
- $_img_q = $_setting;
613
- }
614
- }
615
-
616
- return $_img_q;
617
- }
618
-
619
- private function get_img_quality_string()
620
- {
621
- static $_img_q_string = null;
622
-
623
- if ( is_null( $_img_q_string ) ) {
624
- $_quality_array = $this->get_img_quality_array();
625
- $_setting = $this->get_img_quality_setting();
626
- $_img_q_string = apply_filters( 'autoptimize_filter_extra_imgopt_quality', 'q_' . $_quality_array[ $_setting ] );
627
- }
628
-
629
- return $_img_q_string;
630
- }
631
-
632
- public static function get_img_provider_stats()
633
- {
634
- // wrapper around query_img_provider_stats() so we can get to $this->options from cronjob() in autoptimizeCacheChecker.
635
- $self = new self();
636
- return $self->query_img_provider_stats();
637
- }
638
-
639
- public function query_img_provider_stats()
640
- {
641
- if ( ! empty( $this->options['autoptimize_extra_checkbox_field_5'] ) ) {
642
- $_img_provider_stat_url = '';
643
- $_img_provider_endpoint = $this->get_imgopt_host() . 'read-domain/';
644
- $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
645
-
646
- // make sure parse_url result makes sense, keeping $_img_provider_stat_url empty if not.
647
- if ( $_site_host && ! empty( $_site_host ) ) {
648
- $_img_provider_stat_url = $_img_provider_endpoint . $_site_host;
649
- }
650
-
651
- $_img_provider_stat_url = apply_filters( 'autoptimize_filter_extra_imgopt_stat_url', $_img_provider_stat_url );
652
-
653
- // only do the remote call if $_img_provider_stat_url is not empty to make sure no parse_url weirdness results in useless calls.
654
- if ( ! empty( $_img_provider_stat_url ) ) {
655
- $_img_stat_resp = wp_remote_get( $_img_provider_stat_url );
656
- if ( ! is_wp_error( $_img_stat_resp ) ) {
657
- if ( '200' == wp_remote_retrieve_response_code( $_img_stat_resp ) ) {
658
- $_img_provider_stat = json_decode( wp_remote_retrieve_body( $_img_stat_resp ), true );
659
- $_img_provider_stat['timestamp'] = time();
660
- update_option( 'autoptimize_imgopt_provider_stat', $_img_provider_stat );
661
- }
662
- }
663
- }
664
- }
665
- }
666
-
667
- public function imgopt_launch_ok()
668
- {
669
- static $launch_status = null;
670
-
671
- if ( is_null( $launch_status ) ) {
672
- $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
673
- $magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
674
- $has_launched = get_option( 'autoptimize_imgopt_launched', '' );
675
- if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
676
- $launch_status = true;
677
- if ( ! $has_launched ) {
678
- update_option( 'autoptimize_imgopt_launched', 'on' );
679
- }
680
- } else {
681
- $launch_status = false;
682
- }
683
- }
684
-
685
- return $launch_status;
686
- }
687
-
688
- public static function imgopt_launch_ok_wrapper()
689
- {
690
- // needed for "plug" notice in autoptimizeMain.php.
691
- $self = new self();
692
- return $self->imgopt_launch_ok();
693
- }
694
-
695
- public function get_imgopt_host()
696
- {
697
- static $imgopt_host = null;
698
-
699
- if ( is_null( $imgopt_host ) ) {
700
- $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
701
- if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
702
- $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
703
- } else {
704
- $imgopt_host = 'https://cdn.shortpixel.ai/';
705
- }
706
- }
707
-
708
- return $imgopt_host;
709
- }
710
-
711
- public static function get_imgopt_host_wrapper()
712
- {
713
- // needed for CI tests.
714
- $self = new self();
715
- return $self->get_imgopt_host();
716
- }
717
-
718
- public function get_imgopt_status_notice() {
719
- $_extra_options = $this->options;
720
- 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'] ) ) {
721
- $_imgopt_notice = '';
722
- $_stat = $this->get_imgopt_provider_userstatus();
723
- $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
724
- $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
725
-
726
- if ( is_array( $_stat ) ) {
727
- if ( 1 == $_stat['Status'] ) {
728
- // translators: "add more credits" will appear in a "a href".
729
- $_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>' );
730
- } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
731
- // translators: "add more credits" will appear in a "a href".
732
- $_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>' );
733
- $_imgopt_stats_refresh_url = add_query_arg( array(
734
- 'page' => 'autoptimize_extra',
735
- 'refreshImgProvStats' => '1',
736
- ), admin_url( 'options-general.php' ) );
737
- if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
738
- $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( get_option( 'time_format' ), $_stat['timestamp'] );
739
- } else {
740
- $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
741
- }
742
- $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
743
- // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
744
- $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
745
- } else {
746
- $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
747
- // translators: "log in to check your account" will appear in a "a href".
748
- $_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>' );
749
- }
750
- $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
751
-
752
- return array(
753
- 'status' => $_stat['Status'],
754
- 'notice' => $_imgopt_notice,
755
- );
756
- }
757
- }
758
- return false;
759
- }
760
-
761
- public static function get_imgopt_status_notice_wrapper() {
762
- // needed for notice being shown in autoptimizeCacheChecker.php.
763
- $self = new self();
764
- return $self->get_imgopt_status_notice();
765
- }
766
-
767
- public function get_imgopt_provider_userstatus() {
768
- static $_provider_userstatus = null;
769
-
770
- if ( is_null( $_provider_userstatus ) ) {
771
- $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
772
- if ( is_array( $_stat ) ) {
773
- if ( array_key_exists( 'Status', $_stat ) ) {
774
- $_provider_userstatus['Status'] = $_stat['Status'];
775
- } else {
776
- // if no stats then we assume all is well.
777
- $_provider_userstatus['Status'] = 2;
778
- }
779
- if ( array_key_exists( 'timestamp', $_stat ) ) {
780
- $_provider_userstatus['timestamp'] = $_stat['timestamp'];
781
- } else {
782
- // if no timestamp then we return "".
783
- $_provider_userstatus['timestamp'] = '';
784
- }
785
- }
786
- }
787
-
788
- return $_provider_userstatus;
789
- }
790
-
791
  public function admin_menu()
792
  {
793
- add_submenu_page( null, 'autoptimize_extra', 'autoptimize_extra', 'manage_options', 'autoptimize_extra', array( $this, 'options_page' ) );
 
 
 
 
 
 
 
794
  register_setting( 'autoptimize_extra_settings', 'autoptimize_extra_settings' );
795
  }
796
 
@@ -803,19 +345,13 @@ class autoptimizeExtra
803
 
804
  public function options_page()
805
  {
806
- // Check querystring for "refreshCacheChecker" and call cachechecker if so.
807
- if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
808
- $this->query_img_provider_stats();
809
- }
810
-
811
  // Working with actual option values from the database here.
812
  // That way any saves are still processed as expected, but we can still
813
  // override behavior by using `new autoptimizeExtra($custom_options)` and not have that custom
814
  // behavior being persisted in the DB even if save is done here.
815
- $options = $this->fetch_options();
816
- $gfonts = $options['autoptimize_extra_radio_field_4'];
817
- $sp_url_suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
818
- ?>
819
  <style>
820
  #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
821
  #ao_settings_form .form-table th {font-weight: normal;}
@@ -823,40 +359,13 @@ class autoptimizeExtra
823
  </style>
824
  <div class="wrap">
825
  <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
826
- <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
827
- <?php
828
- if ( 'on' !== get_option( 'autoptimize_js' ) && 'on' !== get_option( 'autoptimize_css' ) && 'on' !== get_option( 'autoptimize_html' ) ) {
829
- ?>
830
- <div class="notice-warning notice"><p>
831
- <?php
832
- _e( 'Most of below Extra optimizations require at least one of HTML, JS or CSS autoptimizations being active.', 'autoptimize' );
833
- ?>
834
- </p></div>
835
- <?php
836
- }
837
 
838
- if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) {
839
- ?>
840
- <div class="notice-warning notice"><p>
841
- <?php
842
- // translators: "Autoptimize support forum" will appear in a "a href".
843
- 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>' );
844
- ?>
845
- </p></div>
846
- <?php
847
- }
848
-
849
- if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] && ! $this->imgopt_launch_ok() ) {
850
- ?>
851
- <div class="notice-warning notice"><p>
852
- <?php
853
- _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' );
854
- ?>
855
- </p></div>
856
- <?php
857
- }
858
-
859
- ?>
860
  <form id='ao_settings_form' action='options.php' method='post'>
861
  <?php settings_fields( 'autoptimize_extra_settings' ); ?>
862
  <h2><?php _e( 'Extra Auto-Optimizations', 'autoptimize' ); ?></h2>
@@ -872,78 +381,6 @@ class autoptimizeExtra
872
  <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="4" <?php checked( 4, $gfonts, true ); ?> ><?php _e( 'Combine and load fonts asynchronously with <a href="https://github.com/typekit/webfontloader#readme" target="_blank">webfont.js</a>', 'autoptimize' ); ?><br/>
873
  </td>
874
  </tr>
875
- <tr>
876
- <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
877
- <td>
878
- <label><input id='autoptimize_imgopt_checkbox' type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_5]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && '1' === $options['autoptimize_extra_checkbox_field_5'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Optimize images on the fly and serve them from a CDN.', 'autoptimize' ); ?></label>
879
- <?php
880
- // show shortpixel status.
881
- $_notice = $this->get_imgopt_status_notice();
882
- if ( $_notice ) {
883
- switch ( $_notice['status'] ) {
884
- case 2:
885
- $_notice_color = 'green';
886
- break;
887
- case 1:
888
- $_notice_color = 'orange';
889
- break;
890
- case -1:
891
- $_notice_color = 'red';
892
- break;
893
- case -2:
894
- $_notice_color = 'red';
895
- break;
896
- default:
897
- $_notice_color = 'green';
898
- }
899
- echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
900
- } else {
901
- // translators: link points to shortpixel.
902
- $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>' );
903
- if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
904
- $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' );
905
- } else {
906
- // translators: link points to shortpixel.
907
- $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>' );
908
- }
909
- echo apply_filters( 'autoptimize_extra_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
910
- }
911
- // translators: link points to shortpixel FAQ.
912
- $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>' );
913
- // translators: links points to shortpixel TOS & Privacy Policy.
914
- $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>' );
915
- echo apply_filters( 'autoptimize_extra_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
916
- ?>
917
- </td>
918
- </tr>
919
- <tr id='autoptimize_imgopt_quality' <?php if ( ! array_key_exists( 'autoptimize_extra_checkbox_field_5', $options ) || ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && '1' !== $options['autoptimize_extra_checkbox_field_5'] ) ) { echo 'class="hidden"'; } ?>>
920
- <th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
921
- <td>
922
- <label>
923
- <select name='autoptimize_extra_settings[autoptimize_extra_select_field_6]'>
924
- <?php
925
- $_imgopt_array = $this->get_img_quality_array();
926
- $_imgopt_val = $this->get_img_quality_setting();
927
-
928
- foreach ( $_imgopt_array as $key => $value ) {
929
- echo '<option value="' . $key . '"';
930
- if ( $_imgopt_val == $key ) {
931
- echo ' selected';
932
- }
933
- echo '>' . ucfirst( $value ) . '</option>';
934
- }
935
- echo "\n";
936
- ?>
937
- </select>
938
- </label>
939
- <p>
940
- <?php
941
- // translators: link points to shortpixel image test page.
942
- echo apply_filters( 'autoptimize_extra_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>' ) );
943
- ?>
944
- </p>
945
- </td>
946
- </tr>
947
  <tr>
948
  <th scope="row"><?php _e( 'Remove emojis', 'autoptimize' ); ?></th>
949
  <td>
@@ -966,7 +403,7 @@ class autoptimizeExtra
966
  <th scope="row"><?php _e( 'Async Javascript-files <em>(advanced users)</em>', 'autoptimize' ); ?></th>
967
  <td>
968
  <?php
969
- if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'async-javascript/async-javascript.php' ) ) {
970
  // translators: link points Async Javascript settings page.
971
  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>' );
972
  } else {
@@ -987,7 +424,7 @@ class autoptimizeExtra
987
  <th scope="row"><?php _e( 'Optimize YouTube videos', 'autoptimize' ); ?></th>
988
  <td>
989
  <?php
990
- if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'wp-youtube-lyte/wp-youtube-lyte.php' ) ) {
991
  _e( 'Great, you have WP YouTube Lyte installed.', 'autoptimize' );
992
  $lyte_config_url = 'options-general.php?page=lyte_settings_page';
993
  echo sprintf( ' <a href="' . $lyte_config_url . '">%s</a>', __( 'Click here to configure it.', 'autoptimize' ) );
@@ -1003,17 +440,6 @@ class autoptimizeExtra
1003
  </table>
1004
  <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
1005
  </form>
1006
- <script>
1007
- jQuery(document).ready(function() {
1008
- jQuery( "#autoptimize_imgopt_checkbox" ).change(function() {
1009
- if (this.checked) {
1010
- jQuery("#autoptimize_imgopt_quality").show("slow");
1011
- } else {
1012
- jQuery("#autoptimize_imgopt_quality").hide("slow");
1013
- }
1014
- });
1015
- });
1016
- </script>
1017
- <?php
1018
  }
1019
  }
24
  public function __construct( $options = array() )
25
  {
26
  if ( empty( $options ) ) {
27
+ $options = self::fetch_options();
28
  }
29
 
30
  $this->options = $options;
40
  }
41
  }
42
 
43
+ public static function fetch_options()
44
  {
45
  $value = get_option( 'autoptimize_extra_settings' );
46
  if ( empty( $value ) ) {
48
  $value = autoptimizeConfig::get_ao_extra_default_options();
49
  }
50
 
 
 
 
 
 
 
 
51
  return $value;
52
  }
53
 
78
  }
79
  }
80
 
81
+ public function filter_remove_qs( $src )
82
+ {
83
  if ( strpos( $src, '?ver=' ) ) {
84
  $src = remove_query_arg( 'ver', $src );
85
  }
122
  add_filter( 'style_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
123
  }
124
 
 
 
 
 
125
  // Avoiding conflicts of interest when async-javascript plugin is active!
126
+ $async_js_plugin_active = autoptimizeUtils::is_plugin_active( 'async-javascript/async-javascript.php' );
 
 
 
127
  if ( ! empty( $options['autoptimize_extra_text_field_3'] ) && ! $async_js_plugin_active ) {
128
  add_filter( 'autoptimize_filter_js_exclude', array( $this, 'extra_async_js' ), 10, 1 );
129
  }
139
  if ( ! empty( $options['autoptimize_extra_text_field_2'] ) || has_filter( 'autoptimize_extra_filter_tobepreconn' ) ) {
140
  add_filter( 'wp_resource_hints', array( $this, 'filter_preconnect' ), 10, 2 );
141
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  }
143
 
144
  public function filter_remove_emoji_dns_prefetch( $urls, $relation_type )
258
  }
259
 
260
  // Replace back in markup.
261
+ $inject_point = apply_filters( 'autoptimize_filter_extra_gfont_injectpoint', '<link' );
262
+ $out = substr_replace( $in, $fonts_markup . $inject_point, strpos( $in, $inject_point ), strlen( $inject_point ) );
263
  unset( $fonts_collection );
264
 
265
  // and insert preload polyfill if "link preload" and if the polyfill isn't there yet (courtesy of inline&defer).
281
  // Walk array, extract domain and add to new array with crossorigin attribute.
282
  foreach ( $preconns as $preconn ) {
283
  $parsed = parse_url( $preconn );
 
284
  if ( is_array( $parsed ) && empty( $parsed['scheme'] ) ) {
285
  $domain = '//' . $parsed['host'];
286
  } elseif ( is_array( $parsed ) ) {
323
  return $in;
324
  }
325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  public function admin_menu()
327
  {
328
+ add_submenu_page(
329
+ null,
330
+ 'autoptimize_extra',
331
+ 'autoptimize_extra',
332
+ 'manage_options',
333
+ 'autoptimize_extra',
334
+ array( $this, 'options_page' )
335
+ );
336
  register_setting( 'autoptimize_extra_settings', 'autoptimize_extra_settings' );
337
  }
338
 
345
 
346
  public function options_page()
347
  {
 
 
 
 
 
348
  // Working with actual option values from the database here.
349
  // That way any saves are still processed as expected, but we can still
350
  // override behavior by using `new autoptimizeExtra($custom_options)` and not have that custom
351
  // behavior being persisted in the DB even if save is done here.
352
+ $options = $this->fetch_options();
353
+ $gfonts = $options['autoptimize_extra_radio_field_4'];
354
+ ?>
 
355
  <style>
356
  #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
357
  #ao_settings_form .form-table th {font-weight: normal;}
359
  </style>
360
  <div class="wrap">
361
  <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
362
+ <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
363
+ <?php if ( 'on' !== get_option( 'autoptimize_js' ) && 'on' !== get_option( 'autoptimize_css' ) && 'on' !== get_option( 'autoptimize_html' ) && ! autoptimizeImages::imgopt_active() ) { ?>
364
+ <div class="notice-warning notice"><p>
365
+ <?php _e( 'Most of below Extra optimizations require at least one of HTML, JS, CSS or Image autoptimizations being active.', 'autoptimize' ); ?>
366
+ </p></div>
367
+ <?php } ?>
 
 
 
 
 
368
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  <form id='ao_settings_form' action='options.php' method='post'>
370
  <?php settings_fields( 'autoptimize_extra_settings' ); ?>
371
  <h2><?php _e( 'Extra Auto-Optimizations', 'autoptimize' ); ?></h2>
381
  <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="4" <?php checked( 4, $gfonts, true ); ?> ><?php _e( 'Combine and load fonts asynchronously with <a href="https://github.com/typekit/webfontloader#readme" target="_blank">webfont.js</a>', 'autoptimize' ); ?><br/>
382
  </td>
383
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
  <tr>
385
  <th scope="row"><?php _e( 'Remove emojis', 'autoptimize' ); ?></th>
386
  <td>
403
  <th scope="row"><?php _e( 'Async Javascript-files <em>(advanced users)</em>', 'autoptimize' ); ?></th>
404
  <td>
405
  <?php
406
+ if ( autoptimizeUtils::is_plugin_active( 'async-javascript/async-javascript.php' ) ) {
407
  // translators: link points Async Javascript settings page.
408
  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>' );
409
  } else {
424
  <th scope="row"><?php _e( 'Optimize YouTube videos', 'autoptimize' ); ?></th>
425
  <td>
426
  <?php
427
+ if ( autoptimizeUtils::is_plugin_active( 'wp-youtube-lyte/wp-youtube-lyte.php' ) ) {
428
  _e( 'Great, you have WP YouTube Lyte installed.', 'autoptimize' );
429
  $lyte_config_url = 'options-general.php?page=lyte_settings_page';
430
  echo sprintf( ' <a href="' . $lyte_config_url . '">%s</a>', __( 'Click here to configure it.', 'autoptimize' ) );
440
  </table>
441
  <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
442
  </form>
443
+ <?php
 
 
 
 
 
 
 
 
 
 
 
444
  }
445
  }
classes/autoptimizeImages.php ADDED
@@ -0,0 +1,1174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Handles optimizing images.
4
+ */
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit;
8
+ }
9
+
10
+ class autoptimizeImages
11
+ {
12
+ /**
13
+ * Options.
14
+ *
15
+ * @var array
16
+ */
17
+ protected $options = array();
18
+
19
+ /**
20
+ * Singleton instance.
21
+ *
22
+ * @var self|null
23
+ */
24
+ protected static $instance = null;
25
+
26
+ public function __construct( array $options = array() )
27
+ {
28
+ // If options are not provided, fetch them.
29
+ if ( empty( $options ) ) {
30
+ $options = $this->fetch_options();
31
+ }
32
+
33
+ $this->set_options( $options );
34
+ }
35
+
36
+ public function set_options( array $options )
37
+ {
38
+ $this->options = $options;
39
+
40
+ return $this;
41
+ }
42
+
43
+ public static function fetch_options()
44
+ {
45
+ $value = get_option( 'autoptimize_imgopt_settings' );
46
+ if ( empty( $value ) ) {
47
+ // Fallback to returning defaults when no stored option exists yet.
48
+ $value = autoptimizeConfig::get_ao_imgopt_default_options();
49
+ }
50
+
51
+ // get service availability and add it to the options-array.
52
+ $value['availabilities'] = get_option( 'autoptimize_service_availablity' );
53
+
54
+ if ( empty( $value['availabilities'] ) ) {
55
+ $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
56
+ }
57
+
58
+ return $value;
59
+ }
60
+
61
+ public static function imgopt_active()
62
+ {
63
+ // function to quickly check if imgopt is active, used below but also in
64
+ // autoptimizeMain.php to start ob_ even if no HTML, JS or CSS optimizing is done
65
+ // and does not use/ request the availablity data (which could slow things down).
66
+ static $imgopt_active = null;
67
+
68
+ if ( null === $imgopt_active ) {
69
+ $opts = get_option( 'autoptimize_imgopt_settings', '' );
70
+ 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
+ $imgopt_active = true;
72
+ } else {
73
+ $imgopt_active = false;
74
+ }
75
+ }
76
+
77
+ return $imgopt_active;
78
+ }
79
+
80
+ /**
81
+ * Helper for getting a singleton instance. While being an
82
+ * anti-pattern generally, it comes in handy for now from a
83
+ * readability/maintainability perspective, until we get some
84
+ * proper dependency injection going.
85
+ *
86
+ * @return self
87
+ */
88
+ public static function instance()
89
+ {
90
+ if ( null === self::$instance ) {
91
+ self::$instance = new self();
92
+ }
93
+
94
+ return self::$instance;
95
+ }
96
+
97
+ public function run()
98
+ {
99
+ if ( is_admin() ) {
100
+ add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
101
+ add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_imgopt_tab' ), 9 );
102
+ } else {
103
+ $this->run_on_frontend();
104
+ }
105
+ }
106
+
107
+ public function run_on_frontend() {
108
+ if ( ! $this->should_run() ) {
109
+ if ( $this->should_lazyload() ) {
110
+ add_filter(
111
+ 'autoptimize_html_after_minify',
112
+ array( $this, 'filter_lazyload_images' ),
113
+ 10,
114
+ 1
115
+ );
116
+ add_action(
117
+ 'wp_footer',
118
+ array( $this, 'add_lazyload_js_footer' ),
119
+ 10,
120
+ 0
121
+ );
122
+ }
123
+ return;
124
+ }
125
+
126
+ $active = false;
127
+
128
+ if ( apply_filters( 'autoptimize_filter_imgopt_do', true ) ) {
129
+ add_filter(
130
+ 'autoptimize_html_after_minify',
131
+ array( $this, 'filter_optimize_images' ),
132
+ 10,
133
+ 1
134
+ );
135
+ $active = true;
136
+ }
137
+
138
+ if ( apply_filters( 'autoptimize_filter_imgopt_do_css', true ) ) {
139
+ add_filter(
140
+ 'autoptimize_filter_base_replace_cdn',
141
+ array( $this, 'filter_optimize_css_images' ),
142
+ 10,
143
+ 1
144
+ );
145
+ $active = true;
146
+ }
147
+
148
+ if ( $active ) {
149
+ add_filter(
150
+ 'autoptimize_extra_filter_tobepreconn',
151
+ array( $this, 'filter_preconnect_imgopt_url' ),
152
+ 10,
153
+ 1
154
+ );
155
+ }
156
+
157
+ if ( $this->should_lazyload() ) {
158
+ add_action(
159
+ 'wp_footer',
160
+ array( $this, 'add_lazyload_js_footer' )
161
+ );
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Basic checks before we can run.
167
+ *
168
+ * @return bool
169
+ */
170
+ protected function should_run()
171
+ {
172
+ $opts = $this->options;
173
+ $service_not_down = ( 'down' !== $opts['availabilities']['extra_imgopt']['status'] );
174
+ $not_launch_status = ( 'launch' !== $opts['availabilities']['extra_imgopt']['status'] );
175
+
176
+ $do_cdn = true;
177
+ $_userstatus = $this->get_imgopt_provider_userstatus();
178
+ if ( -2 == $_userstatus['Status'] ) {
179
+ $do_cdn = false;
180
+ }
181
+
182
+ if (
183
+ $this->imgopt_active()
184
+ && $do_cdn
185
+ && $service_not_down
186
+ && ( $not_launch_status || $this->launch_ok() )
187
+ ) {
188
+ return true;
189
+ }
190
+ return false;
191
+ }
192
+
193
+ public function get_imgopt_host()
194
+ {
195
+ static $imgopt_host = null;
196
+
197
+ if ( null === $imgopt_host ) {
198
+ $imgopt_host = 'https://cdn.shortpixel.ai/';
199
+ $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
200
+ if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
201
+ $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
202
+ }
203
+ }
204
+
205
+ return $imgopt_host;
206
+ }
207
+
208
+ public static function get_imgopt_host_wrapper()
209
+ {
210
+ // needed for CI tests.
211
+ $self = new self();
212
+ return $self->get_imgopt_host();
213
+ }
214
+
215
+ public static function get_service_url_suffix()
216
+ {
217
+ $suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
218
+
219
+ return $suffix;
220
+ }
221
+
222
+ public function get_img_quality_string()
223
+ {
224
+ static $quality = null;
225
+
226
+ if ( null === $quality ) {
227
+ $q_array = $this->get_img_quality_array();
228
+ $setting = $this->get_img_quality_setting();
229
+ $quality = apply_filters(
230
+ 'autoptimize_filter_imgopt_quality',
231
+ 'q_' . $q_array[ $setting ]
232
+ );
233
+ }
234
+
235
+ return $quality;
236
+ }
237
+
238
+ public function get_img_quality_array()
239
+ {
240
+ static $map = null;
241
+
242
+ if ( null === $map ) {
243
+ $map = array(
244
+ '1' => 'lossy',
245
+ '2' => 'glossy',
246
+ '3' => 'lossless',
247
+ );
248
+ $map = apply_filters(
249
+ 'autoptimize_filter_imgopt_quality_array',
250
+ $map
251
+ );
252
+ }
253
+
254
+ return $map;
255
+ }
256
+
257
+ public function get_img_quality_setting()
258
+ {
259
+ static $q = null;
260
+
261
+ if ( null === $q ) {
262
+ if ( is_array( $this->options ) && array_key_exists( 'autoptimize_imgopt_select_field_2', $this->options ) ) {
263
+ $setting = $this->options['autoptimize_imgopt_select_field_2'];
264
+ }
265
+
266
+ if ( ! isset( $setting ) || empty( $setting ) || ( '1' !== $setting && '3' !== $setting ) ) {
267
+ // default image opt. value is 2 ("glossy").
268
+ $q = '2';
269
+ } else {
270
+ $q = $setting;
271
+ }
272
+ }
273
+
274
+ return $q;
275
+ }
276
+
277
+ public function filter_preconnect_imgopt_url( array $in )
278
+ {
279
+ $url_parts = parse_url( $this->get_imgopt_base_url() );
280
+ $in[] = $url_parts['scheme'] . '://' . $url_parts['host'];
281
+
282
+ return $in;
283
+ }
284
+
285
+ /**
286
+ * Makes sure given url contains the full scheme and hostname
287
+ * in case they're not present already.
288
+ *
289
+ * @param string $in Image url to normalize.
290
+ *
291
+ * @return string
292
+ */
293
+ private function normalize_img_url( $in )
294
+ {
295
+ // Only parse the site url once.
296
+ static $parsed_site_url = null;
297
+ if ( null === $parsed_site_url ) {
298
+ $parsed_site_url = parse_url( site_url() );
299
+ }
300
+
301
+ // get CDN domain once.
302
+ static $cdn_domain = null;
303
+ if ( is_null( $cdn_domain ) ) {
304
+ $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
305
+ if ( ! empty( $cdn_url ) ) {
306
+ $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
307
+ } else {
308
+ $cdn_domain = '';
309
+ }
310
+ }
311
+
312
+ /**
313
+ * This method gets called a lot, often for identical urls it seems.
314
+ * `filter_optimize_css_images()` calls us, uses the resulting url and
315
+ * gives it to `can_optimize_image()`, and if that returns trueish
316
+ * then `build_imgopt_url()` is called (which, again, calls this method).
317
+ * Until we dig deeper into whether this all must really happen that
318
+ * way, having an internal cache here helps (to avoid doing repeated
319
+ * identical string operations).
320
+ */
321
+ static $cache = null;
322
+ if ( null === $cache ) {
323
+ $cache = array();
324
+ }
325
+
326
+ // Do the work on cache miss only.
327
+ 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, '/' ) ) {
333
+ // 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 );
342
+ }
343
+
344
+ $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );
345
+
346
+ // Store in cache.
347
+ $cache[ $in ] = $result;
348
+ }
349
+
350
+ return $cache[ $in ];
351
+ }
352
+
353
+ public function filter_optimize_css_images( $in )
354
+ {
355
+ $in = $this->normalize_img_url( $in );
356
+
357
+ if ( $this->can_optimize_image( $in ) ) {
358
+ return $this->build_imgopt_url( $in, '', '' );
359
+ } else {
360
+ return $in;
361
+ }
362
+ }
363
+
364
+ private function get_imgopt_base_url()
365
+ {
366
+ static $imgopt_base_url = null;
367
+
368
+ if ( null === $imgopt_base_url ) {
369
+ $imgopt_host = $this->get_imgopt_host();
370
+ $quality = $this->get_img_quality_string();
371
+ $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;
373
+ $imgopt_base_url = apply_filters( 'autoptimize_filter_imgopt_base_url', $imgopt_base_url );
374
+ }
375
+
376
+ return $imgopt_base_url;
377
+ }
378
+
379
+ private function can_optimize_image( $url )
380
+ {
381
+ static $cdn_url = null;
382
+ static $nopti_images = null;
383
+
384
+ if ( null === $cdn_url ) {
385
+ $cdn_url = apply_filters(
386
+ 'autoptimize_filter_base_cdnurl',
387
+ get_option( 'autoptimize_cdn_url', '' )
388
+ );
389
+ }
390
+
391
+ if ( null === $nopti_images ) {
392
+ $nopti_images = apply_filters( 'autoptimize_filter_imgopt_noptimize', '' );
393
+ }
394
+
395
+ $site_host = AUTOPTIMIZE_SITE_DOMAIN;
396
+ $url = $this->normalize_img_url( $url );
397
+ $url_parsed = parse_url( $url );
398
+
399
+ if ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
400
+ return false;
401
+ } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
402
+ return false;
403
+ } elseif ( strpos( $url, '.php' ) !== false ) {
404
+ return false;
405
+ } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
406
+ // fixme: better check against end of string.
407
+ return false;
408
+ } elseif ( ! empty( $nopti_images ) ) {
409
+ $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
410
+ foreach ( $nopti_images_array as $nopti_image ) {
411
+ if ( strpos( $url, $nopti_image ) !== false ) {
412
+ return false;
413
+ }
414
+ }
415
+ }
416
+ return true;
417
+ }
418
+
419
+ private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
420
+ {
421
+ // sanitize width and height.
422
+ if ( strpos( $width, '%' ) !== false ) {
423
+ $width = 0;
424
+ }
425
+ if ( strpos( $height, '%' ) !== false ) {
426
+ $height = 0;
427
+ }
428
+ $width = (int) $width;
429
+ $height = (int) $height;
430
+
431
+ $filtered_url = apply_filters(
432
+ 'autoptimize_filter_imgopt_build_url',
433
+ $orig_url,
434
+ $width,
435
+ $height
436
+ );
437
+
438
+ // If filter modified the url, return that.
439
+ if ( $filtered_url !== $orig_url ) {
440
+ return $filtered_url;
441
+ }
442
+
443
+ $orig_url = $this->normalize_img_url( $orig_url );
444
+ $imgopt_base_url = $this->get_imgopt_base_url();
445
+ $imgopt_size = '';
446
+
447
+ if ( $width && 0 !== $width ) {
448
+ $imgopt_size = ',w_' . $width;
449
+ }
450
+
451
+ if ( $height && 0 !== $height ) {
452
+ $imgopt_size .= ',h_' . $height;
453
+ }
454
+
455
+ $url = $imgopt_base_url . $imgopt_size . '/' . $orig_url;
456
+
457
+ return $url;
458
+ }
459
+
460
+ public function replace_data_thumbs( $matches )
461
+ {
462
+ return $this->replace_img_callback( $matches, 150, 150 );
463
+ }
464
+
465
+ public function replace_img_callback( $matches, $width = 0, $height = 0 )
466
+ {
467
+ if ( $this->can_optimize_image( $matches[1] ) ) {
468
+ return str_replace( $matches[1], $this->build_imgopt_url( $matches[1], $width, $height ), $matches[0] );
469
+ } else {
470
+ return $matches[0];
471
+ }
472
+ }
473
+
474
+ public function filter_optimize_images( $in )
475
+ {
476
+ /*
477
+ * potential future functional improvements:
478
+ *
479
+ * picture element.
480
+ * filter for critical CSS.
481
+ */
482
+ $to_replace = array();
483
+
484
+ // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
485
+ if ( $this->should_lazyload() ) {
486
+ $in = autoptimizeBase::replace_contents_with_marker_if_exists(
487
+ 'NOSCRIPT',
488
+ '<noscript',
489
+ '#<noscript.*?<\/noscript>#is',
490
+ $in
491
+ );
492
+ }
493
+
494
+ // extract img tags.
495
+ if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
496
+ foreach ( $matches[0] as $tag ) {
497
+ $orig_tag = $tag;
498
+ $imgopt_w = '';
499
+ $imgopt_h = '';
500
+
501
+ // first do (data-)srcsets.
502
+ if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
503
+ foreach ( $allsrcsets as $srcset ) {
504
+ $srcset = $srcset[2];
505
+ $srcsets = explode( ',', $srcset );
506
+ foreach ( $srcsets as $indiv_srcset ) {
507
+ $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
508
+ if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
509
+ $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
510
+ }
511
+ if ( $this->can_optimize_image( $indiv_srcset_parts[0] ) ) {
512
+ $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
513
+ $tag = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
514
+ }
515
+ }
516
+ }
517
+ }
518
+
519
+ // proceed with img src.
520
+ // get width and height and add to $imgopt_size.
521
+ $_get_size = $this->get_size_from_tag( $tag );
522
+ $imgopt_w = $_get_size['width'];
523
+ $imgopt_h = $_get_size['height'];
524
+
525
+ // 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
+ }
535
+ }
536
+ }
537
+
538
+ // 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.
547
+ if ( strpos( $url, $this->get_imgopt_host() ) === 0 ) {
548
+ // if all img src have been replaced during srcset, we have to extract the
549
+ // origin url from the imgopt one to be able to set a lqip placeholder.
550
+ $_url = substr( $url, strpos( $url, '/http' ) + 1 );
551
+ } else {
552
+ $_url = $url;
553
+ }
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
+
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;"';
565
+ }
566
+
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 );
570
+ }
571
+
572
+ // add tag to array for later replacement.
573
+ if ( $tag !== $orig_tag ) {
574
+ $to_replace[ $orig_tag ] = $tag;
575
+ }
576
+ }
577
+ }
578
+
579
+ // and replace all.
580
+ $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
581
+
582
+ // img thumbnails in e.g. woocommerce.
583
+ if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_imgopt_datathumbs', true ) ) {
584
+ $out = preg_replace_callback(
585
+ '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
586
+ array( $this, 'replace_data_thumbs' ),
587
+ $out
588
+ );
589
+ }
590
+
591
+ // background-image in inline style.
592
+ if ( strpos( $out, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
593
+ $out = preg_replace_callback(
594
+ '/style=(?:"|\').*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
595
+ array( $this, 'replace_img_callback' ),
596
+ $out
597
+ );
598
+ }
599
+
600
+ // and restore noscript tags if these were hidden for lazyload purposes.
601
+ if ( $this->should_lazyload() ) {
602
+ $out = autoptimizeBase::restore_marked_content(
603
+ 'NOSCRIPT',
604
+ $out
605
+ );
606
+ }
607
+
608
+ return $out;
609
+ }
610
+
611
+ public function get_size_from_tag( $tag ) {
612
+ // reusable function to extract widht and height from an image tag
613
+ // enforcing a filterable maximum width and height (default 4999X4999).
614
+ $width = '';
615
+ $height = '';
616
+
617
+ if ( preg_match( '#width=("|\')(.*)("|\')#Usmi', $tag, $_width ) ) {
618
+ if ( strpos( $_width[2], '%' ) === false ) {
619
+ $width = (int) $_width[2];
620
+ }
621
+ }
622
+ if ( preg_match( '#height=("|\')(.*)("|\')#Usmi', $tag, $_height ) ) {
623
+ if ( strpos( $_height[2], '%' ) === false ) {
624
+ $height = (int) $_height[2];
625
+ }
626
+ }
627
+
628
+ // check for and enforce (filterable) max sizes.
629
+ $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
630
+ if ( $width > $_max_width ) {
631
+ $_width = $_max_width;
632
+ $height = $_width / $width * $height;
633
+ $width = $_width;
634
+ }
635
+ $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
636
+ if ( $height > $_max_height ) {
637
+ $_height = $_max_height;
638
+ $width = $_height / $height * $width;
639
+ $height = $_height;
640
+ }
641
+
642
+ return array(
643
+ 'width' => $width,
644
+ 'height' => $height,
645
+ );
646
+ }
647
+
648
+ /**
649
+ * Lazyload functions
650
+ */
651
+ public function should_lazyload() {
652
+ if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) ) {
653
+ $lazyload_return = true;
654
+ } else {
655
+ $lazyload_return = false;
656
+ }
657
+
658
+ return $lazyload_return;
659
+ }
660
+
661
+ public static function should_lazyload_wrapper() {
662
+ // needed in autoptimizeMain.php.
663
+ $self = new self();
664
+ return $self->should_lazyload();
665
+ }
666
+
667
+ public function filter_lazyload_images( $in )
668
+ {
669
+ // only used is image optimization is NOT active but lazyload is.
670
+ $to_replace = array();
671
+
672
+ // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
673
+ $out = autoptimizeBase::replace_contents_with_marker_if_exists(
674
+ 'NOSCRIPT',
675
+ '<noscript',
676
+ '#<noscript.*?<\/noscript>#is',
677
+ $in
678
+ );
679
+
680
+ // extract img tags and add lazyload attribs.
681
+ if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
682
+ foreach ( $matches[0] as $tag ) {
683
+ $to_replace[ $tag ] = $this->add_lazyload( $tag );
684
+ }
685
+ $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
686
+ }
687
+
688
+ // restore noscript tags.
689
+ $out = autoptimizeBase::restore_marked_content(
690
+ 'NOSCRIPT',
691
+ $out
692
+ );
693
+
694
+ return $out;
695
+ }
696
+
697
+ public function add_lazyload( $tag ) {
698
+ // adds actual lazyload-attributes to an image node.
699
+ if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
700
+ // store original tag for use in noscript version.
701
+ $noscript_tag = '<noscript>' . $tag . '</noscript>';
702
+
703
+ // insert lazyload class.
704
+ $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
705
+
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.
712
+ }
713
+ if ( false === $height ) {
714
+ $heigth = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
715
+ }
716
+
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
+ // add the noscript-tag from earlier.
724
+ $tag = $noscript_tag . $tag;
725
+ $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
726
+ }
727
+
728
+ return $tag;
729
+ }
730
+
731
+ public function add_lazyload_js_footer() {
732
+ // The JS will by default be excluded form autoptimization but this can be changed with a filter.
733
+ $noptimize_flag = '';
734
+ if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
735
+ $noptimize_flag = ' data-noptimize="1"';
736
+ }
737
+
738
+ // 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>';
742
+
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>' );
748
+ }
749
+ }
750
+
751
+ public function get_lazyload_exclusions() {
752
+ // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
753
+ static $exclude_lazyload_array = null;
754
+
755
+ if ( null === $exclude_lazyload_array ) {
756
+ $options = $this->options;
757
+
758
+ // set default exclusions.
759
+ $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset' );
760
+
761
+ // add from setting.
762
+ if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
763
+ $exclude_lazyload_option = $options['autoptimize_imgopt_text_field_5'];
764
+ if ( ! empty( $exclude_lazyload_option ) ) {
765
+ $exclude_lazyload_array = array_merge( $exclude_lazyload_array, array_filter( array_map( 'trim', explode( ',', $options['autoptimize_imgopt_text_field_5'] ) ) ) );
766
+ }
767
+ }
768
+
769
+ // and filter for developer-initiated changes.
770
+ $exclude_lazyload_array = apply_filters( 'autoptimize_filter_imgopt_lazyload_exclude_array', $exclude_lazyload_array );
771
+ }
772
+
773
+ return $exclude_lazyload_array;
774
+ }
775
+
776
+ public function inject_classes_in_tag( $tag, $target_class ) {
777
+ if ( strpos( $tag, 'class=' ) !== false ) {
778
+ $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
779
+ } else {
780
+ $tag = str_replace( '<img ', '<img class="' . trim( $target_class ) . '" ', $tag );
781
+ }
782
+
783
+ return $tag;
784
+ }
785
+
786
+ public function get_default_lazyload_placeholder( $imgopt_w, $imgopt_h ) {
787
+ 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
+ }
789
+
790
+ public function should_webp() {
791
+ static $webp_return = null;
792
+
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;
797
+ } else {
798
+ $webp_return = false;
799
+ }
800
+ }
801
+
802
+ return $webp_return;
803
+ }
804
+
805
+ /**
806
+ * Admin page logic and related functions below.
807
+ */
808
+ public function imgopt_admin_menu()
809
+ {
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
+ );
818
+ register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
819
+ }
820
+
821
+ public function add_imgopt_tab( $in )
822
+ {
823
+ $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
824
+
825
+ return $in;
826
+ }
827
+
828
+ public function imgopt_options_page()
829
+ {
830
+ // Check querystring for "refreshCacheChecker" and call cachechecker if so.
831
+ if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
832
+ $this->query_img_provider_stats();
833
+ }
834
+
835
+ $options = $this->fetch_options();
836
+ $sp_url_suffix = $this->get_service_url_suffix();
837
+ ?>
838
+ <style>
839
+ #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
840
+ #ao_settings_form .form-table th {font-weight: normal;}
841
+ #autoptimize_imgopt_descr{font-size: 120%;}
842
+ </style>
843
+ <div class="wrap">
844
+ <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
845
+ <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
846
+ <?php if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) { ?>
847
+ <div class="notice-warning notice"><p>
848
+ <?php
849
+ // 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>' );
851
+ ?>
852
+ </p></div>
853
+ <?php } ?>
854
+
855
+ <?php if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] && ! autoptimizeImages::instance()->launch_ok() ) { ?>
856
+ <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' ); ?>
858
+ </p></div>
859
+ <?php } ?>
860
+
861
+ <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
862
+ <div class="notice-warning notice"><p>
863
+ <?php
864
+ // 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>' );
866
+ ?>
867
+ </p></div>
868
+ <?php } ?>
869
+ <form id='ao_settings_form' action='options.php' method='post'>
870
+ <?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>
873
+ <table class="form-table">
874
+ <tr>
875
+ <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
876
+ <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>
878
+ <?php
879
+ // show shortpixel status.
880
+ $_notice = autoptimizeImages::instance()->get_status_notice();
881
+ if ( $_notice ) {
882
+ switch ( $_notice['status'] ) {
883
+ case 2:
884
+ $_notice_color = 'green';
885
+ break;
886
+ case 1:
887
+ $_notice_color = 'orange';
888
+ break;
889
+ case -1:
890
+ $_notice_color = 'red';
891
+ break;
892
+ case -2:
893
+ $_notice_color = 'red';
894
+ break;
895
+ default:
896
+ $_notice_color = 'green';
897
+ }
898
+ echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
899
+ } else {
900
+ // 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>' );
902
+ 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' );
904
+ } 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>' );
907
+ }
908
+ echo apply_filters( 'autoptimize_imgopt_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
909
+ }
910
+ // 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>' );
912
+ // 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>' );
915
+ ?>
916
+ </td>
917
+ </tr>
918
+ <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>
920
+ <td>
921
+ <label>
922
+ <select name='autoptimize_imgopt_settings[autoptimize_imgopt_select_field_2]'>
923
+ <?php
924
+ $_imgopt_array = autoptimizeImages::instance()->get_img_quality_array();
925
+ $_imgopt_val = autoptimizeImages::instance()->get_img_quality_setting();
926
+
927
+ foreach ( $_imgopt_array as $key => $value ) {
928
+ echo '<option value="' . $key . '"';
929
+ if ( $_imgopt_val == $key ) {
930
+ echo ' selected';
931
+ }
932
+ echo '>' . ucfirst( $value ) . '</option>';
933
+ }
934
+ echo "\n";
935
+ ?>
936
+ </select>
937
+ </label>
938
+ <p>
939
+ <?php
940
+ // 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>' ) );
942
+ ?>
943
+ </p>
944
+ </td>
945
+ </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>
948
+ <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>
950
+ </td>
951
+ </tr>
952
+ <tr>
953
+ <th scope="row"><?php _e( 'Lazy-load images?', 'autoptimize' ); ?></th>
954
+ <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>
956
+ </td>
957
+ </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>
960
+ <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>
962
+ </td>
963
+ </tr>
964
+ </table>
965
+ <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
966
+ </form>
967
+ <script>
968
+ jQuery(document).ready(function() {
969
+ jQuery("#autoptimize_imgopt_checkbox").change(function() {
970
+ if (this.checked) {
971
+ jQuery("#autoptimize_imgopt_quality").show("slow");
972
+ jQuery("#autoptimize_imgopt_webp").show("slow");
973
+ } else {
974
+ jQuery("#autoptimize_imgopt_quality").hide("slow");
975
+ jQuery("#autoptimize_imgopt_webp").hide("slow");
976
+ }
977
+ });
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
+ jQuery("#autoptimize_imgopt_lazyload_checkbox").change(function() {
985
+ if (this.checked) {
986
+ jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
987
+ } else {
988
+ jQuery("#autoptimize_imgopt_lazyload_exclusions").hide("slow");
989
+ jQuery("#autoptimize_imgopt_webp_checkbox")[0].checked = false;
990
+ }
991
+ });
992
+ });
993
+ </script>
994
+ <?php
995
+ }
996
+
997
+ /**
998
+ * Ïmg opt status as used on dashboard.
999
+ */
1000
+ 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;
1006
+
1007
+ if ( is_array( $_stat ) ) {
1008
+ if ( 1 == $_stat['Status'] ) {
1009
+ // 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>' );
1011
+ } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1012
+ // 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' ) );
1018
+ 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'] );
1020
+ } else {
1021
+ $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1022
+ }
1023
+ $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1024
+ // 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>' );
1030
+ }
1031
+ $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
1032
+
1033
+ return array(
1034
+ 'status' => $_stat['Status'],
1035
+ 'notice' => $_imgopt_notice,
1036
+ );
1037
+ }
1038
+ }
1039
+ return false;
1040
+ }
1041
+
1042
+ public static function get_imgopt_status_notice_wrapper() {
1043
+ // needed for notice being shown in autoptimizeCacheChecker.php.
1044
+ $self = new self();
1045
+ return $self->get_imgopt_status_notice();
1046
+ }
1047
+
1048
+ /**
1049
+ * Get img provider stats (used to display notice).
1050
+ */
1051
+ public function query_img_provider_stats() {
1052
+ if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) ) {
1053
+ $url = '';
1054
+ $endpoint = $this->get_imgopt_host() . 'read-domain/';
1055
+ $domain = AUTOPTIMIZE_SITE_DOMAIN;
1056
+
1057
+ // make sure parse_url result makes sense, keeping $url empty if not.
1058
+ if ( $domain && ! empty( $domain ) ) {
1059
+ $url = $endpoint . $domain;
1060
+ }
1061
+
1062
+ $url = apply_filters(
1063
+ 'autoptimize_filter_imgopt_stat_url',
1064
+ $url
1065
+ );
1066
+
1067
+ // only do the remote call if $url is not empty to make sure no parse_url
1068
+ // weirdness results in useless calls.
1069
+ if ( ! empty( $url ) ) {
1070
+ $response = wp_remote_get( $url );
1071
+ if ( ! is_wp_error( $response ) ) {
1072
+ if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
1073
+ $stats = json_decode( wp_remote_retrieve_body( $response ), true );
1074
+ update_option( 'autoptimize_imgopt_provider_stat', $stats );
1075
+ }
1076
+ }
1077
+ }
1078
+ }
1079
+ }
1080
+
1081
+ public static function get_img_provider_stats()
1082
+ {
1083
+ // wrapper around query_img_provider_stats() so we can get to $this->options from cronjob() in autoptimizeCacheChecker.
1084
+ $self = new self();
1085
+ return $self->query_img_provider_stats();
1086
+ }
1087
+
1088
+ /**
1089
+ * Determines and returns the service launch status.
1090
+ *
1091
+ * @return bool
1092
+ */
1093
+ public function launch_ok()
1094
+ {
1095
+ static $launch_status = null;
1096
+
1097
+ if ( null === $launch_status ) {
1098
+ $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
1099
+ $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', '' );
1101
+ $launch_status = false;
1102
+ if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
1103
+ $launch_status = true;
1104
+ if ( ! $has_launched ) {
1105
+ update_option( 'autoptimize_imgopt_launched', 'on' );
1106
+ }
1107
+ }
1108
+ }
1109
+
1110
+ return $launch_status;
1111
+ }
1112
+
1113
+ public static function launch_ok_wrapper() {
1114
+ // needed for "plug" notice in autoptimizeMain.php.
1115
+ $self = new self();
1116
+ return $self->launch_ok();
1117
+ }
1118
+
1119
+ public function get_imgopt_provider_userstatus() {
1120
+ static $_provider_userstatus = null;
1121
+
1122
+ if ( is_null( $_provider_userstatus ) ) {
1123
+ $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
1124
+ if ( is_array( $_stat ) ) {
1125
+ if ( array_key_exists( 'Status', $_stat ) ) {
1126
+ $_provider_userstatus['Status'] = $_stat['Status'];
1127
+ } else {
1128
+ // if no stats then we assume all is well.
1129
+ $_provider_userstatus['Status'] = 2;
1130
+ }
1131
+ if ( array_key_exists( 'timestamp', $_stat ) ) {
1132
+ $_provider_userstatus['timestamp'] = $_stat['timestamp'];
1133
+ } else {
1134
+ // if no timestamp then we return "".
1135
+ $_provider_userstatus['timestamp'] = '';
1136
+ }
1137
+ }
1138
+ }
1139
+
1140
+ 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
+ }
1174
+ }
classes/autoptimizeMain.php CHANGED
@@ -50,7 +50,12 @@ class autoptimizeMain
50
 
51
  protected function add_hooks()
52
  {
53
- add_action( 'plugins_loaded', array( $this, 'setup' ) );
 
 
 
 
 
54
 
55
  add_action( 'autoptimize_setup_done', array( $this, 'version_upgrades_check' ) );
56
  add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) );
@@ -58,7 +63,6 @@ class autoptimizeMain
58
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_partners_tab' ) );
59
 
60
  add_action( 'init', array( $this, 'load_textdomain' ) );
61
- add_action( 'plugins_loaded', array( $this, 'hook_page_cache_purge' ) );
62
  add_action( 'admin_init', array( 'PAnD', 'init' ) );
63
 
64
  register_activation_hook( $this->filepath, array( $this, 'on_activate' ) );
@@ -154,7 +158,7 @@ class autoptimizeMain
154
  {
155
  if ( autoptimizeCache::cacheavail() ) {
156
  $conf = autoptimizeConfig::instance();
157
- if ( $conf->get( 'autoptimize_html' ) || $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) ) {
158
  // Hook into WordPress frontend.
159
  if ( defined( 'AUTOPTIMIZE_INIT_EARLIER' ) ) {
160
  add_action(
@@ -172,6 +176,11 @@ class autoptimizeMain
172
  self::DEFAULT_HOOK_PRIORITY
173
  );
174
  }
 
 
 
 
 
175
  }
176
  } else {
177
  add_action( 'admin_notices', 'autoptimizeMain::notice_cache_unavailable' );
@@ -181,6 +190,8 @@ class autoptimizeMain
181
  public function maybe_run_ao_extra()
182
  {
183
  if ( apply_filters( 'autoptimize_filter_extra_activate', true ) ) {
 
 
184
  $ao_extra = new autoptimizeExtra();
185
  $ao_extra->run();
186
 
@@ -378,6 +389,11 @@ class autoptimizeMain
378
  */
379
  public static function is_amp_markup( $content )
380
  {
 
 
 
 
 
381
  $is_amp_markup = preg_match( '/<html[^>]*(?:amp|⚡)/i', $content );
382
 
383
  return (bool) $is_amp_markup;
@@ -414,25 +430,27 @@ class autoptimizeMain
414
 
415
  $classoptions = array(
416
  'autoptimizeScripts' => array(
417
- 'aggregate' => $conf->get( 'autoptimize_js_aggregate' ),
418
- 'justhead' => $conf->get( 'autoptimize_js_justhead' ),
419
- 'forcehead' => $conf->get( 'autoptimize_js_forcehead' ),
420
- 'trycatch' => $conf->get( 'autoptimize_js_trycatch' ),
421
- 'js_exclude' => $conf->get( 'autoptimize_js_exclude' ),
422
- 'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
423
- 'include_inline' => $conf->get( 'autoptimize_js_include_inline' ),
 
424
  ),
425
  'autoptimizeStyles' => array(
426
- 'aggregate' => $conf->get( 'autoptimize_css_aggregate' ),
427
- 'justhead' => $conf->get( 'autoptimize_css_justhead' ),
428
- 'datauris' => $conf->get( 'autoptimize_css_datauris' ),
429
- 'defer' => $conf->get( 'autoptimize_css_defer' ),
430
- 'defer_inline' => $conf->get( 'autoptimize_css_defer_inline' ),
431
- 'inline' => $conf->get( 'autoptimize_css_inline' ),
432
- 'css_exclude' => $conf->get( 'autoptimize_css_exclude' ),
433
- 'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
434
- 'include_inline' => $conf->get( 'autoptimize_css_include_inline' ),
435
- 'nogooglefont' => $conf->get( 'autoptimize_css_nogooglefont' ),
 
436
  ),
437
  'autoptimizeHTML' => array(
438
  'keepcomments' => $conf->get( 'autoptimize_html_keepcomments' ),
@@ -492,6 +510,8 @@ class autoptimizeMain
492
  'autoptimize_service_availablity',
493
  'autoptimize_imgopt_provider_stat',
494
  'autoptimize_imgopt_launched',
 
 
495
  );
496
 
497
  if ( ! is_multisite() ) {
@@ -541,17 +561,11 @@ class autoptimizeMain
541
  public static function notice_plug_imgopt()
542
  {
543
  // Translators: the URL added points to the Autopmize Extra settings.
544
- $_ao_imgopt_plug_notice = sprintf( __( 'Did you know Autoptimize includes on-the-fly image optimization and CDN via ShortPixel? Check out the %1$sAutoptimize Extra settings%2$s to activate this option.', 'autoptimize' ), '<a href="options-general.php?page=autoptimize_extra">', '</a>' );
545
  $_ao_imgopt_plug_notice = apply_filters( 'autoptimize_filter_main_imgopt_plug_notice', $_ao_imgopt_plug_notice );
546
- $_ao_imgopt_launch_ok = autoptimizeExtra::imgopt_launch_ok_wrapper();
547
  $_ao_imgopt_plug_dismissible = 'ao-img-opt-plug-123';
548
-
549
- // check if AO is optimizing images already.
550
- $_ao_imgopt_active = false;
551
- $_ao_extra_options = get_option( 'autoptimize_extra_settings', '' );
552
- if ( is_array( $_ao_extra_options ) && array_key_exists( 'autoptimize_extra_checkbox_field_5', $_ao_extra_options ) && ! empty( $_ao_extra_options['autoptimize_extra_checkbox_field_5'] ) ) {
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>';
50
 
51
  protected function add_hooks()
52
  {
53
+ if ( ! defined( 'AUTOPTIMIZE_SETUP_INITHOOK' ) ) {
54
+ define( 'AUTOPTIMIZE_SETUP_INITHOOK', 'plugins_loaded' );
55
+ }
56
+
57
+ add_action( AUTOPTIMIZE_SETUP_INITHOOK, array( $this, 'setup' ) );
58
+ add_action( AUTOPTIMIZE_SETUP_INITHOOK, array( $this, 'hook_page_cache_purge' ) );
59
 
60
  add_action( 'autoptimize_setup_done', array( $this, 'version_upgrades_check' ) );
61
  add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) );
63
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_partners_tab' ) );
64
 
65
  add_action( 'init', array( $this, 'load_textdomain' ) );
 
66
  add_action( 'admin_init', array( 'PAnD', 'init' ) );
67
 
68
  register_activation_hook( $this->filepath, array( $this, 'on_activate' ) );
158
  {
159
  if ( autoptimizeCache::cacheavail() ) {
160
  $conf = autoptimizeConfig::instance();
161
+ if ( $conf->get( 'autoptimize_html' ) || $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) || autoptimizeImages::imgopt_active() || autoptimizeImages::should_lazyload_wrapper() ) {
162
  // Hook into WordPress frontend.
163
  if ( defined( 'AUTOPTIMIZE_INIT_EARLIER' ) ) {
164
  add_action(
176
  self::DEFAULT_HOOK_PRIORITY
177
  );
178
  }
179
+
180
+ // And disable Jetpack's site accelerator if JS or CSS opt. are active.
181
+ if ( class_exists( 'Jetpack' ) && apply_filters( 'autoptimize_filter_main_disable_jetpack_cdn', true ) && ( $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) ) ) {
182
+ add_filter( 'jetpack_force_disable_site_accelerator', '__return_true' );
183
+ }
184
  }
185
  } else {
186
  add_action( 'admin_notices', 'autoptimizeMain::notice_cache_unavailable' );
190
  public function maybe_run_ao_extra()
191
  {
192
  if ( apply_filters( 'autoptimize_filter_extra_activate', true ) ) {
193
+ $ao_imgopt = new autoptimizeImages();
194
+ $ao_imgopt->run();
195
  $ao_extra = new autoptimizeExtra();
196
  $ao_extra->run();
197
 
389
  */
390
  public static function is_amp_markup( $content )
391
  {
392
+ // Short-circuit when a function is available to determine whether the response is (or will be) an AMP page.
393
+ if ( function_exists( 'is_amp_endpoint' ) ) {
394
+ return is_amp_endpoint();
395
+ }
396
+
397
  $is_amp_markup = preg_match( '/<html[^>]*(?:amp|⚡)/i', $content );
398
 
399
  return (bool) $is_amp_markup;
430
 
431
  $classoptions = array(
432
  'autoptimizeScripts' => array(
433
+ 'aggregate' => $conf->get( 'autoptimize_js_aggregate' ),
434
+ 'justhead' => $conf->get( 'autoptimize_js_justhead' ),
435
+ 'forcehead' => $conf->get( 'autoptimize_js_forcehead' ),
436
+ 'trycatch' => $conf->get( 'autoptimize_js_trycatch' ),
437
+ 'js_exclude' => $conf->get( 'autoptimize_js_exclude' ),
438
+ 'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
439
+ 'include_inline' => $conf->get( 'autoptimize_js_include_inline' ),
440
+ 'minify_excluded' => $conf->get( 'autoptimize_minify_excluded' ),
441
  ),
442
  'autoptimizeStyles' => array(
443
+ 'aggregate' => $conf->get( 'autoptimize_css_aggregate' ),
444
+ 'justhead' => $conf->get( 'autoptimize_css_justhead' ),
445
+ 'datauris' => $conf->get( 'autoptimize_css_datauris' ),
446
+ 'defer' => $conf->get( 'autoptimize_css_defer' ),
447
+ 'defer_inline' => $conf->get( 'autoptimize_css_defer_inline' ),
448
+ 'inline' => $conf->get( 'autoptimize_css_inline' ),
449
+ 'css_exclude' => $conf->get( 'autoptimize_css_exclude' ),
450
+ 'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
451
+ 'include_inline' => $conf->get( 'autoptimize_css_include_inline' ),
452
+ 'nogooglefont' => $conf->get( 'autoptimize_css_nogooglefont' ),
453
+ 'minify_excluded' => $conf->get( 'autoptimize_minify_excluded' ),
454
  ),
455
  'autoptimizeHTML' => array(
456
  'keepcomments' => $conf->get( 'autoptimize_html_keepcomments' ),
510
  'autoptimize_service_availablity',
511
  'autoptimize_imgopt_provider_stat',
512
  'autoptimize_imgopt_launched',
513
+ 'autoptimize_imgopt_settings',
514
+ 'autoptimize_minify_excluded',
515
  );
516
 
517
  if ( ! is_multisite() ) {
561
  public static function notice_plug_imgopt()
562
  {
563
  // Translators: the URL added points to the Autopmize Extra settings.
564
+ $_ao_imgopt_plug_notice = sprintf( __( 'Did you know Autoptimize includes on-the-fly image optimization (with support for WebP) and CDN via ShortPixel? Check out the %1$sAutoptimize Image settings%2$s to activate this option.', 'autoptimize' ), '<a href="options-general.php?page=autoptimize_imgopt">', '</a>' );
565
  $_ao_imgopt_plug_notice = apply_filters( 'autoptimize_filter_main_imgopt_plug_notice', $_ao_imgopt_plug_notice );
566
+ $_ao_imgopt_launch_ok = autoptimizeImages::launch_ok_wrapper();
567
  $_ao_imgopt_plug_dismissible = 'ao-img-opt-plug-123';
568
+ $_ao_imgopt_active = autoptimizeImages::imgopt_active();
 
 
 
 
 
 
569
 
570
  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 ) ) {
571
  echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_imgopt_plug_dismissible . '"><p>';
classes/autoptimizeScripts.php CHANGED
@@ -39,8 +39,9 @@ class autoptimizeScripts extends autoptimizeBase
39
  private $whitelist = '';
40
  private $jsremovables = array();
41
  private $inject_min_late = '';
 
42
 
43
- // Reads the page and collects script tags
44
  public function read($options)
45
  {
46
  $noptimizeJS = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content );
@@ -54,7 +55,7 @@ class autoptimizeScripts extends autoptimizeBase
54
  $this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelistJS ) ) );
55
  }
56
 
57
- // is there JS we should simply remove
58
  $removableJS = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
59
  if (!empty($removableJS)) {
60
  $this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removableJS ) ) );
@@ -81,15 +82,20 @@ class autoptimizeScripts extends autoptimizeBase
81
  $this->include_inline = true;
82
  }
83
 
84
- // filter to "late inject minified JS", default to true for now (it is faster)
85
  $this->inject_min_late = apply_filters( 'autoptimize_filter_js_inject_min_late', true );
86
 
87
- // filters to override hardcoded do(nt)move(last) array contents (array in, array out!)
88
  $this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove );
89
  $this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast );
90
  $this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove );
91
 
92
- // get extra exclusions settings or filter
 
 
 
 
 
93
  $excludeJS = $options['js_exclude'];
94
  $excludeJS = apply_filters( 'autoptimize_filter_js_exclude', $excludeJS, $this->content );
95
 
@@ -122,22 +128,22 @@ class autoptimizeScripts extends autoptimizeBase
122
 
123
  $this->forcehead = apply_filters( 'autoptimize_filter_js_forcehead', $this->forcehead );
124
 
125
- // get cdn url
126
  $this->cdn_url = $options['cdn_url'];
127
 
128
- // noptimize me
129
  $this->content = $this->hide_noptimize($this->content);
130
 
131
- // Save IE hacks
132
  $this->content = $this->hide_iehacks($this->content);
133
 
134
- // comments
135
  $this->content = $this->hide_comments($this->content);
136
 
137
- // Get script files
138
  if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) {
139
  foreach( $matches[0] as $tag ) {
140
- // only consider script aggregation for types whitelisted in should_aggregate-function
141
  $should_aggregate = $this->should_aggregate($tag);
142
  if ( ! $should_aggregate ) {
143
  $tag = '';
@@ -145,7 +151,7 @@ class autoptimizeScripts extends autoptimizeBase
145
  }
146
 
147
  if ( preg_match( '#<script[^>]*src=("|\')([^>]*)("|\')#Usmi', $tag, $source ) ) {
148
- // non-inline script
149
  if ( $this->isremovable($tag, $this->jsremovables) ) {
150
  $this->content = str_replace( $tag, '', $this->content );
151
  continue;
@@ -155,13 +161,13 @@ class autoptimizeScripts extends autoptimizeBase
155
  $url = current( explode( '?', $source[2], 2 ) );
156
  $path = $this->getpath($url);
157
  if ( false !== $path && preg_match( '#\.js$#', $path ) && $this->ismergeable($tag) ) {
158
- // ok to optimize, add to array
159
  $this->scripts[] = $path;
160
  } else {
161
  $origTag = $tag;
162
  $newTag = $tag;
163
 
164
- // non-mergeable script (excluded or dynamic or external)
165
  if ( is_array( $excludeJS ) ) {
166
  // should we add flags?
167
  foreach ( $excludeJS as $exclTag => $exclFlags) {
@@ -172,39 +178,44 @@ class autoptimizeScripts extends autoptimizeBase
172
  }
173
 
174
  // Should we minify the non-aggregated script?
175
- if ( $path && apply_filters( 'autoptimize_filter_js_minify_excluded', true, $url ) ) {
176
- $minified_url = $this->minify_single( $path );
177
- // replace orig URL with minified URL from cache if so
178
- if ( ! empty( $minified_url ) ) {
179
- $newTag = str_replace( $url, $minified_url, $newTag );
 
 
 
 
 
180
  }
181
  }
182
 
183
  if ( $this->ismovable($newTag) ) {
184
- // can be moved, flags and all
185
  if ( $this->movetolast($newTag) ) {
186
  $this->move['last'][] = $newTag;
187
  } else {
188
  $this->move['first'][] = $newTag;
189
  }
190
  } else {
191
- // cannot be moved, so if flag was added re-inject altered tag immediately
192
  if ( $origTag !== $newTag ) {
193
  $this->content = str_replace( $origTag, $newTag, $this->content );
194
  $origTag = '';
195
  }
196
- // and forget about the $tag (not to be touched any more)
197
  $tag = '';
198
  }
199
  }
200
  } else {
201
- // Inline script
202
  if ( $this->isremovable($tag, $this->jsremovables) ) {
203
  $this->content = str_replace( $tag, '', $this->content );
204
  continue;
205
  }
206
 
207
- // unhide comments, as javascript may be wrapped in comment-tags for old times' sake
208
  $tag = $this->restore_comments($tag);
209
  if ( $this->ismergeable($tag) && $this->include_inline ) {
210
  preg_match( '#<script.*>(.*)</script>#Usmi', $tag , $code );
@@ -221,15 +232,15 @@ class autoptimizeScripts extends autoptimizeBase
221
  $this->move['first'][] = $tag;
222
  }
223
  } else {
224
- // We shouldn't touch this
225
  $tag = '';
226
  }
227
  }
228
- // Re-hide comments to be able to do the removal based on tag from $this->content
229
  $tag = $this->hide_comments($tag);
230
  }
231
 
232
- //Remove the original script tag
233
  $this->content = str_replace( $tag, '', $this->content );
234
  }
235
 
39
  private $whitelist = '';
40
  private $jsremovables = array();
41
  private $inject_min_late = '';
42
+ private $minify_excluded = true;
43
 
44
+ // Reads the page and collects script tags.
45
  public function read($options)
46
  {
47
  $noptimizeJS = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content );
55
  $this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelistJS ) ) );
56
  }
57
 
58
+ // is there JS we should simply remove?
59
  $removableJS = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
60
  if (!empty($removableJS)) {
61
  $this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removableJS ) ) );
82
  $this->include_inline = true;
83
  }
84
 
85
+ // filter to "late inject minified JS", default to true for now (it is faster).
86
  $this->inject_min_late = apply_filters( 'autoptimize_filter_js_inject_min_late', true );
87
 
88
+ // filters to override hardcoded do(nt)move(last) array contents (array in, array out!).
89
  $this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove );
90
  $this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast );
91
  $this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove );
92
 
93
+ // Determine whether excluded files should be minified if not yet so.
94
+ if ( ! $options['minify_excluded'] && $options['aggregate'] ) {
95
+ $this->minify_excluded = false;
96
+ }
97
+
98
+ // get extra exclusions settings or filter.
99
  $excludeJS = $options['js_exclude'];
100
  $excludeJS = apply_filters( 'autoptimize_filter_js_exclude', $excludeJS, $this->content );
101
 
128
 
129
  $this->forcehead = apply_filters( 'autoptimize_filter_js_forcehead', $this->forcehead );
130
 
131
+ // get cdn url.
132
  $this->cdn_url = $options['cdn_url'];
133
 
134
+ // noptimize me.
135
  $this->content = $this->hide_noptimize($this->content);
136
 
137
+ // Save IE hacks.
138
  $this->content = $this->hide_iehacks($this->content);
139
 
140
+ // comments.
141
  $this->content = $this->hide_comments($this->content);
142
 
143
+ // Get script files.
144
  if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) {
145
  foreach( $matches[0] as $tag ) {
146
+ // only consider script aggregation for types whitelisted in should_aggregate-function.
147
  $should_aggregate = $this->should_aggregate($tag);
148
  if ( ! $should_aggregate ) {
149
  $tag = '';
151
  }
152
 
153
  if ( preg_match( '#<script[^>]*src=("|\')([^>]*)("|\')#Usmi', $tag, $source ) ) {
154
+ // non-inline script.
155
  if ( $this->isremovable($tag, $this->jsremovables) ) {
156
  $this->content = str_replace( $tag, '', $this->content );
157
  continue;
161
  $url = current( explode( '?', $source[2], 2 ) );
162
  $path = $this->getpath($url);
163
  if ( false !== $path && preg_match( '#\.js$#', $path ) && $this->ismergeable($tag) ) {
164
+ // ok to optimize, add to array.
165
  $this->scripts[] = $path;
166
  } else {
167
  $origTag = $tag;
168
  $newTag = $tag;
169
 
170
+ // non-mergeable script (excluded or dynamic or external).
171
  if ( is_array( $excludeJS ) ) {
172
  // should we add flags?
173
  foreach ( $excludeJS as $exclTag => $exclFlags) {
178
  }
179
 
180
  // Should we minify the non-aggregated script?
181
+ // -> if aggregate is on and exclude minify is on
182
+ // -> if aggregate is off and the file is not in dontmove.
183
+ if ( $path && ( $this->minify_excluded || apply_filters( 'autoptimize_filter_js_minify_excluded', false, $url ) ) ) {
184
+ $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
185
+ if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
186
+ $minified_url = $this->minify_single( $path );
187
+ // replace orig URL with minified URL from cache if so.
188
+ if ( ! empty( $minified_url ) ) {
189
+ $newTag = str_replace( $url, $minified_url, $newTag );
190
+ }
191
  }
192
  }
193
 
194
  if ( $this->ismovable($newTag) ) {
195
+ // can be moved, flags and all.
196
  if ( $this->movetolast($newTag) ) {
197
  $this->move['last'][] = $newTag;
198
  } else {
199
  $this->move['first'][] = $newTag;
200
  }
201
  } else {
202
+ // cannot be moved, so if flag was added re-inject altered tag immediately.
203
  if ( $origTag !== $newTag ) {
204
  $this->content = str_replace( $origTag, $newTag, $this->content );
205
  $origTag = '';
206
  }
207
+ // and forget about the $tag (not to be touched any more).
208
  $tag = '';
209
  }
210
  }
211
  } else {
212
+ // Inline script.
213
  if ( $this->isremovable($tag, $this->jsremovables) ) {
214
  $this->content = str_replace( $tag, '', $this->content );
215
  continue;
216
  }
217
 
218
+ // unhide comments, as javascript may be wrapped in comment-tags for old times' sake.
219
  $tag = $this->restore_comments($tag);
220
  if ( $this->ismergeable($tag) && $this->include_inline ) {
221
  preg_match( '#<script.*>(.*)</script>#Usmi', $tag , $code );
232
  $this->move['first'][] = $tag;
233
  }
234
  } else {
235
+ // We shouldn't touch this.
236
  $tag = '';
237
  }
238
  }
239
+ // Re-hide comments to be able to do the removal based on tag from $this->content.
240
  $tag = $this->hide_comments($tag);
241
  }
242
 
243
+ //Remove the original script tag.
244
  $this->content = str_replace( $tag, '', $this->content );
245
  }
246
 
classes/autoptimizeStyles.php CHANGED
@@ -44,6 +44,7 @@ class autoptimizeStyles extends autoptimizeBase
44
  private $cssremovables = array();
45
  private $include_inline = false;
46
  private $inject_min_late = '';
 
47
 
48
  // public $cdn_url; // Used all over the place implicitly, so will have to be either public or protected :/ .
49
 
@@ -122,6 +123,11 @@ class autoptimizeStyles extends autoptimizeBase
122
  // Store data: URIs setting for later use.
123
  $this->datauris = $options['datauris'];
124
 
 
 
 
 
 
125
  // noptimize me.
126
  $this->content = $this->hide_noptimize( $this->content );
127
 
@@ -201,25 +207,30 @@ class autoptimizeStyles extends autoptimizeBase
201
  // Remove the original style tag.
202
  $this->content = str_replace( $tag, '', $this->content );
203
  } else {
204
- // Excluded CSS, minify if getpath and filter says so...
 
 
205
  if ( preg_match( '#<link.*href=("|\')(.*)("|\')#Usmi', $tag, $source ) ) {
206
  $exploded_url = explode( '?', $source[2], 2 );
207
  $url = $exploded_url[0];
208
  $path = $this->getpath( $url );
209
 
210
- if ( $path && apply_filters( 'autoptimize_filter_css_minify_excluded', true, $url ) ) {
211
- $minified_url = $this->minify_single( $path );
212
- if ( ! empty( $minified_url ) ) {
213
- // Replace orig URL with cached minified URL.
214
- $new_tag = str_replace( $url, $minified_url, $tag );
215
- } else {
216
- $new_tag = $tag;
217
- }
 
 
218
 
219
- $new_tag = $this->optionally_defer_excluded( $new_tag, $url );
220
 
221
- // And replace!
222
- $this->content = str_replace( $tag, $new_tag, $this->content );
 
223
  }
224
  }
225
  }
@@ -797,19 +808,7 @@ class autoptimizeStyles extends autoptimizeBase
797
  // Returns the content.
798
  public function getcontent()
799
  {
800
- // restore comments.
801
- $this->content = $this->restore_comments( $this->content );
802
-
803
- // restore IE hacks.
804
- $this->content = $this->restore_iehacks( $this->content );
805
-
806
- // restore (no)script.
807
- $this->content = $this->restore_marked_content( 'SCRIPT', $this->content );
808
-
809
- // Restore noptimize.
810
- $this->content = $this->restore_noptimize( $this->content );
811
-
812
- // Restore the full content.
813
  if ( ! empty( $this->restofcontent ) ) {
814
  $this->content .= $this->restofcontent;
815
  $this->restofcontent = '';
@@ -847,8 +846,9 @@ class autoptimizeStyles extends autoptimizeBase
847
  }
848
  }
849
  }
850
- $code_out = '<style type="text/css" id="aoatfcss" media="all">' . $defer_inline_code . '</style>';
851
- $this->inject_in_html( $code_out, $replaceTag );
 
852
  }
853
  }
854
 
@@ -874,16 +874,29 @@ class autoptimizeStyles extends autoptimizeBase
874
  if ( $this->defer ) {
875
  $preload_polyfill = autoptimizeConfig::get_ao_css_preload_polyfill();
876
  $noScriptCssBlock .= '</noscript>';
877
- $this->inject_in_html( $preloadCssBlock . $noScriptCssBlock, $replaceTag );
 
878
 
879
  // Adds preload polyfill at end of body tag.
880
  $this->inject_in_html(
881
  apply_filters( 'autoptimize_css_preload_polyfill', $preload_polyfill ),
882
- array( '</body>', 'before' )
883
  );
884
  }
885
  }
886
 
 
 
 
 
 
 
 
 
 
 
 
 
887
  // Return the modified stylesheet.
888
  return $this->content;
889
  }
44
  private $cssremovables = array();
45
  private $include_inline = false;
46
  private $inject_min_late = '';
47
+ private $minify_excluded = true;
48
 
49
  // public $cdn_url; // Used all over the place implicitly, so will have to be either public or protected :/ .
50
 
123
  // Store data: URIs setting for later use.
124
  $this->datauris = $options['datauris'];
125
 
126
+ // Determine whether excluded files should be minified if not yet so.
127
+ if ( ! $options['minify_excluded'] && $options['aggregate'] ) {
128
+ $this->minify_excluded = false;
129
+ }
130
+
131
  // noptimize me.
132
  $this->content = $this->hide_noptimize( $this->content );
133
 
207
  // Remove the original style tag.
208
  $this->content = str_replace( $tag, '', $this->content );
209
  } else {
210
+ // Excluded CSS, minify that file:
211
+ // -> if aggregate is on and exclude minify is on
212
+ // -> if aggregate is off and the file is not in dontmove.
213
  if ( preg_match( '#<link.*href=("|\')(.*)("|\')#Usmi', $tag, $source ) ) {
214
  $exploded_url = explode( '?', $source[2], 2 );
215
  $url = $exploded_url[0];
216
  $path = $this->getpath( $url );
217
 
218
+ if ( $path && ( $this->minify_excluded || apply_filters( 'autoptimize_filter_css_minify_excluded', false, $url ) ) ) {
219
+ $consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false );
220
+ if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
221
+ $minified_url = $this->minify_single( $path );
222
+ if ( ! empty( $minified_url ) ) {
223
+ // Replace orig URL with cached minified URL.
224
+ $new_tag = str_replace( $url, $minified_url, $tag );
225
+ } else {
226
+ $new_tag = $tag;
227
+ }
228
 
229
+ $new_tag = $this->optionally_defer_excluded( $new_tag, $url );
230
 
231
+ // And replace!
232
+ $this->content = str_replace( $tag, $new_tag, $this->content );
233
+ }
234
  }
235
  }
236
  }
808
  // Returns the content.
809
  public function getcontent()
810
  {
811
+ // Restore the full content (only applies when "autoptimize_filter_css_justhead" filter is true).
 
 
 
 
 
 
 
 
 
 
 
 
812
  if ( ! empty( $this->restofcontent ) ) {
813
  $this->content .= $this->restofcontent;
814
  $this->restofcontent = '';
846
  }
847
  }
848
  }
849
+ // inlined critical css set here, but injected when full CSS is injected
850
+ // to avoid CSS containing SVG with <title tag receiving the full CSS link.
851
+ $inlined_ccss_block = '<style type="text/css" id="aoatfcss" media="all">' . $defer_inline_code . '</style>';
852
  }
853
  }
854
 
874
  if ( $this->defer ) {
875
  $preload_polyfill = autoptimizeConfig::get_ao_css_preload_polyfill();
876
  $noScriptCssBlock .= '</noscript>';
877
+ // Inject inline critical CSS, the preloaded full CSS and the noscript-CSS.
878
+ $this->inject_in_html( $inlined_ccss_block . $preloadCssBlock . $noScriptCssBlock, $replaceTag );
879
 
880
  // Adds preload polyfill at end of body tag.
881
  $this->inject_in_html(
882
  apply_filters( 'autoptimize_css_preload_polyfill', $preload_polyfill ),
883
+ apply_filters( 'autoptimize_css_preload_polyfill_injectat', array( '</body>', 'before' ) )
884
  );
885
  }
886
  }
887
 
888
+ // restore comments.
889
+ $this->content = $this->restore_comments( $this->content );
890
+
891
+ // restore IE hacks.
892
+ $this->content = $this->restore_iehacks( $this->content );
893
+
894
+ // restore (no)script.
895
+ $this->content = $this->restore_marked_content( 'SCRIPT', $this->content );
896
+
897
+ // Restore noptimize.
898
+ $this->content = $this->restore_noptimize( $this->content );
899
+
900
  // Return the modified stylesheet.
901
  return $this->content;
902
  }
classes/autoptimizeToolbar.php CHANGED
@@ -133,7 +133,8 @@ class autoptimizeToolbar
133
  // Needed for the AJAX to work properly on the frontend.
134
  wp_localize_script( 'autoptimize-toolbar', 'autoptimize_ajax_object', array(
135
  'ajaxurl' => admin_url( 'admin-ajax.php' ),
136
- 'error_msg' => sprintf( __( 'Your Autoptimize cache might not have been purged successfully, please check on the <a href=%s>Autoptimize settings page</a>.', 'autoptimize' ), admin_url( 'options-general.php?page=autoptimize' ) . ' style="white-space:nowrap;"' ),
 
137
  'dismiss_msg' => __( 'Dismiss this notice.' ),
138
  'nonce' => wp_create_nonce( 'ao_delcache_nonce' ),
139
  ) );
133
  // Needed for the AJAX to work properly on the frontend.
134
  wp_localize_script( 'autoptimize-toolbar', 'autoptimize_ajax_object', array(
135
  'ajaxurl' => admin_url( 'admin-ajax.php' ),
136
+ // translators: links to the Autoptimize settings page.
137
+ 'error_msg' => sprintf( __( 'Your Autoptimize cache might not have been purged successfully, please check on the <a href="%s">Autoptimize settings page</a>.', 'autoptimize' ), admin_url( 'options-general.php?page=autoptimize' ) . ' style="white-space:nowrap;"' ),
138
  'dismiss_msg' => __( 'Dismiss this notice.' ),
139
  'nonce' => wp_create_nonce( 'ao_delcache_nonce' ),
140
  ) );
classes/autoptimizeUtils.php CHANGED
@@ -267,7 +267,7 @@ class autoptimizeUtils
267
  $result = false;
268
 
269
  if ( ! empty( $url ) ) {
270
- $result = ( '/' === $url{1} ); // second char is `/`.
271
  }
272
 
273
  return $result;
@@ -336,4 +336,24 @@ class autoptimizeUtils
336
 
337
  return $is_regex;
338
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  }
267
  $result = false;
268
 
269
  if ( ! empty( $url ) ) {
270
+ $result = ( 0 === strpos( $url, '//' ) );
271
  }
272
 
273
  return $result;
336
 
337
  return $is_regex;
338
  }
339
+
340
+ /**
341
+ * Returns true if a certain WP plugin is active/loaded.
342
+ *
343
+ * @param string $plugin_file Main plugin file.
344
+ *
345
+ * @return bool
346
+ */
347
+ public static function is_plugin_active( $plugin_file )
348
+ {
349
+ static $ipa_exists = null;
350
+ if ( null === $ipa_exists ) {
351
+ if ( ! function_exists( '\is_plugin_active' ) ) {
352
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
353
+ }
354
+ $ipa_exists = function_exists( '\is_plugin_active' );
355
+ }
356
+
357
+ return $ipa_exists && \is_plugin_active( $plugin_file );
358
+ }
359
  }
classes/autoptimizeVersionUpdatesHandler.php CHANGED
@@ -50,6 +50,7 @@ class autoptimizeVersionUpdatesHandler
50
  if ( get_option( 'autoptimize_version', 'none' ) == '2.4.2' ) {
51
  $this->upgrade_from_2_4_2();
52
  }
 
53
  $major_update = false;
54
  // No break, intentionally, so all upgrades are ran during a single request...
55
  }
@@ -230,4 +231,18 @@ class autoptimizeVersionUpdatesHandler
230
  // Save the data.
231
  _set_cron_array( $jobs );
232
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  }
50
  if ( get_option( 'autoptimize_version', 'none' ) == '2.4.2' ) {
51
  $this->upgrade_from_2_4_2();
52
  }
53
+ $this->upgrade_from_2_4();
54
  $major_update = false;
55
  // No break, intentionally, so all upgrades are ran during a single request...
56
  }
231
  // Save the data.
232
  _set_cron_array( $jobs );
233
  }
234
+
235
+ /**
236
+ * Migrate imgopt options from autoptimize_extra_settings to autoptimize_imgopt_settings
237
+ */
238
+ private function upgrade_from_2_4() {
239
+ $extra_settings = get_option( 'autoptimize_extra_settings' );
240
+ $imgopt_settings = get_option( 'autoptimize_imgopt_settings' );
241
+ if ( empty( $imgopt_settings ) ) {
242
+ $imgopt_settings = autoptimizeConfig::get_ao_imgopt_default_options();
243
+ $imgopt_settings['autoptimize_imgopt_checkbox_field_1'] = $extra_settings['autoptimize_extra_checkbox_field_5'];
244
+ $imgopt_settings['autoptimize_imgopt_select_field_2'] = $extra_settings['autoptimize_extra_select_field_6'];
245
+ update_option( 'autoptimize_imgopt_settings', $imgopt_settings );
246
+ }
247
+ }
248
  }
classes/external/js/lazysizes.min.js ADDED
@@ -0,0 +1,2 @@
 
 
1
+ /*! lazysizes - v4.1.7 */
2
+ !function(a,b){var c=b(a,a.document);a.lazySizes=c,"object"==typeof module&&module.exports&&(module.exports=c)}(window,function(a,b){"use strict";if(b.getElementsByClassName){var c,d,e=b.documentElement,f=a.Date,g=a.HTMLPictureElement,h="addEventListener",i="getAttribute",j=a[h],k=a.setTimeout,l=a.requestAnimationFrame||k,m=a.requestIdleCallback,n=/^picture$/i,o=["load","error","lazyincluded","_lazyloaded"],p={},q=Array.prototype.forEach,r=function(a,b){return p[b]||(p[b]=new RegExp("(\\s|^)"+b+"(\\s|$)")),p[b].test(a[i]("class")||"")&&p[b]},s=function(a,b){r(a,b)||a.setAttribute("class",(a[i]("class")||"").trim()+" "+b)},t=function(a,b){var c;(c=r(a,b))&&a.setAttribute("class",(a[i]("class")||"").replace(c," "))},u=function(a,b,c){var d=c?h:"removeEventListener";c&&u(a,b),o.forEach(function(c){a[d](c,b)})},v=function(a,d,e,f,g){var h=b.createEvent("Event");return e||(e={}),e.instance=c,h.initEvent(d,!f,!g),h.detail=e,a.dispatchEvent(h),h},w=function(b,c){var e;!g&&(e=a.picturefill||d.pf)?(c&&c.src&&!b[i]("srcset")&&b.setAttribute("srcset",c.src),e({reevaluate:!0,elements:[b]})):c&&c.src&&(b.src=c.src)},x=function(a,b){return(getComputedStyle(a,null)||{})[b]},y=function(a,b,c){for(c=c||a.offsetWidth;c<d.minSize&&b&&!a._lazysizesWidth;)c=b.offsetWidth,b=b.parentNode;return c},z=function(){var a,c,d=[],e=[],f=d,g=function(){var b=f;for(f=d.length?e:d,a=!0,c=!1;b.length;)b.shift()();a=!1},h=function(d,e){a&&!e?d.apply(this,arguments):(f.push(d),c||(c=!0,(b.hidden?k:l)(g)))};return h._lsFlush=g,h}(),A=function(a,b){return b?function(){z(a)}:function(){var b=this,c=arguments;z(function(){a.apply(b,c)})}},B=function(a){var b,c=0,e=d.throttleDelay,g=d.ricTimeout,h=function(){b=!1,c=f.now(),a()},i=m&&g>49?function(){m(h,{timeout:g}),g!==d.ricTimeout&&(g=d.ricTimeout)}:A(function(){k(h)},!0);return function(a){var d;(a=!0===a)&&(g=33),b||(b=!0,d=e-(f.now()-c),d<0&&(d=0),a||d<9?i():k(i,d))}},C=function(a){var b,c,d=99,e=function(){b=null,a()},g=function(){var a=f.now()-c;a<d?k(g,d-a):(m||e)(e)};return function(){c=f.now(),b||(b=k(g,d))}};!function(){var b,c={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:!0,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:!0,ricTimeout:0,throttleDelay:125};d=a.lazySizesConfig||a.lazysizesConfig||{};for(b in c)b in d||(d[b]=c[b]);a.lazySizesConfig=d,k(function(){d.init&&F()})}();var D=function(){var g,l,m,o,p,y,D,F,G,H,I,J,K=/^img$/i,L=/^iframe$/i,M="onscroll"in a&&!/(gle|ing)bot/.test(navigator.userAgent),N=0,O=0,P=0,Q=-1,R=function(a){P--,(!a||P<0||!a.target)&&(P=0)},S=function(a){return null==J&&(J="hidden"==x(b.body,"visibility")),J||"hidden"!=x(a.parentNode,"visibility")&&"hidden"!=x(a,"visibility")},T=function(a,c){var d,f=a,g=S(a);for(F-=c,I+=c,G-=c,H+=c;g&&(f=f.offsetParent)&&f!=b.body&&f!=e;)(g=(x(f,"opacity")||1)>0)&&"visible"!=x(f,"overflow")&&(d=f.getBoundingClientRect(),g=H>d.left&&G<d.right&&I>d.top-1&&F<d.bottom+1);return g},U=function(){var a,f,h,j,k,m,n,p,q,r,s,t,u=c.elements;if((o=d.loadMode)&&P<8&&(a=u.length)){for(f=0,Q++,r=!d.expand||d.expand<1?e.clientHeight>500&&e.clientWidth>500?500:370:d.expand,c._defEx=r,s=r*d.expFactor,t=d.hFac,J=null,O<s&&P<1&&Q>2&&o>2&&!b.hidden?(O=s,Q=0):O=o>1&&Q>1&&P<6?r:N;f<a;f++)if(u[f]&&!u[f]._lazyRace)if(M)if((p=u[f][i]("data-expand"))&&(m=1*p)||(m=O),q!==m&&(y=innerWidth+m*t,D=innerHeight+m,n=-1*m,q=m),h=u[f].getBoundingClientRect(),(I=h.bottom)>=n&&(F=h.top)<=D&&(H=h.right)>=n*t&&(G=h.left)<=y&&(I||H||G||F)&&(d.loadHidden||S(u[f]))&&(l&&P<3&&!p&&(o<3||Q<4)||T(u[f],m))){if(aa(u[f]),k=!0,P>9)break}else!k&&l&&!j&&P<4&&Q<4&&o>2&&(g[0]||d.preloadAfterLoad)&&(g[0]||!p&&(I||H||G||F||"auto"!=u[f][i](d.sizesAttr)))&&(j=g[0]||u[f]);else aa(u[f]);j&&!k&&aa(j)}},V=B(U),W=function(a){var b=a.target;if(b._lazyCache)return void delete b._lazyCache;R(a),s(b,d.loadedClass),t(b,d.loadingClass),u(b,Y),v(b,"lazyloaded")},X=A(W),Y=function(a){X({target:a.target})},Z=function(a,b){try{a.contentWindow.location.replace(b)}catch(c){a.src=b}},$=function(a){var b,c=a[i](d.srcsetAttr);(b=d.customMedia[a[i]("data-media")||a[i]("media")])&&a.setAttribute("media",b),c&&a.setAttribute("srcset",c)},_=A(function(a,b,c,e,f){var g,h,j,l,o,p;(o=v(a,"lazybeforeunveil",b)).defaultPrevented||(e&&(c?s(a,d.autosizesClass):a.setAttribute("sizes",e)),h=a[i](d.srcsetAttr),g=a[i](d.srcAttr),f&&(j=a.parentNode,l=j&&n.test(j.nodeName||"")),p=b.firesLoad||"src"in a&&(h||g||l),o={target:a},s(a,d.loadingClass),p&&(clearTimeout(m),m=k(R,2500),u(a,Y,!0)),l&&q.call(j.getElementsByTagName("source"),$),h?a.setAttribute("srcset",h):g&&!l&&(L.test(a.nodeName)?Z(a,g):a.src=g),f&&(h||l)&&w(a,{src:g})),a._lazyRace&&delete a._lazyRace,t(a,d.lazyClass),z(function(){(!p||a.complete&&a.naturalWidth>1)&&(W(o),a._lazyCache=!0,k(function(){"_lazyCache"in a&&delete a._lazyCache},9))},!0)}),aa=function(a){var b,c=K.test(a.nodeName),e=c&&(a[i](d.sizesAttr)||a[i]("sizes")),f="auto"==e;(!f&&l||!c||!a[i]("src")&&!a.srcset||a.complete||r(a,d.errorClass)||!r(a,d.lazyClass))&&(b=v(a,"lazyunveilread").detail,f&&E.updateElem(a,!0,a.offsetWidth),a._lazyRace=!0,P++,_(a,b,f,e,c))},ba=function(){if(!l){if(f.now()-p<999)return void k(ba,999);var a=C(function(){d.loadMode=3,V()});l=!0,d.loadMode=3,V(),j("scroll",function(){3==d.loadMode&&(d.loadMode=2),a()},!0)}};return{_:function(){p=f.now(),c.elements=b.getElementsByClassName(d.lazyClass),g=b.getElementsByClassName(d.lazyClass+" "+d.preloadClass),j("scroll",V,!0),j("resize",V,!0),a.MutationObserver?new MutationObserver(V).observe(e,{childList:!0,subtree:!0,attributes:!0}):(e[h]("DOMNodeInserted",V,!0),e[h]("DOMAttrModified",V,!0),setInterval(V,999)),j("hashchange",V,!0),["focus","mouseover","click","load","transitionend","animationend","webkitAnimationEnd"].forEach(function(a){b[h](a,V,!0)}),/d$|^c/.test(b.readyState)?ba():(j("load",ba),b[h]("DOMContentLoaded",V),k(ba,2e4)),c.elements.length?(U(),z._lsFlush()):V()},checkElems:V,unveil:aa}}(),E=function(){var a,c=A(function(a,b,c,d){var e,f,g;if(a._lazysizesWidth=d,d+="px",a.setAttribute("sizes",d),n.test(b.nodeName||""))for(e=b.getElementsByTagName("source"),f=0,g=e.length;f<g;f++)e[f].setAttribute("sizes",d);c.detail.dataAttr||w(a,c.detail)}),e=function(a,b,d){var e,f=a.parentNode;f&&(d=y(a,f,d),e=v(a,"lazybeforesizes",{width:d,dataAttr:!!b}),e.defaultPrevented||(d=e.detail.width)&&d!==a._lazysizesWidth&&c(a,f,e,d))},f=function(){var b,c=a.length;if(c)for(b=0;b<c;b++)e(a[b])},g=C(f);return{_:function(){a=b.getElementsByClassName(d.autosizesClass),j("resize",g)},checkElems:g,updateElem:e}}(),F=function(){F.i||(F.i=!0,E._(),D._())};return c={cfg:d,autoSizer:E,loader:D,init:F,uP:w,aC:s,rC:t,hC:r,fire:v,gW:y,rAF:z}}});
readme.txt CHANGED
@@ -1,17 +1,17 @@
1
  === Autoptimize ===
2
  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: 5.0
7
  Requires PHP: 5.3
8
- Stable tag: 2.4.4
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
 
12
  == Description ==
13
 
14
- Autoptimize makes optimizing your site really easy. It can aggregate, minify and cache scripts and styles, injects CSS in the page head by default (but can also defer), moves and defers scripts to the footer and minifies HTML. The "Extra" options allow you to optimize Google Fonts and images, async non-aggregated JavaScript, remove WordPress core emoji cruft and more. As such it can improve your site's performance even when already on HTTP/2! There is extensive API available to enable you to tailor Autoptimize to each and every site's specific needs.
15
 
16
  If you consider performance important, you really should use one of the many caching plugins to do page caching. Some good candidates to complement Autoptimize that way are e.g. [WP Super Cache](http://wordpress.org/plugins/wp-super-cache/), [HyperCache](http://wordpress.org/plugins/hyper-cache/), [Comet Cache](https://wordpress.org/plugins/comet-cache/) or [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler).
17
 
@@ -91,6 +91,10 @@ When clicking the "Delete Cache" link in the Autoptimize dropdown in the admin t
91
 
92
  Moreover don't worry if your cache never is down to 0 files/ 0KB, as Autoptimize (as from version 2.2) will automatically preload the cache immediately after it has been cleared to speed further minification significantly up.
93
 
 
 
 
 
94
  = Can I still use Cloudflare's Rocket Loader? =
95
 
96
  Cloudflare Rocket Loader is a pretty advanced but invasive way to make JavaScript non-render-blocking, which [Cloudflare still considers Beta](https://wordpress.org/support/topic/rocket-loader-breaking-onload-js-on-linked-css/#post-9263738). Sometimes Autoptimize & Rocket Loader work together, sometimes they don't. The best approach is to disable Rocket Loader, configure Autoptimize and re-enable Rocket Loader (if you think it can help) after that and test if everything still works.
@@ -99,7 +103,7 @@ At the moment (June 2017) it seems RocketLoader might break AO's "inline & defer
99
 
100
  = I tried Autoptimize but my Google Pagespeed Scored barely improved =
101
 
102
- Autoptimize is not a simple "fix my Pagespeed-problems" plugin; it "only" aggregates & minifies (local) JS & CSS and allows for some nice extra's as removing Google Fonts and deferring the loading of the CSS. As such Autoptimize will allow you to improve your performance (load time measured in seconds) and will probably also help you tackle some specific Pagespeed warnings. If you want to improve further, you will probably also have to look into e.g. page caching, image optimization and your webserver configuration, which will improve real performance (again, load time as measured by e.g. https://webpagetest.org) and your "performance best practise" pagespeed ratings.
103
 
104
  = What can I do with the API? =
105
 
@@ -139,13 +143,17 @@ After having installed and activated the plugin, you'll have access to an admin
139
 
140
  If your blog doesn't function normally after having turned on Autoptimize, here are some pointers to identify & solve such issues using "advanced settings":
141
 
142
- * If all works but you notice your blog is slower, ensure you have a page caching plugin installed (WP Super Cache or similar) and check the info on cache size (the solution for that problem also impacts performance for uncached pages) in this FAQ as well.
143
- * In case your blog looks weird, i.e. when the layout gets messed up, there is problem with CSS optimization. In this case you can turn on the option "Look for styles on just head?" and see if that solves the problem. You can also force CSS not to be aggregated by wrapping it in noptimize-tags in your theme or widget or by adding filename (for external stylesheets) or string (for inline styles) to the exclude-list.
144
  * In case some functionality on your site stops working (a carroussel, a menu, the search input, ...) you're likely hitting JavaScript optimization trouble. Change the "Aggregate inline JS" and/ or "Force JavaScript in head?" settings and try again. Excluding 'js/jquery/jquery.js' from optimization (see below) and optionally activating "[Add try/catch wrapping](http://blog.futtta.be/2014/08/18/when-should-you-trycatch-javascript/)") can also help. Alternatively -for the technically savvy- you can exclude specific scripts from being treated (moved and/ or aggregated) by Autoptimize by adding a string that will match the offending Javascript or excluding it from within your template files or widgets by wrapping the code between noptimize-tags. Identifying the offending JavaScript and choosing the correct exclusion-string can be trial and error, but in the majority of cases JavaScript optimization issues can be solved this way. When debugging JavaScript issues, your browsers error console is the most important tool to help you understand what is going on.
145
  * If your theme or plugin require jQuery, you can try either forcing all in head and/ or excluding jquery.js (and jQuery-plugins if needed).
146
  * If you can't get either CSS or JS optimization working, you can off course always continue using the other two optimization-techniques.
147
  * If you tried the troubleshooting tips above and you still can't get CSS and JS working at all, you can ask for support on the [WordPress Autoptimize support forum](http://wordpress.org/support/plugin/autoptimize). See below for a description of what information you should provide in your "trouble ticket"
148
 
 
 
 
 
149
  = Help, I have a blank page or an internal server error after enabling Autoptimize!! =
150
 
151
  Make sure you're not running other HTML, CSS or JS minification plugins (BWP minify, WP minify, ...) simultaneously with Autoptimize or disable that functionality your page caching plugin (W3 Total Cache, WP Fastest Cache, ...). Try enabling only CSS or only JS optimization to see which one causes the server error and follow the generic troubleshooting steps to find a workaround.
@@ -154,6 +162,12 @@ Make sure you're not running other HTML, CSS or JS minification plugins (BWP min
154
 
155
  If you are running Apache, the htaccess file written by Autoptimize can in some cases conflict with the AllowOverrides settings of your Apache configuration (as is the case with the default configuration of some Ubuntu installations), which results in "internal server errors" on the autoptimize CSS- and JS-files. This can be solved by [setting AllowOverrides to All](http://httpd.apache.org/docs/2.4/mod/core.html#allowoverride).
156
 
 
 
 
 
 
 
157
  = I get no error, but my pages are not optimized at all? =
158
 
159
  Autoptimize does a number of checks before actually optimizing. When one of the following is true, your pages won't be optimized:
@@ -217,7 +231,7 @@ Although some online performance assessement tools will single out "query string
217
 
218
  = (How) should I optimize Google Fonts? =
219
 
220
- Google Fonts are typically loaded by a "render blocking" linked CSS-file. If you have a theme and plugins that use Google Fonts, you might end up with multiple such CSS-files. Autoptimize (since version 2.3) now let's you lessen the impact of Google Fonts by either removing them alltogether or by optimizing the way they are loaded. There are two optimization-flavors; the first one is "combine and link", which replaces all requests for Google Fonts into one request, which will still be render-blocking but will allow the fonts to be loaded immediately (meaning you won't see fonts change while the page is loading). The alternative is "combine and load async" which uses JavaScript to load the fonts in a non-render blocking manner but which might cause a "flash of unstyled text". Starting form Autopitimize 2.4 "aggregate & preload" allows to aggregate all Google Font-files in one CSS-file that is preloaded, which should not be considered render blocking, but the fonts are available sooner (so less of a flash of unstyled text).
221
 
222
  = Should I use "preconnect" =
223
 
@@ -235,14 +249,6 @@ When image optimization is on, Autoptimize will look for png, gif, jpeg (.jpg) f
235
 
236
  Have a look at [Shortpixel's FAQ](https://shortpixel.helpscoutdocs.com/category/60-shortpixel-ai-cdn).
237
 
238
- = Can I disable the minification of excluded JS/ CSS? =
239
-
240
- As from AO 2.4 excluded JS/ CSS are minified if the (filename indicates the) file is not minified yet. You can disable this with these filters;
241
-
242
- `
243
- add_filter('autoptimize_filter_js_minify_excluded','__return_false');
244
- add_filter('autoptimize_filter_css_minify_excluded','__return_false');`
245
-
246
  = Can I disable AO listening to page cache purges? =
247
 
248
  As from AO 2.4 AO "listens" to page cache purges to clear its own cache. You can disable this behavior with this filter;
@@ -261,12 +267,6 @@ By default AO uses non multibyte-safe string methods, but if your PHP has the mb
261
  `
262
  add_filter('autoptimize_filter_main_use_mbstring', '__return_true');`
263
 
264
- = The Shortpixel image optimizing notice cannot be dismissed? =
265
-
266
- In some rare cases the logic to dismiss the notice does not work due to the transient (WordPress cache) not keeping the dismissed state. If this happens you can use this code snippet to make the notice go away;
267
-
268
- `add_filter( 'autoptimize_filter_main_imgopt_plug_notice', '__return_empty_string' );`
269
-
270
  = Where can I get help? =
271
 
272
  You can get help on the [wordpress.org support forum](http://wordpress.org/support/plugin/autoptimize). If you are 100% sure this your problem cannot be solved using Autoptimize configuration and that you in fact discovered a bug in the code, you can [create an issue on GitHub](https://github.com/futtta/autoptimize/issues). If you're looking for premium support, check out our [Autoptimize Pro Support and Web Performance Optimization services](http://autoptimize.com/).
@@ -283,6 +283,13 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
283
 
284
  == Changelog ==
285
 
 
 
 
 
 
 
 
286
  = 2.4.4 =
287
  * bugfix: safer way of removing extra cronjobs
288
 
1
  === Autoptimize ===
2
  Contributors: futtta, optimizingmatters, zytzagoo, turl
3
+ Tags: optimize, minify, performance, pagespeed, images, google fonts
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.2
7
  Requires PHP: 5.3
8
+ Stable tag: 2.5.0
9
 
10
+ Autoptimize speeds up your website by optimizing JS, CSS, images, HTML, Google Fonts and async-ing JS, removing emoji cruft and more.
11
 
12
  == Description ==
13
 
14
+ Autoptimize makes optimizing your site really easy. It can aggregate, minify and cache scripts and styles, injects CSS in the page head by default but can also inline critical CSS and defer the aggregated full CSS, moves and defers scripts to the footer and minifies HTML. You can optimize and lazyload images, optimize Google Fonts, async non-aggregated JavaScript, remove WordPress core emoji cruft and more. As such it can improve your site's performance even when already on HTTP/2! There is extensive API available to enable you to tailor Autoptimize to each and every site's specific needs.
15
 
16
  If you consider performance important, you really should use one of the many caching plugins to do page caching. Some good candidates to complement Autoptimize that way are e.g. [WP Super Cache](http://wordpress.org/plugins/wp-super-cache/), [HyperCache](http://wordpress.org/plugins/hyper-cache/), [Comet Cache](https://wordpress.org/plugins/comet-cache/) or [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler).
17
 
91
 
92
  Moreover don't worry if your cache never is down to 0 files/ 0KB, as Autoptimize (as from version 2.2) will automatically preload the cache immediately after it has been cleared to speed further minification significantly up.
93
 
94
+ = My site looks broken when I purge Autoptimize's cache! =
95
+
96
+ When clearing AO's cache, no page cache should contain pages (HTML) that refers to the removed optimized CSS/ JS. Although for that purpose there is integretion between Autoptimize and some page caches, this integration does not cover 100% of setups so you might need to purge your page cache manually.
97
+
98
  = Can I still use Cloudflare's Rocket Loader? =
99
 
100
  Cloudflare Rocket Loader is a pretty advanced but invasive way to make JavaScript non-render-blocking, which [Cloudflare still considers Beta](https://wordpress.org/support/topic/rocket-loader-breaking-onload-js-on-linked-css/#post-9263738). Sometimes Autoptimize & Rocket Loader work together, sometimes they don't. The best approach is to disable Rocket Loader, configure Autoptimize and re-enable Rocket Loader (if you think it can help) after that and test if everything still works.
103
 
104
  = I tried Autoptimize but my Google Pagespeed Scored barely improved =
105
 
106
+ Autoptimize is not a simple "fix my Pagespeed-problems" plugin; it "only" aggregates & minifies (local) JS & CSS and images and allows for some nice extra's as removing Google Fonts and deferring the loading of the CSS. As such Autoptimize will allow you to improve your performance (load time measured in seconds) and will probably also help you tackle some specific Pagespeed warnings. If you want to improve further, you will probably also have to look into e.g. page caching and your webserver configuration, which will improve real performance (again, load time as measured by e.g. https://webpagetest.org) and your "performance best practise" pagespeed ratings.
107
 
108
  = What can I do with the API? =
109
 
143
 
144
  If your blog doesn't function normally after having turned on Autoptimize, here are some pointers to identify & solve such issues using "advanced settings":
145
 
146
+ * If all works but you notice your blog is slower, ensure you have a page caching plugin installed (WP Super Cache or similar) and check the info on cache size (the soution for that problem also impacts performance for uncached pages) in this FAQ as well.
147
+ * In case your blog looks weird, i.e. when the layout gets messed up, there is problem with CSS optimization. Try excluding one or more CSS-files from being optimized. You can also force CSS not to be aggregated by wrapping it in noptimize-tags in your theme or widget or by adding filename (for external stylesheets) or string (for inline styles) to the exclude-list.
148
  * In case some functionality on your site stops working (a carroussel, a menu, the search input, ...) you're likely hitting JavaScript optimization trouble. Change the "Aggregate inline JS" and/ or "Force JavaScript in head?" settings and try again. Excluding 'js/jquery/jquery.js' from optimization (see below) and optionally activating "[Add try/catch wrapping](http://blog.futtta.be/2014/08/18/when-should-you-trycatch-javascript/)") can also help. Alternatively -for the technically savvy- you can exclude specific scripts from being treated (moved and/ or aggregated) by Autoptimize by adding a string that will match the offending Javascript or excluding it from within your template files or widgets by wrapping the code between noptimize-tags. Identifying the offending JavaScript and choosing the correct exclusion-string can be trial and error, but in the majority of cases JavaScript optimization issues can be solved this way. When debugging JavaScript issues, your browsers error console is the most important tool to help you understand what is going on.
149
  * If your theme or plugin require jQuery, you can try either forcing all in head and/ or excluding jquery.js (and jQuery-plugins if needed).
150
  * If you can't get either CSS or JS optimization working, you can off course always continue using the other two optimization-techniques.
151
  * If you tried the troubleshooting tips above and you still can't get CSS and JS working at all, you can ask for support on the [WordPress Autoptimize support forum](http://wordpress.org/support/plugin/autoptimize). See below for a description of what information you should provide in your "trouble ticket"
152
 
153
+ = I excluded files but they are still being autoptimized? =
154
+
155
+ AO minifies excluded JS/ CSS if the filename indicates the file is not minified yet. As of AO 2.5 you can disable this on the "JS, CSS & HTML"-tab under misc. options by unticking "minify excluded files".
156
+
157
  = Help, I have a blank page or an internal server error after enabling Autoptimize!! =
158
 
159
  Make sure you're not running other HTML, CSS or JS minification plugins (BWP minify, WP minify, ...) simultaneously with Autoptimize or disable that functionality your page caching plugin (W3 Total Cache, WP Fastest Cache, ...). Try enabling only CSS or only JS optimization to see which one causes the server error and follow the generic troubleshooting steps to find a workaround.
162
 
163
  If you are running Apache, the htaccess file written by Autoptimize can in some cases conflict with the AllowOverrides settings of your Apache configuration (as is the case with the default configuration of some Ubuntu installations), which results in "internal server errors" on the autoptimize CSS- and JS-files. This can be solved by [setting AllowOverrides to All](http://httpd.apache.org/docs/2.4/mod/core.html#allowoverride).
164
 
165
+ = Can't log in on domain mapped multisites =
166
+
167
+ Domain mapped multisites require Autoptimize to be initialized at a different WordPress action, add this line of code to your wp-config.php to make it so to hook into `setup_theme` for example:
168
+
169
+ `define( 'AUTOPTIMIZE_SETUP_INITHOOK', 'setup_theme' );`
170
+
171
  = I get no error, but my pages are not optimized at all? =
172
 
173
  Autoptimize does a number of checks before actually optimizing. When one of the following is true, your pages won't be optimized:
231
 
232
  = (How) should I optimize Google Fonts? =
233
 
234
+ Google Fonts are typically loaded by a "render blocking" linked CSS-file. If you have a theme and plugins that use Google Fonts, you might end up with multiple such CSS-files. Autoptimize (since version 2.3) now let's you lessen the impact of Google Fonts by either removing them alltogether or by optimizing the way they are loaded. There are two optimization-flavors; the first one is "combine and link", which replaces all requests for Google Fonts into one request, which will still be render-blocking but will allow the fonts to be loaded immediately (meaning you won't see fonts change while the page is loading). The alternative is "combine and load async" which uses JavaScript to load the fonts in a non-render blocking manner but which might cause a "flash of unstyled text".
235
 
236
  = Should I use "preconnect" =
237
 
249
 
250
  Have a look at [Shortpixel's FAQ](https://shortpixel.helpscoutdocs.com/category/60-shortpixel-ai-cdn).
251
 
 
 
 
 
 
 
 
 
252
  = Can I disable AO listening to page cache purges? =
253
 
254
  As from AO 2.4 AO "listens" to page cache purges to clear its own cache. You can disable this behavior with this filter;
267
  `
268
  add_filter('autoptimize_filter_main_use_mbstring', '__return_true');`
269
 
 
 
 
 
 
 
270
  = Where can I get help? =
271
 
272
  You can get help on the [wordpress.org support forum](http://wordpress.org/support/plugin/autoptimize). If you are 100% sure this your problem cannot be solved using Autoptimize configuration and that you in fact discovered a bug in the code, you can [create an issue on GitHub](https://github.com/futtta/autoptimize/issues). If you're looking for premium support, check out our [Autoptimize Pro Support and Web Performance Optimization services](http://autoptimize.com/).
283
 
284
  == Changelog ==
285
 
286
+ = 2.5.0 =
287
+ * moved image optimization to a separate tab and move all code to a separate file.
288
+ * added lazyloading (using lazysizes)
289
+ * added webp support (requires image optimization and lazyloading to be active)
290
+ * added option to enable/ disable the minification of excluded JS/ CSS files (on by default)
291
+ * misc. bugfixes and smaller improvements
292
+
293
  = 2.4.4 =
294
  * bugfix: safer way of removing extra cronjobs
295