Autoptimize - Version 2.6.0

Version Description

  • New: Autoptimize can be configured at network level or at individual site-level when on multisite.
  • Extra: new option to specify what resources need to be preloaded.
  • Extra: add display=swap to Autoptimized (CSS-based) Google Fonts.
  • Images: support for lazyloading of background-images when set in the inline style attribute of a div.
  • Images: updated to lazysizes 5.2.
  • CSS/ JS: no longer add type attributes to Autoptimized resources.
  • Improvement: cache clearing now also integrates with Kinsta, WP-Optimize & Nginx helper.
  • Added "Critical CSS" tab to highlight the criticalcss.com integration, which will be fully included in Autoptimize 2.7.
  • Batch of misc. smaller improvements & fixes, more info in the GitHub commit log.
Download this release

Release Info

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

Code changes from version 2.5.1 to 2.6.0

autoptimize.php CHANGED
@@ -1,15 +1,15 @@
1
  <?php
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.1
7
- Author: Frank Goossens (futtta)
8
- Author URI: https://autoptimize.com/
9
- Text Domain: autoptimize
10
- Released under the GNU General Public License (GPL)
11
- http://www.gnu.org/licenses/gpl.txt
12
- */
13
 
14
  /**
15
  * Autoptimize main plugin file.
@@ -20,16 +20,16 @@ if ( ! defined( 'ABSPATH' ) ) {
20
  exit;
21
  }
22
 
23
- define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.5.1' );
24
 
25
  // plugin_dir_path() returns the trailing slash!
26
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
27
  define( 'AUTOPTIMIZE_PLUGIN_FILE', __FILE__ );
28
 
29
  // Bail early if attempting to run on non-supported php versions.
30
- if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
31
  function autoptimize_incompatible_admin_notice() {
32
- echo '<div class="error"><p>' . __( 'Autoptimize requires PHP 5.3 (or higher) to function properly. Please upgrade PHP. The Plugin has been auto-deactivated.', 'autoptimize' ) . '</p></div>';
33
  if ( isset( $_GET['activate'] ) ) {
34
  unset( $_GET['activate'] );
35
  }
1
  <?php
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.6.0
7
+ * Author: Frank Goossens (futtta)
8
+ * Author URI: https://autoptimize.com/
9
+ * Text Domain: autoptimize
10
+ * Released under the GNU General Public License (GPL)
11
+ * http://www.gnu.org/licenses/gpl.txt
12
+ */
13
 
14
  /**
15
  * Autoptimize main plugin file.
20
  exit;
21
  }
22
 
23
+ define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.6.0' );
24
 
25
  // plugin_dir_path() returns the trailing slash!
26
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
27
  define( 'AUTOPTIMIZE_PLUGIN_FILE', __FILE__ );
28
 
29
  // Bail early if attempting to run on non-supported php versions.
30
+ if ( version_compare( PHP_VERSION, '5.6', '<' ) ) {
31
  function autoptimize_incompatible_admin_notice() {
32
+ echo '<div class="error"><p>' . __( 'Autoptimize requires PHP 5.6 (or higher) to function properly. Please upgrade PHP. The Plugin has been auto-deactivated.', 'autoptimize' ) . '</p></div>';
33
  if ( isset( $_GET['activate'] ) ) {
34
  unset( $_GET['activate'] );
35
  }
classes/autoptimizeBase.php CHANGED
@@ -23,7 +23,11 @@ abstract class autoptimizeBase
23
  */
24
  public $debug_log = false;
25
 
26
- /** @var string */
 
 
 
 
27
  public $cdn_url = '';
28
 
29
  public function __construct( $content )
@@ -302,7 +306,7 @@ abstract class autoptimizeBase
302
 
303
  // Simple str_replace-based approach fails when $url is protocol-or-host-relative.
304
  $is_protocol_relative = autoptimizeUtils::is_protocol_relative( $url );
305
- $is_host_relative = ( ! $is_protocol_relative && ( '/' === $url{0} ) );
306
  $cdn_url = rtrim( $cdn_url, '/' );
307
 
308
  if ( $is_host_relative ) {
@@ -639,9 +643,9 @@ abstract class autoptimizeBase
639
  protected function prepare_minify_single( $filepath )
640
  {
641
  // Decide what we're dealing with, return false if we don't know.
642
- if ( $this->str_ends_in( $filepath, '.js' ) ) {
643
  $type = 'js';
644
- } elseif ( $this->str_ends_in( $filepath, '.css' ) ) {
645
  $type = 'css';
646
  } else {
647
  return false;
@@ -655,7 +659,7 @@ abstract class autoptimizeBase
655
  'js/jquery/jquery.js',
656
  );
657
  foreach ( $minified_variants as $ending ) {
658
- if ( $this->str_ends_in( $filepath, $ending ) ) {
659
  return false;
660
  }
661
  }
@@ -683,24 +687,4 @@ abstract class autoptimizeBase
683
 
684
  return $url;
685
  }
686
-
687
- /**
688
- * Returns true if given $str ends with given $test.
689
- *
690
- * @param string $str String to check.
691
- * @param string $test Ending to match.
692
- *
693
- * @return bool
694
- */
695
- protected function str_ends_in( $str, $test )
696
- {
697
- // @codingStandardsIgnoreStart
698
- // substr_compare() is bugged on 5.5.11: https://3v4l.org/qGYBH
699
- // return ( 0 === substr_compare( $str, $test, -strlen( $test ) ) );
700
- // @codingStandardsIgnoreEnd
701
-
702
- $length = strlen( $test );
703
-
704
- return ( substr( $str, -$length, $length ) === $test );
705
- }
706
  }
23
  */
24
  public $debug_log = false;
25
 
26
+ /**
27
+ * Initiated $cdn_url.
28
+ *
29
+ * @var string
30
+ */
31
  public $cdn_url = '';
32
 
33
  public function __construct( $content )
306
 
307
  // Simple str_replace-based approach fails when $url is protocol-or-host-relative.
308
  $is_protocol_relative = autoptimizeUtils::is_protocol_relative( $url );
309
+ $is_host_relative = ( ! $is_protocol_relative && ( '/' === $url[0] ) );
310
  $cdn_url = rtrim( $cdn_url, '/' );
311
 
312
  if ( $is_host_relative ) {
643
  protected function prepare_minify_single( $filepath )
644
  {
645
  // Decide what we're dealing with, return false if we don't know.
646
+ if ( autoptimizeUtils::str_ends_in( $filepath, '.js' ) ) {
647
  $type = 'js';
648
+ } elseif ( autoptimizeUtils::str_ends_in( $filepath, '.css' ) ) {
649
  $type = 'css';
650
  } else {
651
  return false;
659
  'js/jquery/jquery.js',
660
  );
661
  foreach ( $minified_variants as $ending ) {
662
+ if ( autoptimizeUtils::str_ends_in( $filepath, $ending ) ) {
663
  return false;
664
  }
665
  }
687
 
688
  return $url;
689
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
690
  }
classes/autoptimizeCache.php CHANGED
@@ -109,6 +109,16 @@ class autoptimizeCache
109
  } else {
110
  // Write code to cache without doing anything else.
111
  file_put_contents( $this->cachedir . $this->filename, $data );
 
 
 
 
 
 
 
 
 
 
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 ) );
@@ -207,6 +217,10 @@ class autoptimizeCache
207
  * cache directory into a new one with a unique name and then
208
  * re-creating the default (empty) cache directory.
209
  *
 
 
 
 
210
  * @return bool Returns true when everything is done successfully, false otherwise.
211
  */
212
  protected static function clear_cache_via_rename()
@@ -260,7 +274,7 @@ class autoptimizeCache
260
  {
261
  $pathname = self::get_pathname_base();
262
  $basename = basename( $pathname );
263
- $prefix = $basename . '-';
264
 
265
  return $prefix;
266
  }
@@ -287,6 +301,11 @@ class autoptimizeCache
287
  */
288
  public static function delete_advanced_cache_clear_artifacts()
289
  {
 
 
 
 
 
290
  $dir = self::get_pathname_base();
291
  $prefix = self::get_advanced_cache_clear_prefix();
292
  $parent = dirname( $dir );
@@ -360,6 +379,12 @@ class autoptimizeCache
360
  self::clear_cache_classic();
361
  }
362
 
 
 
 
 
 
 
363
  // Remove the transient so it gets regenerated...
364
  delete_transient( 'autoptimize_stats' );
365
 
@@ -378,6 +403,7 @@ class autoptimizeCache
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 );
383
  }
@@ -562,13 +588,54 @@ class autoptimizeCache
562
  </Files>
563
  </IfModule>';
564
  }
 
 
 
 
565
  @file_put_contents( $htaccess, $content ); // @codingStandardsIgnoreLine
566
  }
567
 
 
 
 
 
568
  // All OK!
569
  return true;
570
  }
571
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
  /**
573
  * Checks if cache dirs exist and create if not.
574
  * Returns false if not succesful.
@@ -672,6 +739,17 @@ class autoptimizeCache
672
  }
673
  } elseif ( function_exists( 'sg_cachepress_purge_cache' ) ) {
674
  sg_cachepress_purge_cache();
 
 
 
 
 
 
 
 
 
 
 
675
  } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
676
  // fallback for WP-Super-Cache
677
  global $cache_path;
109
  } else {
110
  // Write code to cache without doing anything else.
111
  file_put_contents( $this->cachedir . $this->filename, $data );
112
+
113
+ // save fallback .js or .css file if filter true (to be false by default) but not if snippet or single.
114
+ if ( self::do_fallback() && strpos( $this->filename, '_snippet_' ) === false && strpos( $this->filename, '_single_' ) === false ) {
115
+ $_extension = pathinfo( $this->filename, PATHINFO_EXTENSION );
116
+ $_fallback_file = AUTOPTIMIZE_CACHEFILE_PREFIX . 'fallback.' . $_extension;
117
+ if ( ! file_exists( $this->cachedir . $_extension . '/' . $_fallback_file ) ) {
118
+ file_put_contents( $this->cachedir . $_extension . '/' . $_fallback_file, $data );
119
+ }
120
+ }
121
+
122
  if ( apply_filters( 'autoptimize_filter_cache_create_static_gzip', false ) ) {
123
  // Create an additional cached gzip file.
124
  file_put_contents( $this->cachedir . $this->filename . '.gz', gzencode( $data, 9, FORCE_GZIP ) );
217
  * cache directory into a new one with a unique name and then
218
  * re-creating the default (empty) cache directory.
219
  *
220
+ * Important/ Fixme: this does not take multisite into account, so
221
+ * if advanced_cache_clear_enabled is true (it is not by default)
222
+ * then the content for all subsites is zapped!
223
+ *
224
  * @return bool Returns true when everything is done successfully, false otherwise.
225
  */
226
  protected static function clear_cache_via_rename()
274
  {
275
  $pathname = self::get_pathname_base();
276
  $basename = basename( $pathname );
277
+ $prefix = $basename . '-artifact-';
278
 
279
  return $prefix;
280
  }
301
  */
302
  public static function delete_advanced_cache_clear_artifacts()
303
  {
304
+ // Don't go through these motions (called from the cachechecker) if advanced cache clear isn't even active.
305
+ if ( ! self::advanced_cache_clear_enabled() ) {
306
+ return false;
307
+ }
308
+
309
  $dir = self::get_pathname_base();
310
  $prefix = self::get_advanced_cache_clear_prefix();
311
  $parent = dirname( $dir );
379
  self::clear_cache_classic();
380
  }
381
 
382
+ // Remove 404 handler if required.
383
+ if ( self::do_fallback() ) {
384
+ $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php';
385
+ @unlink( $_fallback_php ); // @codingStandardsIgnoreLine
386
+ }
387
+
388
  // Remove the transient so it gets regenerated...
389
  delete_transient( 'autoptimize_stats' );
390
 
403
  if ( apply_filters( 'autoptimize_filter_speedupper', true ) && false == get_transient( 'autoptimize_cache_warmer_protector' ) ) {
404
  set_transient( 'autoptimize_cache_warmer_protector', 'I shall not warm cache for another 10 minutes.', 60 * 10 );
405
  $url = site_url() . '/?ao_speedup_cachebuster=' . rand( 1, 100000 );
406
+ $url = apply_filters( 'autoptimize_filter_cache_warmer_url', $url );
407
  $cache = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
408
  unset( $cache );
409
  }
588
  </Files>
589
  </IfModule>';
590
  }
591
+
592
+ if ( self::do_fallback() ) {
593
+ $content .= "\nErrorDocument 404 " . trailingslashit( parse_url( content_url(), PHP_URL_PATH ) ) . 'autoptimize_404_handler.php';
594
+ }
595
  @file_put_contents( $htaccess, $content ); // @codingStandardsIgnoreLine
596
  }
597
 
598
+ if ( self::do_fallback() ) {
599
+ self::check_fallback_php();
600
+ }
601
+
602
  // All OK!
603
  return true;
604
  }
605
 
606
+ /**
607
+ * Checks if fallback-php file exists and create it if not.
608
+ *
609
+ * Return bool
610
+ */
611
+ public static function check_fallback_php() {
612
+ $_fallback_filename = 'autoptimize_404_handler.php';
613
+ $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . $_fallback_filename;
614
+ $_fallback_status = true;
615
+
616
+ if ( ! file_exists( $_fallback_php ) ) {
617
+ $_fallback_php_contents = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $_fallback_filename );
618
+ $_fallback_php_contents = str_replace( '<?php exit;', '<?php', $_fallback_php_contents );
619
+ $_fallback_php_contents = str_replace( '<!--ao-cache-dir-->', AUTOPTIMIZE_CACHE_DIR, $_fallback_php_contents );
620
+ if ( apply_filters( 'autoptimize_filter_cache_fallback_log_errors', false ) ) {
621
+ $_fallback_php_contents = str_replace( '// error_log', 'error_log', $_fallback_php_contents );
622
+ }
623
+ $_fallback_status = file_put_contents( $_fallback_php, $_fallback_php_contents );
624
+ }
625
+
626
+ return $_fallback_status;
627
+ }
628
+
629
+ /**
630
+ * Tells if AO should try to avoid 404's by creating fallback filesize
631
+ * and create a php 404 handler and tell .htaccess to redirect to said handler.
632
+ *
633
+ * Return bool
634
+ */
635
+ public static function do_fallback() {
636
+ return apply_filters( 'autoptimize_filter_cache_do_fallback', false );
637
+ }
638
+
639
  /**
640
  * Checks if cache dirs exist and create if not.
641
  * Returns false if not succesful.
739
  }
740
  } elseif ( function_exists( 'sg_cachepress_purge_cache' ) ) {
741
  sg_cachepress_purge_cache();
742
+ } elseif ( array_key_exists( 'KINSTA_CACHE_ZONE', $_SERVER ) ) {
743
+ $_kinsta_clear_cache_url = 'https://localhost/kinsta-clear-cache-all';
744
+ $_kinsta_response = wp_remote_get(
745
+ $_kinsta_clear_cache_url,
746
+ array(
747
+ 'sslverify' => false,
748
+ 'timeout' => 5,
749
+ )
750
+ );
751
+ } elseif ( defined('NGINX_HELPER_BASENAME') ) {
752
+ do_action( 'rt_nginx_helper_purge_all' );
753
  } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
754
  // fallback for WP-Super-Cache
755
  global $cache_path;
classes/autoptimizeCacheChecker.php CHANGED
@@ -58,10 +58,10 @@ class autoptimizeCacheChecker
58
  $stat_array = autoptimizeCache::stats();
59
  $cache_size = round( $stat_array[1] );
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 . ')';
@@ -87,27 +87,27 @@ class autoptimizeCacheChecker
87
 
88
  public function show_admin_notice()
89
  {
90
- if ( (bool) get_option( 'autoptimize_cachesize_notice', false ) && current_user_can( 'manage_options' ) ) {
91
  echo '<div class="notice notice-warning"><p>';
92
  _e( '<strong>Autoptimize\'s cache size is getting big</strong>, consider purging the cache. Have a look at <a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank" rel="noopener noreferrer">the Autoptimize FAQ</a> to see how you can keep the cache size under control.', 'autoptimize' );
93
  echo '</p></div>';
94
- update_option( 'autoptimize_cachesize_notice', false );
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';
102
 
103
- if ( -1 == $_imgopt_notice['status'] ) {
104
  $_hide_notice = '1';
105
  }
106
 
107
  $_imgopt_notice_dismissible = apply_filters( 'autoptimize_filter_imgopt_notice_dismissable', $_dismissible . $_hide_notice );
108
 
109
  if ( $_imgopt_notice && PAnD::is_admin_notice_active( $_imgopt_notice_dismissible ) ) {
110
- echo '<div class="notice notice-warning is-dismissible" data-dismissible="' . $_imgopt_notice_dismissible . '"><p>' . $_imgopt_notice['notice'] . '</p></div>';
111
  }
112
  }
113
  }
58
  $stat_array = autoptimizeCache::stats();
59
  $cache_size = round( $stat_array[1] );
60
  if ( ( $cache_size > $max_size ) && ( $do_cache_check ) ) {
61
+ autoptimizeOptionWrapper::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', autoptimizeOptionWrapper::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 . ')';
87
 
88
  public function show_admin_notice()
89
  {
90
+ if ( (bool) autoptimizeOptionWrapper::get_option( 'autoptimize_cachesize_notice', false ) && current_user_can( 'manage_options' ) ) {
91
  echo '<div class="notice notice-warning"><p>';
92
  _e( '<strong>Autoptimize\'s cache size is getting big</strong>, consider purging the cache. Have a look at <a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank" rel="noopener noreferrer">the Autoptimize FAQ</a> to see how you can keep the cache size under control.', 'autoptimize' );
93
  echo '</p></div>';
94
+ autoptimizeOptionWrapper::update_option( 'autoptimize_cachesize_notice', false );
95
  }
96
 
97
  // Notice for image proxy usage.
98
+ $_imgopt_notice = autoptimizeImages::instance()->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, -3 ) ) ) {
100
  $_dismissible = 'ao-img-opt-notice-';
101
  $_hide_notice = '7';
102
 
103
+ if ( -1 == $_imgopt_notice['status'] || -2 == $_imgopt_notice['status'] || -3 == $_imgopt_notice['status'] ) {
104
  $_hide_notice = '1';
105
  }
106
 
107
  $_imgopt_notice_dismissible = apply_filters( 'autoptimize_filter_imgopt_notice_dismissable', $_dismissible . $_hide_notice );
108
 
109
  if ( $_imgopt_notice && PAnD::is_admin_notice_active( $_imgopt_notice_dismissible ) ) {
110
+ echo '<div class="notice notice-warning is-dismissible" data-dismissible="' . $_imgopt_notice_dismissible . '"><p><strong>' . __( 'Autoptimize', 'autoptimize' ) . '</strong>: ' . $_imgopt_notice['notice'] . '</p></div>';
111
  }
112
  }
113
  }
classes/autoptimizeConfig.php CHANGED
@@ -1,11 +1,33 @@
1
  <?php
2
- if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly.
 
 
 
 
 
 
3
 
4
  class autoptimizeConfig
5
  {
6
- private $config = null;
 
 
 
 
 
 
 
 
 
 
 
7
  static private $instance = null;
8
 
 
 
 
 
 
9
  private $settings_screen_do_remote_http = true;
10
 
11
  /**
@@ -15,6 +37,10 @@ class autoptimizeConfig
15
  {
16
  if ( is_admin() ) {
17
  // Add the admin page and settings.
 
 
 
 
18
  add_action( 'admin_menu', array( $this, 'addmenu' ) );
19
  add_action( 'admin_init', array( $this, 'registersettings' ) );
20
 
@@ -29,9 +55,9 @@ class autoptimizeConfig
29
  }
30
 
31
  // Clean cache?
32
- if ( get_option( 'autoptimize_cache_clean' ) ) {
33
  autoptimizeCache::clearall();
34
- update_option( 'autoptimize_cache_clean', 0 );
35
  }
36
 
37
  $this->settings_screen_do_remote_http = apply_filters( 'autoptimize_settingsscreen_remotehttp', $this->settings_screen_do_remote_http );
@@ -43,6 +69,8 @@ class autoptimizeConfig
43
  }
44
 
45
  /**
 
 
46
  * @return autoptimizeConfig
47
  */
48
  static public function instance()
@@ -55,42 +83,23 @@ class autoptimizeConfig
55
  return self::$instance;
56
  }
57
 
58
- public function show()
 
 
 
 
 
 
 
 
 
 
59
  {
60
  $conf = self::instance();
61
  ?>
62
  <style>
63
  /* title and button */
64
  #ao_title_and_button:after {content:''; display:block; clear:both;}
65
- #ao_adv_button{float:right;}
66
- #ao_hide_adv:before, #ao_show_adv:before {
67
- display: inline-block;
68
- float: left;
69
- height: 20px;
70
- width: 35px;
71
- background: none;
72
- color: #b4b9be;
73
- font: normal 20px/26px dashicons;
74
- letter-spacing: -4px;
75
- text-align: left;
76
- speak: none;
77
- -webkit-font-smoothing: antialiased;
78
- -moz-osx-font-smoothing: grayscale;
79
- }
80
- #ao_hide_adv:before {
81
- content: "\f108 \f142";
82
- }
83
- #ao_show_adv:before {
84
- content: "\f108 \f140";
85
- }
86
-
87
- /* animate "show adv" button */
88
- #ao_show_adv { animation: watchmenow 3s linear 5s 10; }
89
- #ao_show_adv:hover { animation: none; }
90
- @keyframes watchmenow {
91
- 0% { box-shadow: unset; }
92
- 100% { box-shadow: 0px 0px 20px yellow; }
93
- }
94
 
95
  /* form */
96
  .itemDetail {
@@ -164,9 +173,6 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
164
  #autoptimize_admin_feed {width:0%;display:none !important;}
165
  }
166
  @media (max-width: 782px) {
167
- #ao_hide_adv span, #ao_show_adv span {display: none;}
168
- #ao_hide_adv,#ao_show_adv {height: 34px;padding: 4px 12px 8px 8px;}
169
- #ao_hide_adv:before,#ao_show_adv:before {font-size: 25px;}
170
  #autoptimize_main input[type="checkbox"] {margin-left: 10px;}
171
  #autoptimize_main .cb_label {display: block; padding-left: 45px; text-indent: -45px;}
172
  }
@@ -174,10 +180,6 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
174
 
175
  <div class="wrap">
176
 
177
- <?php if ( version_compare( PHP_VERSION, '5.3.0' ) < 0 ) { ?>
178
- <div class="notice-error notice"><?php echo '<p>' . sprintf( __( '<strong>You are using a very old version of PHP</strong> (5.2.x or older) which has <a href=%s>serious security and performance issues</a>. Support for PHP 5.5 and below will be removed in one of the next AO released, please ask your hoster to provide you with an upgrade path to 7.x.', 'autoptimize' ), '"http://blog.futtta.be/2016/03/15/why-would-you-still-be-on-php-5-2/" target="_blank"' ) . '</p>'; ?></div>
179
- <?php } ?>
180
-
181
  <?php if ( defined( 'AUTOPTIMIZE_LEGACY_MINIFIERS' ) ) { ?>
182
  <div class="notice-error notice"><p>
183
  <?php _e( "You are using the (no longer supported) AUTOPTIMIZE_LEGACY_MINIFIERS constant. Ensure your site is working properly and remove the constant, it doesn't do anything any more.", 'autoptimize' ); ?>
@@ -185,216 +187,238 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
185
  <?php } ?>
186
 
187
  <div id="autoptimize_main">
188
- <div id="ao_title_and_button">
189
- <h1 id="ao_title"><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?>
190
- <span id="ao_adv_button">
191
- <?php if ( get_option( 'autoptimize_show_adv', '1' ) == '1' ) { ?>
192
- <a href="javascript:void(0);" id="ao_show_adv" class="button" style="display:none;"><span><?php _e("Show advanced settings","autoptimize") ?></span></a>
193
- <a href="javascript:void(0);" id="ao_hide_adv" class="button"><span><?php _e("Hide advanced settings","autoptimize") ?></span></a>
194
- <style>tr.ao_adv{display:table-row;} li.ao_adv{display:list-item;}</style>
195
- <?php $hiddenClass = ''; ?>
196
- <?php } else { ?>
197
- <a href="javascript:void(0);" id="ao_show_adv" class="button"><span><?php _e("Show advanced settings","autoptimize") ?></span></a>
198
- <a href="javascript:void(0);" id="ao_hide_adv" class="button" style="display:none;"><span><?php _e("Hide advanced settings","autoptimize") ?></span></a>
199
- <?php $hiddenClass = 'hidden '; ?>
200
- <?php } ?>
201
- </span>
202
- </h1>
203
- </div>
204
-
205
  <?php echo $this->ao_admin_tabs(); ?>
206
 
207
- <form method="post" action="options.php">
208
  <?php settings_fields( 'autoptimize' ); ?>
209
 
210
  <ul>
211
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  <li class="itemDetail">
213
- <h2 class="itemTitle"><?php _e('JavaScript Options','autoptimize'); ?></h2>
214
  <table class="form-table">
215
  <tr valign="top">
216
- <th scope="row"><?php _e('Optimize JavaScript Code?','autoptimize'); ?></th>
217
- <td><input type="checkbox" id="autoptimize_js" name="autoptimize_js" <?php echo get_option('autoptimize_js')?'checked="checked" ':''; ?>/></td>
218
  </tr>
219
- <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
220
  <th scope="row"><?php _e( 'Aggregate JS-files?', 'autoptimize' ); ?></th>
221
- <td><label class="cb_label"><input type="checkbox" id="autoptimize_js_aggregate" name="autoptimize_js_aggregate" <?php echo $conf->get( 'autoptimize_js_aggregate' ) ? 'checked="checked" ':''; ?>/>
222
  <?php _e( 'Aggregate all linked JS-files to have them loaded non-render blocking? If this option is off, the individual JS-files will remain in place but will be minified.', 'autoptimize' ); ?></label></td>
223
  </tr>
224
- <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
225
- <th scope="row"><?php _e('Also aggregate inline JS?','autoptimize'); ?></th>
226
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_include_inline" <?php echo get_option('autoptimize_js_include_inline')?'checked="checked" ':''; ?>/>
227
- <?php _e('Let Autoptimize also extract JS from the HTML. <strong>Warning</strong>: this can make Autoptimize\'s cache size grow quickly, so only enable this if you know what you\'re doing.','autoptimize'); ?></label></td>
228
  </tr>
229
- <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
230
- <th scope="row"><?php _e('Force JavaScript in &lt;head&gt;?','autoptimize'); ?></th>
231
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_forcehead" <?php echo get_option('autoptimize_js_forcehead')?'checked="checked" ':''; ?>/>
232
- <?php _e('Load JavaScript early, this can potentially fix some JS-errors, but makes the JS render blocking.','autoptimize'); ?></label></td>
233
  </tr>
234
- <?php if (get_option('autoptimize_js_justhead')) { ?>
235
- <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
236
- <th scope="row"><?php _e('Look for scripts only in &lt;head&gt;?','autoptimize'); echo ' <i>'. __('(deprecated)','autoptimize') . '</i>'; ?></th>
237
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_justhead" <?php echo get_option('autoptimize_js_justhead')?'checked="checked" ':''; ?>/>
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">
247
- <th scope="row"><?php _e('Add try-catch wrapping?','autoptimize'); ?></th>
248
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_trycatch" <?php echo get_option('autoptimize_js_trycatch')?'checked="checked" ':''; ?>/>
249
- <?php _e('If your scripts break because of a JS-error, you might want to try this.','autoptimize'); ?></label></td>
250
  </tr>
251
  </table>
252
  </li>
253
 
254
  <li class="itemDetail">
255
- <h2 class="itemTitle"><?php _e('CSS Options','autoptimize'); ?></h2>
256
  <table class="form-table">
257
  <tr valign="top">
258
- <th scope="row"><?php _e('Optimize CSS Code?','autoptimize'); ?></th>
259
- <td><input type="checkbox" id="autoptimize_css" name="autoptimize_css" <?php echo get_option('autoptimize_css')?'checked="checked" ':''; ?>/></td>
260
  </tr>
261
- <tr class="<?php echo $hiddenClass;?>css_sub ao_adv" valign="top">
262
  <th scope="row"><?php _e( 'Aggregate CSS-files?', 'autoptimize' ); ?></th>
263
  <td><label class="cb_label"><input type="checkbox" id="autoptimize_css_aggregate" name="autoptimize_css_aggregate" <?php echo $conf->get( 'autoptimize_css_aggregate' ) ? 'checked="checked" ' : ''; ?>/>
264
- <?php _e('Aggregate all linked CSS-files? If this option is off, the individual CSS-files will remain in place but will be minified.', 'autoptimize' ); ?></label></td>
265
  </tr>
266
- <tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv css_aggregate">
267
- <th scope="row"><?php _e('Also aggregate inline CSS?','autoptimize'); ?></th>
268
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_include_inline" <?php echo get_option('autoptimize_css_include_inline','1')?'checked="checked" ':''; ?>/>
269
- <?php _e('Check this option for Autoptimize to also aggregate CSS in the HTML.','autoptimize'); ?></label></td>
270
  </tr>
271
- <tr class="<?php echo $hiddenClass;?>css_sub ao_adv css_aggregate" valign="top">
272
- <th scope="row"><?php _e('Generate data: URIs for images?','autoptimize'); ?></th>
273
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_datauris" <?php echo get_option('autoptimize_css_datauris')?'checked="checked" ':''; ?>/>
274
- <?php _e('Enable this to include small background-images in the CSS itself instead of as separate downloads.','autoptimize'); ?></label></td>
275
  </tr>
276
- <?php if (get_option('autoptimize_css_justhead')) { ?>
277
- <tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv css_aggregate">
278
- <th scope="row"><?php _e('Look for styles only in &lt;head&gt;?','autoptimize'); echo ' <i>'. __('(deprecated)','autoptimize') . '</i>'; ?></th>
279
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_justhead" <?php echo get_option('autoptimize_css_justhead')?'checked="checked" ':''; ?>/>
280
- <?php _e('Don\'t autoptimize CSS outside the head-section. If the cache gets big, you might want to enable this.','autoptimize'); ?></label></td>
 
 
 
 
 
281
  </tr>
282
  <?php } ?>
283
- <tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv">
284
- <th scope="row"><?php _e('Inline and Defer CSS?','autoptimize'); ?></th>
285
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_defer" id="autoptimize_css_defer" <?php echo get_option('autoptimize_css_defer')?'checked="checked" ':''; ?>/>
286
  <?php
287
  _e( 'Inline "above the fold CSS" while loading the main autoptimized CSS only after page load. <a href="http://wordpress.org/plugins/autoptimize/faq/" target="_blank">Check the FAQ</a> for more info.', 'autoptimize' );
288
- if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
289
  echo ' ';
290
  $critcss_install_url = network_admin_url() . 'plugin-install.php?s=autoptimize+criticalcss&tab=search&type=term';
291
- echo sprintf( __( 'This can be fully automated for different types of pages with the %s.', 'autoptimize' ), '<a href="'.$critcss_install_url.'">Autoptimize CriticalCSS Power-Up</a>' );
 
292
  }
293
  ?>
294
  </label></td>
295
  </tr>
296
- <tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv" id="autoptimize_css_defer_inline">
297
  <th scope="row"></th>
298
- <td><label><textarea rows="10" cols="10" style="width:100%;" placeholder="<?php _e('Paste the above the fold CSS here.','autoptimize'); ?>" name="autoptimize_css_defer_inline"><?php echo get_option('autoptimize_css_defer_inline'); ?></textarea></label></td>
299
  </tr>
300
- <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv css_sub css_aggregate">
301
- <th scope="row"><?php _e('Inline all CSS?','autoptimize'); ?></th>
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>
 
 
 
309
  </tr>
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">
331
  <tr valign="top">
332
- <th scope="row"><?php _e('CDN Base URL','autoptimize'); ?></th>
333
- <td><label><input id="cdn_url" type="text" name="autoptimize_cdn_url" pattern="^(https?:)?\/\/([\da-z\.-]+)\.([\da-z\.]{2,6})([\/\w \.-]*)*(:\d{2,5})?\/?$" style="width:100%" value="<?php echo esc_url(get_option('autoptimize_cdn_url',''),array("http","https")); ?>" /><br />
334
- <?php _e('Enter your CDN root URL to enable CDN for Autoptimized files. The URL can be http, https or protocol-relative (e.g. <code>//cdn.example.com/</code>). This is not needed for Cloudflare.','autoptimize'); ?></label></td>
335
  </tr>
336
  </table>
337
  </li>
338
 
339
- <li class="<?php echo $hiddenClass;?>itemDetail ao_adv">
340
- <h2 class="itemTitle"><?php _e('Cache Info','autoptimize'); ?></h2>
341
  <table class="form-table" >
342
- <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
343
- <th scope="row"><?php _e('Cache folder','autoptimize'); ?></th>
344
- <td><?php echo htmlentities(AUTOPTIMIZE_CACHE_DIR); ?></td>
345
  </tr>
346
- <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
347
- <th scope="row"><?php _e('Can we write?','autoptimize'); ?></th>
348
- <td><?php echo (autoptimizeCache::cacheavail() ? __('Yes','autoptimize') : __('No','autoptimize')); ?></td>
349
  </tr>
350
- <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
351
- <th scope="row"><?php _e('Cached styles and scripts','autoptimize'); ?></th>
352
- <td><?php
353
- $AOstatArr = autoptimizeCache::stats();
354
- if ( ! empty( $AOstatArr ) && is_array( $AOstatArr ) ) {
355
- $AOcacheSize = size_format( $AOstatArr[1], 2 );
356
- $details = '';
357
- if ( $AOcacheSize > 0 ) {
358
- $details = ', ~' . $AOcacheSize . ' total';
 
359
  }
360
- printf( __( '%1$s files, totalling %2$s Kbytes (calculated at %3$s)', 'autoptimize' ), $AOstatArr[0], $AOcacheSize, date( 'H:i e', $AOstatArr[2] ) );
 
361
  }
362
- ?></td>
 
363
  </tr>
364
  </table>
365
  </li>
366
 
367
- <li class="<?php echo $hiddenClass;?>itemDetail ao_adv">
368
- <h2 class="itemTitle"><?php _e('Misc Options','autoptimize'); ?></h2>
369
  <table class="form-table">
370
- <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
371
- <th scope="row"><?php _e('Save aggregated script/css as static files?','autoptimize'); ?></th>
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" ':''; ?>/>
389
- <?php _e('By default Autoptimize is also active for logged on users, uncheck not to optimize when logged in e.g. to use a pagebuilder.','autoptimize'); ?></label></td>
390
  </tr>
391
  <?php
392
- if ( function_exists("is_checkout") || function_exists("is_cart") || function_exists("edd_is_checkout") || function_exists("wpsc_is_cart") || function_exists("wpsc_is_checkout") ) {
393
  ?>
394
- <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
395
- <th scope="row"><?php _e('Also optimize shop cart/ checkout?','autoptimize'); ?></th>
396
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_checkout" <?php echo get_option('autoptimize_optimize_checkout','1')?'checked="checked" ':''; ?>/>
397
- <?php _e('By default Autoptimize is also active on your shop\'s cart/ checkout, uncheck not to optimize those.','autoptimize'); ?></label>
398
  </td>
399
  </tr>
400
  <?php } ?>
@@ -403,59 +427,57 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
403
 
404
  </ul>
405
 
406
- <input type="hidden" id="autoptimize_show_adv" name="autoptimize_show_adv" value="<?php echo get_option('autoptimize_show_adv','1'); ?>">
407
-
408
  <p class="submit">
409
- <input type="submit" class="button-secondary" value="<?php _e('Save Changes','autoptimize') ?>" />
410
- <input type="submit" class="button-primary" name="autoptimize_cache_clean" value="<?php _e('Save Changes and Empty Cache','autoptimize') ?>" />
411
  </p>
412
 
413
  </form>
414
  </div>
415
  <div id="autoptimize_admin_feed" class="hidden">
416
  <div class="autoptimize_banner hidden">
417
- <ul>
418
  <?php
419
  if ( $this->settings_screen_do_remote_http ) {
420
- $AO_banner = get_transient( 'autoptimize_banner' );
421
- if ( empty( $AO_banner ) ) {
422
- $banner_resp = wp_remote_get( 'https://misc.optimizingmatters.com/autoptimize_news.html?ao_ver='.AUTOPTIMIZE_PLUGIN_VERSION );
423
  if ( ! is_wp_error( $banner_resp ) ) {
424
  if ( '200' == wp_remote_retrieve_response_code( $banner_resp ) ) {
425
- $AO_banner = wp_kses_post( wp_remote_retrieve_body( $banner_resp ) );
426
- set_transient('autoptimize_banner', $AO_banner, DAY_IN_SECONDS);
427
  }
428
  }
429
  }
430
- echo $AO_banner;
431
  }
432
  ?>
433
- <li><?php _e("Need help? <a href='https://wordpress.org/plugins/autoptimize/faq/'>Check out the FAQ here</a>.","autoptimize"); ?></li>
434
- <li><?php _e("Happy with Autoptimize?","autoptimize"); ?><br /><a href="<?php echo network_admin_url(); ?>plugin-install.php?tab=search&type=author&s=optimizingmatters"><?php _e("Try my other plugins!","autoptimize"); ?></a></li>
435
  </ul>
436
  </div>
437
  <div style="margin-left:10px;margin-top:-5px;">
438
  <h2>
439
- <?php _e("futtta about","autoptimize") ?>
440
  <select id="feed_dropdown" >
441
- <option value="1"><?php _e("Autoptimize","autoptimize") ?></option>
442
- <option value="2"><?php _e("WordPress","autoptimize") ?></option>
443
- <option value="3"><?php _e("Web Technology","autoptimize") ?></option>
444
  </select>
445
  </h2>
446
  <div id="futtta_feed">
447
  <div id="autoptimizefeed">
448
- <?php $this->getFutttaFeeds("http://feeds.feedburner.com/futtta_autoptimize"); ?>
449
  </div>
450
  <div id="wordpressfeed">
451
- <?php $this->getFutttaFeeds("http://feeds.feedburner.com/futtta_wordpress"); ?>
452
  </div>
453
  <div id="webtechfeed">
454
- <?php $this->getFutttaFeeds("http://feeds.feedburner.com/futtta_webtech"); ?>
455
  </div>
456
  </div>
457
  </div>
458
- <div style="float:right;margin:50px 15px;"><a href="http://blog.futtta.be/2013/10/21/do-not-donate-to-me/" target="_blank"><img width="100px" height="85px" src="<?php echo plugins_url().'/'.plugin_basename(dirname(__FILE__)).'/external/do_not_donate_smallest.png'; ?>" title="<?php _e("Do not donate for this plugin!","autoptimize"); ?>"></a></div>
459
  </div>
460
 
461
  <script type="text/javascript">
@@ -476,39 +498,6 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
476
  jQuery("#futtta_feed").fadeTo("slow",1);
477
  });
478
 
479
- jQuery( "#ao_show_adv" ).click(function() {
480
- jQuery( "#ao_show_adv" ).hide();
481
- jQuery( "#ao_hide_adv" ).show();
482
- jQuery( ".ao_adv" ).removeClass("hidden");
483
- jQuery( ".ao_adv" ).show("slow");
484
- if (jQuery("#autoptimize_css").attr('checked')) {
485
- jQuery(".css_sub:visible").fadeTo("fast",1);
486
- if (!jQuery("#autoptimize_css_defer").attr('checked')) {
487
- jQuery("#autoptimize_css_defer_inline").hide();
488
- }
489
- }
490
- if (jQuery("#autoptimize_js").attr('checked')) {
491
- jQuery(".js_sub:visible").fadeTo("fast",1);
492
- }
493
- check_ini_state();
494
- jQuery( "input#autoptimize_show_adv" ).val("1");
495
- });
496
-
497
- jQuery( "#ao_hide_adv" ).click(function() {
498
- jQuery( "#ao_hide_adv" ).hide();
499
- jQuery( "#ao_show_adv" ).show();
500
- jQuery( ".ao_adv" ).hide("slow");
501
- jQuery( ".ao_adv" ).addClass("hidden");
502
- if (!jQuery("#autoptimize_css").attr('checked')) {
503
- jQuery(".css_sub:visible").fadeTo("fast",.33);
504
- }
505
- if (!jQuery("#autoptimize_js").attr('checked')) {
506
- jQuery(".js_sub:visible").fadeTo("fast",.33);
507
- }
508
- check_ini_state();
509
- jQuery( "input#autoptimize_show_adv" ).val("0");
510
- });
511
-
512
  jQuery( "#autoptimize_html" ).change(function() {
513
  if (this.checked) {
514
  jQuery(".html_sub:visible").fadeTo("fast",1);
@@ -573,6 +562,14 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
573
  }
574
  });
575
 
 
 
 
 
 
 
 
 
576
  jQuery("#feed_dropdown").change(function() { show_feed(jQuery("#feed_dropdown").val()) });
577
  feedid=jQuery.cookie(cookiename);
578
  if(typeof(feedid) !== "string") feedid=1;
@@ -610,6 +607,9 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
610
  if (!jQuery("#autoptimize_js_aggregate").attr('checked')) {
611
  jQuery(".js_aggregate:visible").fadeTo('fast',.33);
612
  }
 
 
 
613
  }
614
 
615
  function show_feed(id) {
@@ -626,7 +626,14 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
626
 
627
  public function addmenu()
628
  {
629
- $hook = add_options_page( __( 'Autoptimize Options', 'autoptimize' ), 'Autoptimize', 'manage_options', 'autoptimize', array( $this, 'show' ) );
 
 
 
 
 
 
 
630
  add_action( 'admin_print_scripts-' . $hook, array( $this, 'autoptimize_admin_scripts' ) );
631
  add_action( 'admin_print_styles-' . $hook, array( $this, 'autoptimize_admin_styles' ) );
632
  }
@@ -646,6 +653,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
646
  public function registersettings() {
647
  register_setting( 'autoptimize', 'autoptimize_html' );
648
  register_setting( 'autoptimize', 'autoptimize_html_keepcomments' );
 
649
  register_setting( 'autoptimize', 'autoptimize_js' );
650
  register_setting( 'autoptimize', 'autoptimize_js_aggregate' );
651
  register_setting( 'autoptimize', 'autoptimize_js_exclude' );
@@ -665,13 +673,12 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
665
  register_setting( 'autoptimize', 'autoptimize_cdn_url' );
666
  register_setting( 'autoptimize', 'autoptimize_cache_clean' );
667
  register_setting( 'autoptimize', 'autoptimize_cache_nogzip' );
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)
675
  {
676
  // Inspired on http://wpengineer.com/meta-links-for-wordpress-plugins/.
677
  // Do it only once - saves time.
@@ -689,7 +696,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
689
  // If it's us, add the link.
690
  if ( $file === $plugin ) {
691
  $newlink = array( sprintf( '<a href="options-general.php?page=autoptimize">%s</a>', __( 'Settings' ) ) );
692
- $links = array_merge( $links, $newlink );
693
  }
694
  }
695
 
@@ -697,35 +704,37 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
697
  }
698
 
699
  /**
 
 
700
  * @return array
701
  */
702
  public static function get_defaults()
703
  {
704
  static $config = array(
705
- 'autoptimize_html' => 0,
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,
713
- 'autoptimize_js_forcehead' => 0,
714
- 'autoptimize_css' => 0,
715
- 'autoptimize_css_aggregate' => 1,
716
- 'autoptimize_css_exclude' => 'admin-bar.min.css, dashicons.min.css, wp-content/cache/, wp-content/uploads/',
717
- 'autoptimize_css_justhead' => 0,
 
718
  'autoptimize_css_include_inline' => 1,
719
- 'autoptimize_css_defer' => 0,
720
- 'autoptimize_css_defer_inline' => '',
721
- 'autoptimize_css_inline' => 0,
722
- 'autoptimize_css_datauris' => 0,
723
- 'autoptimize_cdn_url' => '',
724
- 'autoptimize_cache_nogzip' => 1,
725
- 'autoptimize_show_adv' => 1,
726
- 'autoptimize_optimize_logged' => 1,
727
- 'autoptimize_optimize_checkout' => 1,
728
- 'autoptimize_minify_excluded' => 1,
729
  );
730
 
731
  return $config;
@@ -744,6 +753,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
744
  'autoptimize_extra_radio_field_4' => '1',
745
  'autoptimize_extra_text_field_2' => '',
746
  'autoptimize_extra_text_field_3' => '',
 
747
  );
748
 
749
  return $defaults;
@@ -773,7 +783,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
773
  */
774
  public static function get_ao_css_preload_polyfill()
775
  {
776
- $preload_poly = apply_filters('autoptimize_css_preload_polyfill','<script data-cfasync=\'false\'>!function(t){"use strict";t.loadCSS||(t.loadCSS=function(){});var e=loadCSS.relpreload={};if(e.support=function(){var e;try{e=t.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),e.bindMediaToggle=function(t){function e(){t.media=a}var a=t.media||"all";t.addEventListener?t.addEventListener("load",e):t.attachEvent&&t.attachEvent("onload",e),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(e,3e3)},e.poly=function(){if(!e.support())for(var a=t.document.getElementsByTagName("link"),n=0;n<a.length;n++){var o=a[n];"preload"!==o.rel||"style"!==o.getAttribute("as")||o.getAttribute("data-loadcss")||(o.setAttribute("data-loadcss",!0),e.bindMediaToggle(o))}},!e.support()){e.poly();var a=t.setInterval(e.poly,500);t.addEventListener?t.addEventListener("load",function(){e.poly(),t.clearInterval(a)}):t.attachEvent&&t.attachEvent("onload",function(){e.poly(),t.clearInterval(a)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:t.loadCSS=loadCSS}("undefined"!=typeof global?global:this);</script>');
777
  return $preload_poly;
778
  }
779
 
@@ -784,11 +794,11 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
784
  */
785
  public static function get_ao_css_preload_onload()
786
  {
787
- $preload_onload = apply_filters('autoptimize_filter_css_preload_onload',"this.onload=null;this.rel='stylesheet'");
788
  return $preload_onload;
789
  }
790
 
791
- public function get($key)
792
  {
793
  if ( ! is_array( $this->config ) ) {
794
  // Default config.
@@ -796,7 +806,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
796
 
797
  // Override with user settings.
798
  foreach ( array_keys( $config ) as $name ) {
799
- $conf = get_option( $name );
800
  if ( false !== $conf ) {
801
  // It was set before!
802
  $config[ $name ] = $conf;
@@ -814,24 +824,25 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
814
  return false;
815
  }
816
 
817
- private function getFutttaFeeds($url) {
818
  if ( $this->settings_screen_do_remote_http ) {
819
- $rss = fetch_feed( $url );
820
  $maxitems = 0;
821
 
822
  if ( ! is_wp_error( $rss ) ) {
823
- $maxitems = $rss->get_item_quantity( 7 );
824
  $rss_items = $rss->get_items( 0, $maxitems );
825
  }
826
  ?>
827
  <ul>
828
- <?php if ( $maxitems == 0 ) : ?>
829
  <li><?php _e( 'No items', 'autoptimize' ); ?></li>
830
  <?php else : ?>
831
  <?php foreach ( $rss_items as $item ) : ?>
832
  <li>
833
  <a href="<?php echo esc_url( $item->get_permalink() ); ?>"
834
- title="<?php printf( __( 'Posted %s', 'autoptimize' ), $item->get_date('j F Y | g:i a') ); ?>">
 
835
  <?php echo esc_html( $item->get_title() ); ?>
836
  </a>
837
  </li>
@@ -842,33 +853,33 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
842
  }
843
  }
844
 
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 ) {
852
  if ( isset( $_GET['page'] ) ) {
853
- $currentId = $_GET['page'];
854
  } else {
855
- $currentId = "autoptimize";
856
  }
857
- $tabContent .= '<h2 class="nav-tab-wrapper">';
858
- foreach ($tabs as $tabId => $tabName) {
859
- if ( $currentId == $tabId ) {
860
  $class = ' nav-tab-active';
861
- } else{
862
  $class = '';
863
  }
864
- $tabContent .= '<a class="nav-tab' . $class . '" href="?page=' . $tabId . '">' . $tabName . '</a>';
865
  }
866
- $tabContent .= '</h2>';
867
  } else {
868
- $tabContent = '<hr/>';
869
  }
870
 
871
- return $tabContent;
872
  }
873
 
874
  /**
@@ -893,4 +904,17 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
893
  }
894
  return ( defined( 'DOING_AJAX' ) && DOING_AJAX );
895
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
896
  }
1
  <?php
2
+ /**
3
+ * Main configurtion logic.
4
+ */
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit;
8
+ }
9
 
10
  class autoptimizeConfig
11
  {
12
+ /**
13
+ * Options.
14
+ *
15
+ * @var array
16
+ */
17
+ private $config = null;
18
+
19
+ /**
20
+ * Singleton instance.
21
+ *
22
+ * @var self|null
23
+ */
24
  static private $instance = null;
25
 
26
+ /**
27
+ * Options.
28
+ *
29
+ * @var bool
30
+ */
31
  private $settings_screen_do_remote_http = true;
32
 
33
  /**
37
  {
38
  if ( is_admin() ) {
39
  // Add the admin page and settings.
40
+ if ( autoptimizeOptionWrapper::is_ao_active_for_network() ) {
41
+ add_action( 'network_admin_menu', array( $this, 'addmenu' ) );
42
+ }
43
+
44
  add_action( 'admin_menu', array( $this, 'addmenu' ) );
45
  add_action( 'admin_init', array( $this, 'registersettings' ) );
46
 
55
  }
56
 
57
  // Clean cache?
58
+ if ( autoptimizeOptionWrapper::get_option( 'autoptimize_cache_clean' ) ) {
59
  autoptimizeCache::clearall();
60
+ autoptimizeOptionWrapper::update_option( 'autoptimize_cache_clean', 0 );
61
  }
62
 
63
  $this->settings_screen_do_remote_http = apply_filters( 'autoptimize_settingsscreen_remotehttp', $this->settings_screen_do_remote_http );
69
  }
70
 
71
  /**
72
+ * Instantiates aoconfig.
73
+ *
74
  * @return autoptimizeConfig
75
  */
76
  static public function instance()
83
  return self::$instance;
84
  }
85
 
86
+ public function show_network_message() {
87
+ ?>
88
+ <div class="wrap">
89
+ <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
90
+ <?php echo $this->ao_admin_tabs(); ?>
91
+ <p style="font-size:120%;"><?php _e( 'Autoptimize is enabled and configured on a WordPress network level. Please contact your network administrator if you need Autoptimize settings changed.', 'autoptimize' ); ?></p>
92
+ </div>
93
+ <?php
94
+ }
95
+
96
+ public function show_config()
97
  {
98
  $conf = self::instance();
99
  ?>
100
  <style>
101
  /* title and button */
102
  #ao_title_and_button:after {content:''; display:block; clear:both;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  /* form */
105
  .itemDetail {
173
  #autoptimize_admin_feed {width:0%;display:none !important;}
174
  }
175
  @media (max-width: 782px) {
 
 
 
176
  #autoptimize_main input[type="checkbox"] {margin-left: 10px;}
177
  #autoptimize_main .cb_label {display: block; padding-left: 45px; text-indent: -45px;}
178
  }
180
 
181
  <div class="wrap">
182
 
 
 
 
 
183
  <?php if ( defined( 'AUTOPTIMIZE_LEGACY_MINIFIERS' ) ) { ?>
184
  <div class="notice-error notice"><p>
185
  <?php _e( "You are using the (no longer supported) AUTOPTIMIZE_LEGACY_MINIFIERS constant. Ensure your site is working properly and remove the constant, it doesn't do anything any more.", 'autoptimize' ); ?>
187
  <?php } ?>
188
 
189
  <div id="autoptimize_main">
190
+ <h1 id="ao_title"><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  <?php echo $this->ao_admin_tabs(); ?>
192
 
193
+ <form method="post" action="<?php echo admin_url( 'options.php' ); ?>">
194
  <?php settings_fields( 'autoptimize' ); ?>
195
 
196
  <ul>
197
 
198
+ <?php
199
+ // Only show enable site configuration in network site option.
200
+ if ( is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
201
+ ?>
202
+ <li class="itemDetail multiSite">
203
+ <h2 class="itemTitle"><?php _e( 'Multisite Options', 'autoptimize' ); ?></h2>
204
+ <table class="form-table">
205
+ <tr valign="top">
206
+ <th scope="row"><?php _e( 'Enable site configuration?', 'autoptimize' ); ?></th>
207
+ <td><label class="cb_label"><input type="checkbox" id="autoptimize_enable_site_config" name="autoptimize_enable_site_config" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_enable_site_config' ) ? 'checked="checked" ' : ''; ?>/>
208
+ <?php _e( 'Enable Autoptimize configuration per site.', 'autoptimize' ); ?></label></td>
209
+ </tr>
210
+ </table>
211
+ </li>
212
+ <?php } else { ?>
213
+ <input type="hidden" id="autoptimize_enable_site_config" name="autoptimize_enable_site_config" value="on" />
214
+ <?php } ?>
215
+
216
  <li class="itemDetail">
217
+ <h2 class="itemTitle"><?php _e( 'JavaScript Options', 'autoptimize' ); ?></h2>
218
  <table class="form-table">
219
  <tr valign="top">
220
+ <th scope="row"><?php _e( 'Optimize JavaScript Code?', 'autoptimize' ); ?></th>
221
+ <td><input type="checkbox" id="autoptimize_js" name="autoptimize_js" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js' ) ? 'checked="checked" ' : ''; ?>/></td>
222
  </tr>
223
+ <tr valign="top" class="js_sub">
224
  <th scope="row"><?php _e( 'Aggregate JS-files?', 'autoptimize' ); ?></th>
225
+ <td><label class="cb_label"><input type="checkbox" id="autoptimize_js_aggregate" name="autoptimize_js_aggregate" <?php echo $conf->get( 'autoptimize_js_aggregate' ) ? 'checked="checked" ' : ''; ?>/>
226
  <?php _e( 'Aggregate all linked JS-files to have them loaded non-render blocking? If this option is off, the individual JS-files will remain in place but will be minified.', 'autoptimize' ); ?></label></td>
227
  </tr>
228
+ <tr valign="top" class="js_sub js_aggregate">
229
+ <th scope="row"><?php _e( 'Also aggregate inline JS?', 'autoptimize' ); ?></th>
230
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_include_inline" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_include_inline' ) ? 'checked="checked" ' : ''; ?>/>
231
+ <?php _e( 'Let Autoptimize also extract JS from the HTML. <strong>Warning</strong>: this can make Autoptimize\'s cache size grow quickly, so only enable this if you know what you\'re doing.', 'autoptimize' ); ?></label></td>
232
  </tr>
233
+ <tr valign="top" class="js_sub js_aggregate">
234
+ <th scope="row"><?php _e( 'Force JavaScript in &lt;head&gt;?', 'autoptimize' ); ?></th>
235
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_forcehead" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_forcehead' ) ? 'checked="checked" ' : ''; ?>/>
236
+ <?php _e( 'Load JavaScript early, this can potentially fix some JS-errors, but makes the JS render blocking.', 'autoptimize' ); ?></label></td>
237
  </tr>
238
+ <?php if ( autoptimizeOptionWrapper::get_option( 'autoptimize_js_justhead' ) ) { ?>
239
+ <tr valign="top" class="js_sub js_aggregate">
240
+ <th scope="row">
241
+ <?php
242
+ _e( 'Look for scripts only in &lt;head&gt;?', 'autoptimize' );
243
+ echo ' <i>' . __( '(deprecated)', 'autoptimize' ) . '</i>';
244
+ ?>
245
+ </th>
246
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_justhead" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_justhead' ) ? 'checked="checked" ' : ''; ?>/>
247
+ <?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>
248
  </tr>
249
  <?php } ?>
250
+ <tr valign="top" class="js_sub">
251
+ <th scope="row"><?php _e( 'Exclude scripts from Autoptimize:', 'autoptimize' ); ?></th>
252
+ <td><label><input type="text" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_exclude', 'wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.js' ); ?>"/><br />
253
+ <?php
254
+ echo __( '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' ) . ' ' . __( 'Important: excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
255
+ ?>
256
+ </label></td>
257
  </tr>
258
+ <tr valign="top" class="js_sub js_aggregate">
259
+ <th scope="row"><?php _e( 'Add try-catch wrapping?', 'autoptimize' ); ?></th>
260
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_trycatch" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_trycatch' ) ? 'checked="checked" ' : ''; ?>/>
261
+ <?php _e( 'If your scripts break because of a JS-error, you might want to try this.', 'autoptimize' ); ?></label></td>
262
  </tr>
263
  </table>
264
  </li>
265
 
266
  <li class="itemDetail">
267
+ <h2 class="itemTitle"><?php _e( 'CSS Options', 'autoptimize' ); ?></h2>
268
  <table class="form-table">
269
  <tr valign="top">
270
+ <th scope="row"><?php _e( 'Optimize CSS Code?', 'autoptimize' ); ?></th>
271
+ <td><input type="checkbox" id="autoptimize_css" name="autoptimize_css" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) ? 'checked="checked" ' : ''; ?>/></td>
272
  </tr>
273
+ <tr class="css_sub" valign="top">
274
  <th scope="row"><?php _e( 'Aggregate CSS-files?', 'autoptimize' ); ?></th>
275
  <td><label class="cb_label"><input type="checkbox" id="autoptimize_css_aggregate" name="autoptimize_css_aggregate" <?php echo $conf->get( 'autoptimize_css_aggregate' ) ? 'checked="checked" ' : ''; ?>/>
276
+ <?php _e( 'Aggregate all linked CSS-files? If this option is off, the individual CSS-files will remain in place but will be minified.', 'autoptimize' ); ?></label></td>
277
  </tr>
278
+ <tr valign="top" class="css_sub css_aggregate">
279
+ <th scope="row"><?php _e( 'Also aggregate inline CSS?', 'autoptimize' ); ?></th>
280
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_include_inline" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_include_inline', '1' ) ? 'checked="checked" ' : ''; ?>/>
281
+ <?php _e( 'Check this option for Autoptimize to also aggregate CSS in the HTML.', 'autoptimize' ); ?></label></td>
282
  </tr>
283
+ <tr class="css_sub css_aggregate" valign="top">
284
+ <th scope="row"><?php _e( 'Generate data: URIs for images?', 'autoptimize' ); ?></th>
285
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_datauris" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_datauris' ) ? 'checked="checked" ' : ''; ?>/>
286
+ <?php _e( 'Enable this to include small background-images in the CSS itself instead of as separate downloads.', 'autoptimize' ); ?></label></td>
287
  </tr>
288
+ <?php if ( autoptimizeOptionWrapper::get_option( 'autoptimize_css_justhead' ) ) { ?>
289
+ <tr valign="top" class="css_sub css_aggregate">
290
+ <th scope="row">
291
+ <?php
292
+ _e( 'Look for styles only in &lt;head&gt;?', 'autoptimize' );
293
+ echo ' <i>' . __( '(deprecated)', 'autoptimize' ) . '</i>';
294
+ ?>
295
+ </th>
296
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_justhead" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_justhead' ) ? 'checked="checked" ' : ''; ?>/>
297
+ <?php _e( 'Don\'t autoptimize CSS outside the head-section. If the cache gets big, you might want to enable this.', 'autoptimize' ); ?></label></td>
298
  </tr>
299
  <?php } ?>
300
+ <tr valign="top" class="css_sub">
301
+ <th scope="row"><?php _e( 'Inline and Defer CSS?', 'autoptimize' ); ?></th>
302
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_defer" id="autoptimize_css_defer" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer' ) ? 'checked="checked" ' : ''; ?>/>
303
  <?php
304
  _e( 'Inline "above the fold CSS" while loading the main autoptimized CSS only after page load. <a href="http://wordpress.org/plugins/autoptimize/faq/" target="_blank">Check the FAQ</a> for more info.', 'autoptimize' );
305
+ if ( ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
306
  echo ' ';
307
  $critcss_install_url = network_admin_url() . 'plugin-install.php?s=autoptimize+criticalcss&tab=search&type=term';
308
+ // translators: links to plugin install screen with "autoptimize critical CSS" search.
309
+ echo sprintf( __( 'This can be fully automated for different types of pages with the %s.', 'autoptimize' ), '<a href="' . $critcss_install_url . '">Autoptimize CriticalCSS Power-Up</a>' );
310
  }
311
  ?>
312
  </label></td>
313
  </tr>
314
+ <tr valign="top" class="css_sub" id="autoptimize_css_defer_inline">
315
  <th scope="row"></th>
316
+ <td><label><textarea rows="10" cols="10" style="width:100%;" placeholder="<?php _e( 'Paste the above the fold CSS here. You can leave this empty when using the automated Critical CSS integration.', 'autoptimize' ); ?>" name="autoptimize_css_defer_inline"><?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer_inline' ); ?></textarea></label></td>
317
  </tr>
318
+ <tr valign="top" class="css_sub css_aggregate">
319
+ <th scope="row"><?php _e( 'Inline all CSS?', 'autoptimize' ); ?></th>
320
+ <td><label class="cb_label"><input type="checkbox" id="autoptimize_css_inline" name="autoptimize_css_inline" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_inline' ) ? 'checked="checked" ' : ''; ?>/>
321
+ <?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>
322
  </tr>
323
+ <tr valign="top" class="css_sub">
324
+ <th scope="row"><?php _e( 'Exclude CSS from Autoptimize:', 'autoptimize' ); ?></th>
325
+ <td><label><input type="text" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_exclude', 'wp-content/cache/, wp-content/uploads/, admin-bar.min.css, dashicons.min.css' ); ?>"/><br />
326
+ <?php
327
+ echo __( 'A comma-separated list of CSS you want to exclude from being optimized.', 'autoptimize' ) . ' ' . __( 'Important: excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
328
+ ?>
329
+ </label></td>
330
  </tr>
331
  </table>
332
  </li>
333
 
334
  <li class="itemDetail">
335
+ <h2 class="itemTitle"><?php _e( 'HTML Options', 'autoptimize' ); ?></h2>
336
  <table class="form-table">
337
  <tr valign="top">
338
+ <th scope="row"><?php _e( 'Optimize HTML Code?', 'autoptimize' ); ?></th>
339
+ <td><input type="checkbox" id="autoptimize_html" name="autoptimize_html" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_html' ) ? 'checked="checked" ' : ''; ?>/></td>
340
  </tr>
341
+ <tr class="html_sub" valign="top">
342
+ <th scope="row"><?php _e( 'Keep HTML comments?', 'autoptimize' ); ?></th>
343
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_html_keepcomments" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_html_keepcomments' ) ? 'checked="checked" ' : ''; ?>/>
344
+ <?php _e( 'Enable this if you want HTML comments to remain in the page.', 'autoptimize' ); ?></label></td>
345
  </tr>
346
  </table>
347
  </li>
348
 
349
  <li class="itemDetail">
350
+ <h2 class="itemTitle"><?php _e( 'CDN Options', 'autoptimize' ); ?></h2>
351
  <table class="form-table">
352
  <tr valign="top">
353
+ <th scope="row"><?php _e( 'CDN Base URL', 'autoptimize' ); ?></th>
354
+ <td><label><input id="cdn_url" type="text" name="autoptimize_cdn_url" pattern="^(https?:)?\/\/([\da-z\.-]+)\.([\da-z\.]{2,6})([\/\w \.-]*)*(:\d{2,5})?\/?$" style="width:100%" value="<?php echo esc_url( autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' ), array( 'http', 'https' ) ); ?>" /><br />
355
+ <?php _e( 'Enter your CDN root URL to enable CDN for Autoptimized files. The URL can be http, https or protocol-relative (e.g. <code>//cdn.example.com/</code>). This is not needed for Cloudflare.', 'autoptimize' ); ?></label></td>
356
  </tr>
357
  </table>
358
  </li>
359
 
360
+ <li class="itemDetail">
361
+ <h2 class="itemTitle"><?php _e( 'Cache Info', 'autoptimize' ); ?></h2>
362
  <table class="form-table" >
363
+ <tr valign="top" >
364
+ <th scope="row"><?php _e( 'Cache folder', 'autoptimize' ); ?></th>
365
+ <td><?php echo htmlentities( AUTOPTIMIZE_CACHE_DIR ); ?></td>
366
  </tr>
367
+ <tr valign="top" >
368
+ <th scope="row"><?php _e( 'Can we write?', 'autoptimize' ); ?></th>
369
+ <td><?php echo ( autoptimizeCache::cacheavail() ? __( 'Yes', 'autoptimize' ) : __( 'No', 'autoptimize' ) ); ?></td>
370
  </tr>
371
+ <tr valign="top" >
372
+ <th scope="row"><?php _e( 'Cached styles and scripts', 'autoptimize' ); ?></th>
373
+ <td>
374
+ <?php
375
+ $ao_stat_arr = autoptimizeCache::stats();
376
+ if ( ! empty( $ao_stat_arr ) && is_array( $ao_stat_arr ) ) {
377
+ $ao_cache_size = size_format( $ao_stat_arr[1], 2 );
378
+ $details = '';
379
+ if ( $ao_cache_size > 0 ) {
380
+ $details = ', ~' . $ao_cache_size . ' total';
381
  }
382
+ // translators: Kilobytes + timestamp shown.
383
+ printf( __( '%1$s files, totalling %2$s Kbytes (calculated at %3$s)', 'autoptimize' ), $ao_stat_arr[0], $ao_cache_size, date( 'H:i e', $ao_stat_arr[2] ) );
384
  }
385
+ ?>
386
+ </td>
387
  </tr>
388
  </table>
389
  </li>
390
 
391
+ <li class="itemDetail">
392
+ <h2 class="itemTitle"><?php _e( 'Misc Options', 'autoptimize' ); ?></h2>
393
  <table class="form-table">
394
+ <tr valign="top" >
395
+ <th scope="row"><?php _e( 'Save aggregated script/css as static files?', 'autoptimize' ); ?></th>
396
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_nogzip" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_cache_nogzip', '1' ) ? 'checked="checked" ' : ''; ?>/>
397
+ <?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>
398
  </tr>
399
  <?php
400
+ $_min_excl_class = '';
401
+ if ( ! $conf->get( 'autoptimize_css_aggregate' ) && ! $conf->get( 'autoptimize_js_aggregate' ) ) {
402
+ $_min_excl_class = 'hidden';
403
  }
404
  ?>
405
+ <tr valign="top" id="min_excl_row" class="<?php echo $_min_excl_class; ?>">
406
+ <th scope="row"><?php _e( 'Minify excluded CSS and JS files?', 'autoptimize' ); ?></th>
407
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_minify_excluded" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_minify_excluded', '1' ) ? 'checked="checked" ' : ''; ?>/>
408
+ <?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>
409
  </tr>
410
+ <tr valign="top">
411
+ <th scope="row"><?php _e( 'Also optimize for logged in editors/ administrators?', 'autoptimize' ); ?></th>
412
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_logged" <?php echo get_option( 'autoptimize_optimize_logged', '1' ) ? 'checked="checked" ' : ''; ?>/>
413
+ <?php _e( 'By default Autoptimize is also active for logged on editors/ administrators, uncheck this option if you don\'t want Autoptimize to optimize when logged in e.g. to use a pagebuilder.', 'autoptimize' ); ?></label></td>
414
  </tr>
415
  <?php
416
+ if ( function_exists( 'is_checkout' ) || function_exists( 'is_cart' ) || function_exists( 'edd_is_checkout' ) || function_exists( 'wpsc_is_cart' ) || function_exists( 'wpsc_is_checkout' ) ) {
417
  ?>
418
+ <tr valign="top" >
419
+ <th scope="row"><?php _e( 'Also optimize shop cart/ checkout?', 'autoptimize' ); ?></th>
420
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_checkout" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_checkout', '0' ) ? 'checked="checked" ' : ''; ?>/>
421
+ <?php _e( 'By default Autoptimize is also active on your shop\'s cart/ checkout, uncheck not to optimize those.', 'autoptimize' ); ?></label>
422
  </td>
423
  </tr>
424
  <?php } ?>
427
 
428
  </ul>
429
 
 
 
430
  <p class="submit">
431
+ <input type="submit" class="button-secondary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" />
432
+ <input type="submit" class="button-primary" name="autoptimize_cache_clean" value="<?php _e( 'Save Changes and Empty Cache', 'autoptimize' ); ?>" />
433
  </p>
434
 
435
  </form>
436
  </div>
437
  <div id="autoptimize_admin_feed" class="hidden">
438
  <div class="autoptimize_banner hidden">
439
+ <ul>
440
  <?php
441
  if ( $this->settings_screen_do_remote_http ) {
442
+ $ao_banner = get_transient( 'autoptimize_banner' );
443
+ if ( empty( $ao_banner ) ) {
444
+ $banner_resp = wp_remote_get( 'https://misc.optimizingmatters.com/autoptimize_news.html?ao_ver=' . AUTOPTIMIZE_PLUGIN_VERSION );
445
  if ( ! is_wp_error( $banner_resp ) ) {
446
  if ( '200' == wp_remote_retrieve_response_code( $banner_resp ) ) {
447
+ $ao_banner = wp_kses_post( wp_remote_retrieve_body( $banner_resp ) );
448
+ set_transient( 'autoptimize_banner', $ao_banner, WEEK_IN_SECONDS );
449
  }
450
  }
451
  }
452
+ echo $ao_banner;
453
  }
454
  ?>
455
+ <li><?php _e( "Need help? <a href='https://wordpress.org/plugins/autoptimize/faq/'>Check out the FAQ here</a>.", 'autoptimize' ); ?></li>
456
+ <li><?php _e( 'Happy with Autoptimize?', 'autoptimize' ); ?><br /><a href="<?php echo network_admin_url(); ?>plugin-install.php?tab=search&type=author&s=optimizingmatters"><?php _e( 'Try my other plugins!', 'autoptimize' ); ?></a></li>
457
  </ul>
458
  </div>
459
  <div style="margin-left:10px;margin-top:-5px;">
460
  <h2>
461
+ <?php _e( 'futtta about', 'autoptimize' ); ?>
462
  <select id="feed_dropdown" >
463
+ <option value="1"><?php _e( 'Autoptimize', 'autoptimize' ); ?></option>
464
+ <option value="2"><?php _e( 'WordPress', 'autoptimize' ); ?></option>
465
+ <option value="3"><?php _e( 'Web Technology', 'autoptimize' ); ?></option>
466
  </select>
467
  </h2>
468
  <div id="futtta_feed">
469
  <div id="autoptimizefeed">
470
+ <?php $this->get_futtta_feeds( 'http://feeds.feedburner.com/futtta_autoptimize' ); ?>
471
  </div>
472
  <div id="wordpressfeed">
473
+ <?php $this->get_futtta_feeds( 'http://feeds.feedburner.com/futtta_wordpress' ); ?>
474
  </div>
475
  <div id="webtechfeed">
476
+ <?php $this->get_futtta_feeds( 'http://feeds.feedburner.com/futtta_webtech' ); ?>
477
  </div>
478
  </div>
479
  </div>
480
+ <div style="float:right;margin:50px 15px;"><a href="http://blog.futtta.be/2013/10/21/do-not-donate-to-me/" target="_blank"><img width="100px" height="85px" src="<?php echo plugins_url() . '/' . plugin_basename( dirname( __FILE__ ) ) . '/external/do_not_donate_smallest.png'; ?>" title="<?php _e( 'Do not donate for this plugin!', 'autoptimize' ); ?>"></a></div>
481
  </div>
482
 
483
  <script type="text/javascript">
498
  jQuery("#futtta_feed").fadeTo("slow",1);
499
  });
500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  jQuery( "#autoptimize_html" ).change(function() {
502
  if (this.checked) {
503
  jQuery(".html_sub:visible").fadeTo("fast",1);
562
  }
563
  });
564
 
565
+ jQuery( "#autoptimize_enable_site_config" ).change(function() {
566
+ if (this.checked) {
567
+ jQuery("li.itemDetail:not(.multiSite)").fadeTo("fast",.33);
568
+ } else {
569
+ jQuery("li.itemDetail:not(.multiSite)").fadeTo("fast",1);
570
+ }
571
+ });
572
+
573
  jQuery("#feed_dropdown").change(function() { show_feed(jQuery("#feed_dropdown").val()) });
574
  feedid=jQuery.cookie(cookiename);
575
  if(typeof(feedid) !== "string") feedid=1;
607
  if (!jQuery("#autoptimize_js_aggregate").attr('checked')) {
608
  jQuery(".js_aggregate:visible").fadeTo('fast',.33);
609
  }
610
+ if (jQuery("#autoptimize_enable_site_config").attr('checked')) {
611
+ jQuery("li.itemDetail:not(.multiSite)").fadeTo('fast',.33);
612
+ }
613
  }
614
 
615
  function show_feed(id) {
626
 
627
  public function addmenu()
628
  {
629
+ if ( is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
630
+ $hook = add_submenu_page( 'settings.php', __( 'Autoptimize Options', 'autoptimize' ), 'Autoptimize', 'manage_network_options', 'autoptimize', array( $this, 'show_config' ) );
631
+ } elseif ( ! is_multisite() || 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_enable_site_config' ) ) {
632
+ $hook = add_options_page( __( 'Autoptimize Options', 'autoptimize' ), 'Autoptimize', 'manage_options', 'autoptimize', array( $this, 'show_config' ) );
633
+ } else {
634
+ $hook = add_options_page( __( 'Autoptimize Options', 'autoptimize' ), 'Autoptimize', 'manage_options', 'autoptimize', array( $this, 'show_network_message' ) );
635
+ }
636
+
637
  add_action( 'admin_print_scripts-' . $hook, array( $this, 'autoptimize_admin_scripts' ) );
638
  add_action( 'admin_print_styles-' . $hook, array( $this, 'autoptimize_admin_styles' ) );
639
  }
653
  public function registersettings() {
654
  register_setting( 'autoptimize', 'autoptimize_html' );
655
  register_setting( 'autoptimize', 'autoptimize_html_keepcomments' );
656
+ register_setting( 'autoptimize', 'autoptimize_enable_site_config' );
657
  register_setting( 'autoptimize', 'autoptimize_js' );
658
  register_setting( 'autoptimize', 'autoptimize_js_aggregate' );
659
  register_setting( 'autoptimize', 'autoptimize_js_exclude' );
673
  register_setting( 'autoptimize', 'autoptimize_cdn_url' );
674
  register_setting( 'autoptimize', 'autoptimize_cache_clean' );
675
  register_setting( 'autoptimize', 'autoptimize_cache_nogzip' );
 
676
  register_setting( 'autoptimize', 'autoptimize_optimize_logged' );
677
  register_setting( 'autoptimize', 'autoptimize_optimize_checkout' );
678
  register_setting( 'autoptimize', 'autoptimize_minify_excluded' );
679
  }
680
 
681
+ public function setmeta( $links, $file = null )
682
  {
683
  // Inspired on http://wpengineer.com/meta-links-for-wordpress-plugins/.
684
  // Do it only once - saves time.
696
  // If it's us, add the link.
697
  if ( $file === $plugin ) {
698
  $newlink = array( sprintf( '<a href="options-general.php?page=autoptimize">%s</a>', __( 'Settings' ) ) );
699
+ $links = array_merge( $links, $newlink );
700
  }
701
  }
702
 
704
  }
705
 
706
  /**
707
+ * Provides the default options.
708
+ *
709
  * @return array
710
  */
711
  public static function get_defaults()
712
  {
713
  static $config = array(
714
+ 'autoptimize_html' => 0,
715
+ 'autoptimize_html_keepcomments' => 0,
716
+ 'autoptimize_enable_site_config' => 1,
717
+ 'autoptimize_js' => 0,
718
+ 'autoptimize_js_aggregate' => 1,
719
+ 'autoptimize_js_exclude' => 'wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.js',
720
+ 'autoptimize_js_trycatch' => 0,
721
+ 'autoptimize_js_justhead' => 0,
722
+ 'autoptimize_js_include_inline' => 0,
723
+ 'autoptimize_js_forcehead' => 0,
724
+ 'autoptimize_css' => 0,
725
+ 'autoptimize_css_aggregate' => 1,
726
+ 'autoptimize_css_exclude' => 'admin-bar.min.css, dashicons.min.css, wp-content/cache/, wp-content/uploads/',
727
+ 'autoptimize_css_justhead' => 0,
728
  'autoptimize_css_include_inline' => 1,
729
+ 'autoptimize_css_defer' => 0,
730
+ 'autoptimize_css_defer_inline' => '',
731
+ 'autoptimize_css_inline' => 0,
732
+ 'autoptimize_css_datauris' => 0,
733
+ 'autoptimize_cdn_url' => '',
734
+ 'autoptimize_cache_nogzip' => 1,
735
+ 'autoptimize_optimize_logged' => 1,
736
+ 'autoptimize_optimize_checkout' => 0,
737
+ 'autoptimize_minify_excluded' => 1,
 
738
  );
739
 
740
  return $config;
753
  'autoptimize_extra_radio_field_4' => '1',
754
  'autoptimize_extra_text_field_2' => '',
755
  'autoptimize_extra_text_field_3' => '',
756
+ 'autoptimize_extra_text_field_7' => '',
757
  );
758
 
759
  return $defaults;
783
  */
784
  public static function get_ao_css_preload_polyfill()
785
  {
786
+ $preload_poly = apply_filters( 'autoptimize_css_preload_polyfill', '<script data-cfasync=\'false\'>!function(t){"use strict";t.loadCSS||(t.loadCSS=function(){});var e=loadCSS.relpreload={};if(e.support=function(){var e;try{e=t.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),e.bindMediaToggle=function(t){function e(){t.media=a}var a=t.media||"all";t.addEventListener?t.addEventListener("load",e):t.attachEvent&&t.attachEvent("onload",e),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(e,3e3)},e.poly=function(){if(!e.support())for(var a=t.document.getElementsByTagName("link"),n=0;n<a.length;n++){var o=a[n];"preload"!==o.rel||"style"!==o.getAttribute("as")||o.getAttribute("data-loadcss")||(o.setAttribute("data-loadcss",!0),e.bindMediaToggle(o))}},!e.support()){e.poly();var a=t.setInterval(e.poly,500);t.addEventListener?t.addEventListener("load",function(){e.poly(),t.clearInterval(a)}):t.attachEvent&&t.attachEvent("onload",function(){e.poly(),t.clearInterval(a)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:t.loadCSS=loadCSS}("undefined"!=typeof global?global:this);</script>' );
787
  return $preload_poly;
788
  }
789
 
794
  */
795
  public static function get_ao_css_preload_onload()
796
  {
797
+ $preload_onload = apply_filters( 'autoptimize_filter_css_preload_onload', "this.onload=null;this.rel='stylesheet'" );
798
  return $preload_onload;
799
  }
800
 
801
+ public function get( $key )
802
  {
803
  if ( ! is_array( $this->config ) ) {
804
  // Default config.
806
 
807
  // Override with user settings.
808
  foreach ( array_keys( $config ) as $name ) {
809
+ $conf = autoptimizeOptionWrapper::get_option( $name );
810
  if ( false !== $conf ) {
811
  // It was set before!
812
  $config[ $name ] = $conf;
824
  return false;
825
  }
826
 
827
+ private function get_futtta_feeds( $url ) {
828
  if ( $this->settings_screen_do_remote_http ) {
829
+ $rss = fetch_feed( $url );
830
  $maxitems = 0;
831
 
832
  if ( ! is_wp_error( $rss ) ) {
833
+ $maxitems = $rss->get_item_quantity( 7 );
834
  $rss_items = $rss->get_items( 0, $maxitems );
835
  }
836
  ?>
837
  <ul>
838
+ <?php if ( 0 == $maxitems ) : ?>
839
  <li><?php _e( 'No items', 'autoptimize' ); ?></li>
840
  <?php else : ?>
841
  <?php foreach ( $rss_items as $item ) : ?>
842
  <li>
843
  <a href="<?php echo esc_url( $item->get_permalink() ); ?>"
844
+ <?php // translators: the variable contains a date. ?>
845
+ title="<?php printf( __( 'Posted %s', 'autoptimize' ), $item->get_date( 'j F Y | g:i a' ) ); ?>">
846
  <?php echo esc_html( $item->get_title() ); ?>
847
  </a>
848
  </li>
853
  }
854
  }
855
 
 
856
  static function ao_admin_tabs()
857
  {
858
+ // based on http://wordpress.stackexchange.com/a/58826 .
859
+ $tabs = apply_filters( 'autoptimize_filter_settingsscreen_tabs', array( 'autoptimize' => __( 'JS, CSS &amp; HTML', 'autoptimize' ) ) );
860
+ $tab_content = '';
861
+ $tabs_count = count( $tabs );
862
  if ( $tabs_count > 1 ) {
863
  if ( isset( $_GET['page'] ) ) {
864
+ $current_id = $_GET['page'];
865
  } else {
866
+ $current_id = 'autoptimize';
867
  }
868
+ $tab_content .= '<h2 class="nav-tab-wrapper">';
869
+ foreach ( $tabs as $tab_id => $tab_name ) {
870
+ if ( $current_id == $tab_id ) {
871
  $class = ' nav-tab-active';
872
+ } else {
873
  $class = '';
874
  }
875
+ $tab_content .= '<a class="nav-tab' . $class . '" href="?page=' . $tab_id . '">' . $tab_name . '</a>';
876
  }
877
+ $tab_content .= '</h2>';
878
  } else {
879
+ $tab_content = '<hr/>';
880
  }
881
 
882
+ return $tab_content;
883
  }
884
 
885
  /**
904
  }
905
  return ( defined( 'DOING_AJAX' ) && DOING_AJAX );
906
  }
907
+
908
+ /**
909
+ * Returns true menu or tab is to be shown.
910
+ *
911
+ * @return bool
912
+ */
913
+ public static function should_show_menu_tabs() {
914
+ if ( ! is_multisite() || is_network_admin() || 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_enable_site_config' ) ) {
915
+ return true;
916
+ } else {
917
+ return false;
918
+ }
919
+ }
920
  }
classes/autoptimizeCriticalCSSSettings.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Temporary options page for AO26, will integrate CCSS functionality in next release.
4
+ */
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit;
8
+ }
9
+
10
+ class autoptimizeCriticalCSSSettings {
11
+ /**
12
+ * Options.
13
+ *
14
+ * @var bool
15
+ */
16
+ private $settings_screen_do_remote_http = true;
17
+
18
+ public function __construct()
19
+ {
20
+ $this->settings_screen_do_remote_http = apply_filters( 'autoptimize_settingsscreen_remotehttp', $this->settings_screen_do_remote_http );
21
+ $this->run();
22
+ }
23
+
24
+ protected function enabled()
25
+ {
26
+ return apply_filters( 'autoptimize_filter_show_criticalcsss_tabs', true );
27
+ }
28
+
29
+ public function run()
30
+ {
31
+ if ( $this->enabled() ) {
32
+ add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_critcss_tabs' ), 10, 1 );
33
+ }
34
+
35
+ if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
36
+ add_action( 'network_admin_menu', array( $this, 'add_critcss_admin_menu' ) );
37
+ } else {
38
+ add_action( 'admin_menu', array( $this, 'add_critcss_admin_menu' ) );
39
+ }
40
+ }
41
+
42
+ public function add_critcss_tabs( $in )
43
+ {
44
+ $in = array_merge( $in, array( 'ao_critcss' => '⚡ ' . __( 'Critical CSS', 'autoptimize' ) ) );
45
+
46
+ return $in;
47
+ }
48
+
49
+ public function add_critcss_admin_menu()
50
+ {
51
+ if ( $this->enabled() ) {
52
+ add_submenu_page( null, 'Critical CSS', 'Critical CSS', 'manage_options', 'ao_critcss', array( $this, 'ao_criticalcsssettings_page' ) );
53
+ }
54
+ }
55
+
56
+ public function ao_criticalcsssettings_page()
57
+ {
58
+ ?>
59
+ <style>
60
+ .ao_settings_div {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
61
+ .ao_settings_div .form-table th {font-weight: normal;}
62
+ </style>
63
+ <script>document.title = "Autoptimize: <?php _e( 'Critical CSS', 'autoptimize' ); ?> " + document.title;</script>
64
+ <div class="wrap">
65
+ <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
66
+ <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
67
+ <div class="ao_settings_div">
68
+ <?php
69
+ $ccss_explanation = '';
70
+
71
+ // get the HTML with the explanation of what critical CSS is.
72
+ if ( $this->settings_screen_do_remote_http ) {
73
+ $ccss_explanation = get_transient( 'ccss_explain_ao26' );
74
+ if ( empty( $ccss_explanation ) ) {
75
+ $ccss_expl_resp = wp_remote_get( 'https://misc.optimizingmatters.com/autoptimize_ccss_explain_ao26.html?ao_ver=' . AUTOPTIMIZE_PLUGIN_VERSION );
76
+ if ( ! is_wp_error( $ccss_expl_resp ) ) {
77
+ if ( '200' == wp_remote_retrieve_response_code( $ccss_expl_resp ) ) {
78
+ $ccss_explanation = wp_kses_post( wp_remote_retrieve_body( $ccss_expl_resp ) );
79
+ set_transient( 'ccss_explain_ao26', $ccss_explanation, WEEK_IN_SECONDS );
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ // placeholder text in case HTML is empty.
86
+ if ( empty( $ccss_explanation ) ) {
87
+ $ccss_explanation = '<h2>Fix render-blocking CSS!</h2><p>Significantly improve your first-paint times by making CSS non-render-blocking.</p><br /><a href="./plugin-install.php?s=autoptimize+criticalcss&tab=search&type=term" class="button">Install the "Autoptimize Critical CSS Power-Up"!</a>';
88
+ }
89
+
90
+ // and echo it.
91
+ echo $ccss_explanation . '<p>&nbsp;</p>';
92
+ ?>
93
+ </div>
94
+ </div>
95
+ <?php
96
+ }
97
+ }
classes/autoptimizeExtra.php CHANGED
@@ -16,6 +16,13 @@ class autoptimizeExtra
16
  */
17
  protected $options = array();
18
 
 
 
 
 
 
 
 
19
  /**
20
  * Creates an instance and calls run().
21
  *
@@ -30,19 +37,47 @@ class autoptimizeExtra
30
  $this->options = $options;
31
  }
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  public function run()
34
  {
35
  if ( is_admin() ) {
36
- add_action( 'admin_menu', array( $this, 'admin_menu' ) );
 
 
 
 
37
  add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_extra_tab' ) );
38
  } else {
39
  $this->run_on_frontend();
40
  }
41
  }
42
 
 
 
 
 
 
 
 
43
  public static function fetch_options()
44
  {
45
- $value = get_option( 'autoptimize_extra_settings' );
46
  if ( empty( $value ) ) {
47
  // Fallback to returning defaults when no stored option exists yet.
48
  $value = autoptimizeConfig::get_ao_extra_default_options();
@@ -139,6 +174,11 @@ class autoptimizeExtra
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 )
@@ -230,6 +270,10 @@ class autoptimizeExtra
230
  }
231
 
232
  $fonts_string = apply_filters( 'autoptimize_filter_extra_gfont_fontstring', str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) ) );
 
 
 
 
233
 
234
  if ( ! empty( $fonts_string ) ) {
235
  if ( '5' === $options['autoptimize_extra_radio_field_4'] ) {
@@ -253,9 +297,22 @@ class autoptimizeExtra
253
  $fonts_array = array_merge( $fonts_array, $_fonts['fonts'] );
254
  }
255
 
256
- $fonts_array = array_map( 'urldecode', $fonts_array );
257
- $fonts_markup = '<script data-cfasync="false" id="ao_optimized_gfonts_config" type="text/javascript">WebFontConfig={google:{families:' . wp_json_encode( $fonts_array ) . ' },classes:false, events:false, timeout:1500};</script>';
258
- $fonts_library_markup = '<script data-cfasync="false" id="ao_optimized_gfonts_webfontloader" type="text/javascript">(function() {var wf = document.createElement(\'script\');wf.src=\'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js\';wf.type=\'text/javascript\';wf.async=\'true\';var s=document.getElementsByTagName(\'script\')[0];s.parentNode.insertBefore(wf, s);})();</script>';
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  $in = substr_replace( $in, $fonts_library_markup . '</head>', strpos( $in, '</head>' ), strlen( '</head>' ) );
260
  }
261
 
@@ -274,18 +331,22 @@ class autoptimizeExtra
274
 
275
  public function filter_preconnect( $hints, $relation_type )
276
  {
277
- $options = $this->options;
 
278
 
279
  // Get settings and store in array.
280
- $preconns = array_filter( array_map( 'trim', explode( ',', $options['autoptimize_extra_text_field_2'] ) ) );
 
 
281
  $preconns = apply_filters( 'autoptimize_extra_filter_tobepreconn', $preconns );
282
 
283
  // Walk array, extract domain and add to new array with crossorigin attribute.
284
  foreach ( $preconns as $preconn ) {
 
285
  $parsed = parse_url( $preconn );
286
- if ( is_array( $parsed ) && empty( $parsed['scheme'] ) ) {
287
  $domain = '//' . $parsed['host'];
288
- } elseif ( is_array( $parsed ) ) {
289
  $domain = $parsed['scheme'] . '://' . $parsed['host'];
290
  }
291
 
@@ -325,22 +386,76 @@ class autoptimizeExtra
325
  return $in;
326
  }
327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  public function admin_menu()
329
  {
330
- add_submenu_page(
331
- null,
332
- 'autoptimize_extra',
333
- 'autoptimize_extra',
334
- 'manage_options',
335
- 'autoptimize_extra',
336
- array( $this, 'options_page' )
337
- );
338
- register_setting( 'autoptimize_extra_settings', 'autoptimize_extra_settings' );
 
 
 
339
  }
340
 
341
  public function add_extra_tab( $in )
342
  {
343
- $in = array_merge( $in, array( 'autoptimize_extra' => __( 'Extra', 'autoptimize' ) ) );
 
 
344
 
345
  return $in;
346
  }
@@ -359,16 +474,17 @@ class autoptimizeExtra
359
  #ao_settings_form .form-table th {font-weight: normal;}
360
  #autoptimize_extra_descr{font-size: 120%;}
361
  </style>
 
362
  <div class="wrap">
363
  <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
364
  <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
365
- <?php if ( 'on' !== get_option( 'autoptimize_js' ) && 'on' !== get_option( 'autoptimize_css' ) && 'on' !== get_option( 'autoptimize_html' ) && ! autoptimizeImages::imgopt_active() ) { ?>
366
  <div class="notice-warning notice"><p>
367
  <?php _e( 'Most of below Extra optimizations require at least one of HTML, JS, CSS or Image autoptimizations being active.', 'autoptimize' ); ?>
368
  </p></div>
369
  <?php } ?>
370
 
371
- <form id='ao_settings_form' action='options.php' method='post'>
372
  <?php settings_fields( 'autoptimize_extra_settings' ); ?>
373
  <h2><?php _e( 'Extra Auto-Optimizations', 'autoptimize' ); ?></h2>
374
  <span id='autoptimize_extra_descr'><?php _e( 'The following settings can improve your site\'s performance even more.', 'autoptimize' ); ?></span>
@@ -378,8 +494,10 @@ class autoptimizeExtra
378
  <td>
379
  <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="1" <?php if ( ! in_array( $gfonts, array( 2, 3, 4, 5 ) ) ) { echo 'checked'; } ?> ><?php _e( 'Leave as is', 'autoptimize' ); ?><br/>
380
  <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="2" <?php checked( 2, $gfonts, true ); ?> ><?php _e( 'Remove Google Fonts', 'autoptimize' ); ?><br/>
381
- <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="3" <?php checked( 3, $gfonts, true ); ?> ><?php _e( 'Combine and link in head (fonts load fast but are render-blocking)', 'autoptimize' ); ?><br/>
382
- <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="5" <?php checked( 5, $gfonts, true ); ?> ><?php _e( 'Combine and preload in head (fonts load late, but are not render-blocking)', 'autoptimize' ); ?><br/>
 
 
383
  <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/>
384
  </td>
385
  </tr>
@@ -401,6 +519,12 @@ class autoptimizeExtra
401
  <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_2]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_2', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_2'] ); } ?>'><br /><?php _e( 'Add 3rd party domains you want the browser to <a href="https://www.keycdn.com/support/preconnect/#primary" target="_blank">preconnect</a> to, separated by comma\'s. Make sure to include the correct protocol (HTTP or HTTPS).', 'autoptimize' ); ?></label>
402
  </td>
403
  </tr>
 
 
 
 
 
 
404
  <tr>
405
  <th scope="row"><?php _e( 'Async Javascript-files <em>(advanced users)</em>', 'autoptimize' ); ?></th>
406
  <td>
16
  */
17
  protected $options = array();
18
 
19
+ /**
20
+ * Singleton instance.
21
+ *
22
+ * @var self|null
23
+ */
24
+ protected static $instance = null;
25
+
26
  /**
27
  * Creates an instance and calls run().
28
  *
37
  $this->options = $options;
38
  }
39
 
40
+ /**
41
+ * Helper for getting a singleton instance. While being an
42
+ * anti-pattern generally, it comes in handy for now from a
43
+ * readability/maintainability perspective, until we get some
44
+ * proper dependency injection going.
45
+ *
46
+ * @return self
47
+ */
48
+ public static function instance()
49
+ {
50
+ if ( null === self::$instance ) {
51
+ self::$instance = new self();
52
+ }
53
+
54
+ return self::$instance;
55
+ }
56
+
57
  public function run()
58
  {
59
  if ( is_admin() ) {
60
+ if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
61
+ add_action( 'network_admin_menu', array( $this, 'admin_menu' ) );
62
+ } else {
63
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
64
+ }
65
  add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_extra_tab' ) );
66
  } else {
67
  $this->run_on_frontend();
68
  }
69
  }
70
 
71
+ public function set_options( array $options )
72
+ {
73
+ $this->options = $options;
74
+
75
+ return $this;
76
+ }
77
+
78
  public static function fetch_options()
79
  {
80
+ $value = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings' );
81
  if ( empty( $value ) ) {
82
  // Fallback to returning defaults when no stored option exists yet.
83
  $value = autoptimizeConfig::get_ao_extra_default_options();
174
  if ( ! empty( $options['autoptimize_extra_text_field_2'] ) || has_filter( 'autoptimize_extra_filter_tobepreconn' ) ) {
175
  add_filter( 'wp_resource_hints', array( $this, 'filter_preconnect' ), 10, 2 );
176
  }
177
+
178
+ // Preload!
179
+ if ( ! empty( $options['autoptimize_extra_text_field_7'] ) ) {
180
+ add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_preload' ), 10, 2 );
181
+ }
182
  }
183
 
184
  public function filter_remove_emoji_dns_prefetch( $urls, $relation_type )
270
  }
271
 
272
  $fonts_string = apply_filters( 'autoptimize_filter_extra_gfont_fontstring', str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) ) );
273
+ // only add display parameter if there is none in $fonts_string (by virtue of the filter).
274
+ if ( strpos( $fonts_string, 'display=' ) === false ) {
275
+ $fonts_string .= apply_filters( 'autoptimize_filter_extra_gfont_display', '&amp;display=swap' );
276
+ }
277
 
278
  if ( ! empty( $fonts_string ) ) {
279
  if ( '5' === $options['autoptimize_extra_radio_field_4'] ) {
297
  $fonts_array = array_merge( $fonts_array, $_fonts['fonts'] );
298
  }
299
 
300
+ $fonts_array = array_map( 'urldecode', $fonts_array );
301
+ $fonts_array = array_map(
302
+ function( $_f ) {
303
+ return trim( $_f, ',' );
304
+ },
305
+ $fonts_array
306
+ );
307
+
308
+ // type attrib on <script not added by default.
309
+ $type_js = '';
310
+ if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
311
+ $type_js = 'type="text/javascript" ';
312
+ }
313
+
314
+ $fonts_markup = '<script ' . $type_js . 'data-cfasync="false" id="ao_optimized_gfonts_config">WebFontConfig={google:{families:' . wp_json_encode( $fonts_array ) . ' },classes:false, events:false, timeout:1500};</script>';
315
+ $fonts_library_markup = '<script ' . $type_js . 'data-cfasync="false" id="ao_optimized_gfonts_webfontloader">(function() {var wf = document.createElement(\'script\');wf.src=\'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js\';wf.type=\'text/javascript\';wf.async=\'true\';var s=document.getElementsByTagName(\'script\')[0];s.parentNode.insertBefore(wf, s);})();</script>';
316
  $in = substr_replace( $in, $fonts_library_markup . '</head>', strpos( $in, '</head>' ), strlen( '</head>' ) );
317
  }
318
 
331
 
332
  public function filter_preconnect( $hints, $relation_type )
333
  {
334
+ $options = $this->options;
335
+ $preconns = array();
336
 
337
  // Get settings and store in array.
338
+ if ( array_key_exists( 'autoptimize_extra_text_field_2', $options ) ) {
339
+ $preconns = array_filter( array_map( 'trim', explode( ',', $options['autoptimize_extra_text_field_2'] ) ) );
340
+ }
341
  $preconns = apply_filters( 'autoptimize_extra_filter_tobepreconn', $preconns );
342
 
343
  // Walk array, extract domain and add to new array with crossorigin attribute.
344
  foreach ( $preconns as $preconn ) {
345
+ $domain = '';
346
  $parsed = parse_url( $preconn );
347
+ if ( is_array( $parsed ) && ! empty( $parsed['host'] ) && empty( $parsed['scheme'] ) ) {
348
  $domain = '//' . $parsed['host'];
349
+ } elseif ( is_array( $parsed ) && ! empty( $parsed['host'] ) ) {
350
  $domain = $parsed['scheme'] . '://' . $parsed['host'];
351
  }
352
 
386
  return $in;
387
  }
388
 
389
+ public function filter_preload( $in ) {
390
+ // make array from comma separated list.
391
+ $options = $this->options;
392
+ $preloads = array();
393
+ if ( array_key_exists( 'autoptimize_extra_text_field_7', $options ) ) {
394
+ $preloads = array_filter( array_map( 'trim', explode( ',', $options['autoptimize_extra_text_field_7'] ) ) );
395
+ }
396
+ $preloads = apply_filters( 'autoptimize_filter_extra_tobepreloaded', $preloads );
397
+
398
+ // immediately return if nothing to be preloaded.
399
+ if ( empty( $preloads ) ) {
400
+ return $in;
401
+ }
402
+
403
+ // iterate through array and add preload link to tmp string.
404
+ $preload_output = '';
405
+ foreach ( $preloads as $preload ) {
406
+ $crossorigin = '';
407
+ $preload_as = '';
408
+ $mime_type = '';
409
+
410
+ if ( autoptimizeUtils::str_ends_in( $preload, '.css' ) ) {
411
+ $preload_as = 'style';
412
+ } elseif ( autoptimizeUtils::str_ends_in( $preload, '.js' ) ) {
413
+ $preload_as = 'script';
414
+ } elseif ( autoptimizeUtils::str_ends_in( $preload, '.woff' ) || autoptimizeUtils::str_ends_in( $preload, '.woff2' ) || autoptimizeUtils::str_ends_in( $preload, '.ttf' ) || autoptimizeUtils::str_ends_in( $preload, '.eot' ) ) {
415
+ $preload_as = 'font';
416
+ $crossorigin = ' crossorigin';
417
+ $mime_type = ' type="font/' . pathinfo( $preload, PATHINFO_EXTENSION ) . '"';
418
+ if ( ' type="font/eot"' === $mime_type ) {
419
+ $mime_type = 'application/vnd.ms-fontobject';
420
+ }
421
+ } elseif ( autoptimizeUtils::str_ends_in( $preload, '.jpeg' ) || autoptimizeUtils::str_ends_in( $preload, '.jpg' ) || autoptimizeUtils::str_ends_in( $preload, '.webp' ) || autoptimizeUtils::str_ends_in( $preload, '.png' ) || autoptimizeUtils::str_ends_in( $preload, '.gif' ) ) {
422
+ $preload_as = 'image';
423
+ } else {
424
+ $preload_as = 'other';
425
+ }
426
+
427
+ $preload_output .= '<link rel="preload" href="' . $preload . '" as="' . $preload_as . '"' . $mime_type . $crossorigin . '>';
428
+ }
429
+ $preload_output = apply_filters( 'autoptimize_filter_extra_preload_output', $preload_output );
430
+
431
+ // add string to head (before first link node by default).
432
+ $preload_inject = apply_filters( 'autoptimize_filter_extra_preload_inject', '<link' );
433
+ $position = autoptimizeUtils::strpos( $in, $preload_inject );
434
+
435
+ return autoptimizeUtils::substr_replace( $in, $preload_output . $preload_inject, $position, strlen( $preload_inject ) );
436
+ }
437
+
438
  public function admin_menu()
439
  {
440
+ // no acces if multisite and not network admin and no site config allowed.
441
+ if ( autoptimizeConfig::should_show_menu_tabs() ) {
442
+ add_submenu_page(
443
+ null,
444
+ 'autoptimize_extra',
445
+ 'autoptimize_extra',
446
+ 'manage_options',
447
+ 'autoptimize_extra',
448
+ array( $this, 'options_page' )
449
+ );
450
+ register_setting( 'autoptimize_extra_settings', 'autoptimize_extra_settings' );
451
+ }
452
  }
453
 
454
  public function add_extra_tab( $in )
455
  {
456
+ if ( autoptimizeConfig::should_show_menu_tabs() ) {
457
+ $in = array_merge( $in, array( 'autoptimize_extra' => __( 'Extra', 'autoptimize' ) ) );
458
+ }
459
 
460
  return $in;
461
  }
474
  #ao_settings_form .form-table th {font-weight: normal;}
475
  #autoptimize_extra_descr{font-size: 120%;}
476
  </style>
477
+ <script>document.title = "Autoptimize: <?php _e( 'Extra', 'autoptimize' ); ?> " + document.title;</script>
478
  <div class="wrap">
479
  <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
480
  <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
481
+ <?php if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_js' ) && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_html' ) && ! autoptimizeImages::imgopt_active() ) { ?>
482
  <div class="notice-warning notice"><p>
483
  <?php _e( 'Most of below Extra optimizations require at least one of HTML, JS, CSS or Image autoptimizations being active.', 'autoptimize' ); ?>
484
  </p></div>
485
  <?php } ?>
486
 
487
+ <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
488
  <?php settings_fields( 'autoptimize_extra_settings' ); ?>
489
  <h2><?php _e( 'Extra Auto-Optimizations', 'autoptimize' ); ?></h2>
490
  <span id='autoptimize_extra_descr'><?php _e( 'The following settings can improve your site\'s performance even more.', 'autoptimize' ); ?></span>
494
  <td>
495
  <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="1" <?php if ( ! in_array( $gfonts, array( 2, 3, 4, 5 ) ) ) { echo 'checked'; } ?> ><?php _e( 'Leave as is', 'autoptimize' ); ?><br/>
496
  <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="2" <?php checked( 2, $gfonts, true ); ?> ><?php _e( 'Remove Google Fonts', 'autoptimize' ); ?><br/>
497
+ <?php // translators: "display:swap" should remain untranslated, will be shown in code tags. ?>
498
+ <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="3" <?php checked( 3, $gfonts, true ); ?> ><?php echo __( 'Combine and link in head (fonts load fast but are render-blocking)', 'autoptimize' ) . ', ' . sprintf( __( 'includes %1$sdisplay:swap%2$s.', 'autoptimize' ), '<code>', '</code>' ); ?><br/>
499
+ <?php // translators: "display:swap" should remain untranslated, will be shown in code tags. ?>
500
+ <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="5" <?php checked( 5, $gfonts, true ); ?> ><?php echo __( 'Combine and preload in head (fonts load late, but are not render-blocking)', 'autoptimize' ) . ', ' . sprintf( __( 'includes %1$sdisplay:swap%2$s.', 'autoptimize' ), '<code>', '</code>' ); ?><br/>
501
  <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/>
502
  </td>
503
  </tr>
519
  <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_2]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_2', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_2'] ); } ?>'><br /><?php _e( 'Add 3rd party domains you want the browser to <a href="https://www.keycdn.com/support/preconnect/#primary" target="_blank">preconnect</a> to, separated by comma\'s. Make sure to include the correct protocol (HTTP or HTTPS).', 'autoptimize' ); ?></label>
520
  </td>
521
  </tr>
522
+ <tr>
523
+ <th scope="row"><?php _e( 'Preload specific requests <em>(advanced users)</em>', 'autoptimize' ); ?></th>
524
+ <td>
525
+ <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_7]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_7', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_7'] ); } ?>'><br /><?php _e( 'Comma-separated list with full URL\'s of to to-be-preloaded resources. To be used sparingly!', 'autoptimize' ); ?></label>
526
+ </td>
527
+ </tr>
528
  <tr>
529
  <th scope="row"><?php _e( 'Async Javascript-files <em>(advanced users)</em>', 'autoptimize' ); ?></th>
530
  <td>
classes/autoptimizeImages.php CHANGED
@@ -42,14 +42,14 @@ class autoptimizeImages
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 );
@@ -66,7 +66,7 @@ class autoptimizeImages
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 {
@@ -97,10 +97,14 @@ class autoptimizeImages
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
 
@@ -157,7 +161,9 @@ class autoptimizeImages
157
  if ( $this->should_lazyload() ) {
158
  add_action(
159
  'wp_footer',
160
- array( $this, 'add_lazyload_js_footer' )
 
 
161
  );
162
  }
163
  }
@@ -175,7 +181,8 @@ class autoptimizeImages
175
 
176
  $do_cdn = true;
177
  $_userstatus = $this->get_imgopt_provider_userstatus();
178
- if ( -2 == $_userstatus['Status'] ) {
 
179
  $do_cdn = false;
180
  }
181
 
@@ -302,7 +309,7 @@ class autoptimizeImages
302
  // get CDN domain once.
303
  static $cdn_domain = null;
304
  if ( is_null( $cdn_domain ) ) {
305
- $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
306
  if ( ! empty( $cdn_url ) ) {
307
  $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
308
  } else {
@@ -326,16 +333,21 @@ class autoptimizeImages
326
 
327
  // Do the work on cache miss only.
328
  if ( ! isset( $cache[ $in ] ) ) {
329
- // Default to what was given to us.
330
  $result = trim( $in );
331
- if ( autoptimizeUtils::is_protocol_relative( $in ) ) {
332
- $result = $parsed_site_url['scheme'] . ':' . $in;
333
- } elseif ( 0 === strpos( $in, '/' ) ) {
 
 
 
 
 
 
334
  // Root-relative...
335
- $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'];
336
- $result .= $in;
337
- } elseif ( ! empty( $cdn_domain ) && strpos( $in, $cdn_domain ) !== 0 ) {
338
- $result = str_replace( $cdn_domain, $parsed_site_url['host'], $in );
339
  }
340
 
341
  $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );
@@ -381,7 +393,7 @@ class autoptimizeImages
381
  if ( null === $cdn_url ) {
382
  $cdn_url = apply_filters(
383
  'autoptimize_filter_base_cdnurl',
384
- get_option( 'autoptimize_cdn_url', '' )
385
  );
386
  }
387
 
@@ -543,6 +555,7 @@ class autoptimizeImages
543
  $_url = $url;
544
  }
545
 
 
546
  if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
547
  $lqip_w = '';
548
  $lqip_h = '';
@@ -586,7 +599,7 @@ class autoptimizeImages
586
  );
587
  }
588
 
589
- // lazyload: restore noscript tags + lazyload picture source tags.
590
  if ( $this->should_lazyload() ) {
591
  $out = autoptimizeBase::restore_marked_content(
592
  'SCRIPT',
@@ -594,6 +607,7 @@ class autoptimizeImages
594
  );
595
 
596
  $out = $this->process_picture_tag( $out, true, true );
 
597
  } else {
598
  $out = $this->process_picture_tag( $out, true, false );
599
  }
@@ -648,7 +662,7 @@ class autoptimizeImages
648
  }
649
 
650
  public function should_lazyload( $context = '' ) {
651
- if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) ) {
652
  $lazyload_return = true;
653
  } else {
654
  $lazyload_return = false;
@@ -658,6 +672,14 @@ class autoptimizeImages
658
  return $lazyload_return;
659
  }
660
 
 
 
 
 
 
 
 
 
661
  public function filter_lazyload_images( $in )
662
  {
663
  // only used is image optimization is NOT active but lazyload is.
@@ -684,6 +706,9 @@ class autoptimizeImages
684
  // and also lazyload picture tag.
685
  $out = $this->process_picture_tag( $out, false, true );
686
 
 
 
 
687
  // restore noscript tags.
688
  $out = autoptimizeBase::restore_marked_content(
689
  'SCRIPT',
@@ -699,10 +724,12 @@ class autoptimizeImages
699
  $tag = $this->maybe_fix_missing_quotes( $tag );
700
 
701
  // store original tag for use in noscript version.
702
- $noscript_tag = '<noscript>' . $tag . '</noscript>';
 
 
703
 
704
  // insert lazyload class.
705
- $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
706
 
707
  if ( ! $placeholder || empty( $placeholder ) ) {
708
  // get image width & heigth for placeholder fun (and to prevent content reflow).
@@ -720,12 +747,13 @@ class autoptimizeImages
720
  // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
721
  $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
722
  }
723
- $tag = str_replace( ' src=', ' src=\'' . $placeholder . '\' data-src=', $tag );
724
- $tag = str_replace( ' srcset=', ' data-srcset=', $tag );
 
725
 
726
  // move sizes to data-sizes unless filter says no.
727
  if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
728
- $tag = str_replace( 'sizes=', 'data-sizes=', $tag );
729
  }
730
 
731
  // add the noscript-tag from earlier.
@@ -737,16 +765,26 @@ class autoptimizeImages
737
  }
738
 
739
  public function add_lazyload_js_footer() {
 
 
 
 
740
  // The JS will by default be excluded form autoptimization but this can be changed with a filter.
741
  $noptimize_flag = '';
742
  if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
743
  $noptimize_flag = ' data-noptimize="1"';
744
  }
745
 
 
 
 
 
 
 
746
  // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
747
  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>' );
748
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
749
- echo '<script async' . $noptimize_flag . ' src=\'' . plugins_url( 'external/js/lazysizes.min.js', __FILE__ ) . '\'></script>';
750
 
751
  // And add webp detection and loading JS.
752
  if ( $this->should_webp() ) {
@@ -756,6 +794,19 @@ class autoptimizeImages
756
  }
757
  }
758
 
 
 
 
 
 
 
 
 
 
 
 
 
 
759
  public function get_lazyload_exclusions() {
760
  // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
761
  static $exclude_lazyload_array = null;
@@ -785,7 +836,7 @@ class autoptimizeImages
785
  if ( strpos( $tag, 'class=' ) !== false ) {
786
  $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
787
  } else {
788
- $tag = str_replace( '<img ', '<img class="' . trim( $target_class ) . '" ', $tag );
789
  }
790
 
791
  return $tag;
@@ -832,7 +883,7 @@ class autoptimizeImages
832
  $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
833
  }
834
  // should we lazy-load?
835
- if ( $lazy && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
836
  $_picture_replacement = str_replace( ' srcset=', ' data-srcset=', $_picture_replacement );
837
  }
838
  $to_replace_pict[ $_source[0] ] = $_picture_replacement;
@@ -846,10 +897,38 @@ class autoptimizeImages
846
  return $out;
847
  }
848
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
849
  public function maybe_fix_missing_quotes( $tag_in ) {
850
- // W3TC's Minify_HTML class removes quotes around attribute value, this re-adds them.
851
  if ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) {
852
- return preg_replace( '/=([^("|\')]*)(\s|>)/U', '=\'$1\'$2', $tag_in );
853
  } else {
854
  return $tag_in;
855
  }
@@ -860,20 +939,25 @@ class autoptimizeImages
860
  */
861
  public function imgopt_admin_menu()
862
  {
863
- add_submenu_page(
864
- null,
865
- 'autoptimize_imgopt',
866
- 'autoptimize_imgopt',
867
- 'manage_options',
868
- 'autoptimize_imgopt',
869
- array( $this, 'imgopt_options_page' )
870
- );
871
- register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
 
 
 
872
  }
873
 
874
  public function add_imgopt_tab( $in )
875
  {
876
- $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
 
 
877
 
878
  return $in;
879
  }
@@ -893,6 +977,7 @@ class autoptimizeImages
893
  #ao_settings_form .form-table th {font-weight: normal;}
894
  #autoptimize_imgopt_descr{font-size: 120%;}
895
  </style>
 
896
  <div class="wrap">
897
  <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
898
  <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
@@ -919,7 +1004,7 @@ class autoptimizeImages
919
  ?>
920
  </p></div>
921
  <?php } ?>
922
- <form id='ao_settings_form' action='options.php' method='post'>
923
  <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
924
  <h2><?php _e( 'Image optimization', 'autoptimize' ); ?></h2>
925
  <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>
@@ -930,7 +1015,7 @@ class autoptimizeImages
930
  <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>
931
  <?php
932
  // show shortpixel status.
933
- $_notice = autoptimizeImages::instance()->get_status_notice();
934
  if ( $_notice ) {
935
  switch ( $_notice['status'] ) {
936
  case 2:
@@ -940,9 +1025,8 @@ class autoptimizeImages
940
  $_notice_color = 'orange';
941
  break;
942
  case -1:
943
- $_notice_color = 'red';
944
- break;
945
  case -2:
 
946
  $_notice_color = 'red';
947
  break;
948
  default:
@@ -951,7 +1035,7 @@ class autoptimizeImages
951
  echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
952
  } else {
953
  // translators: link points to shortpixel.
954
- $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>' );
955
  if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
956
  $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' );
957
  } else {
@@ -962,6 +1046,7 @@ class autoptimizeImages
962
  }
963
  // translators: link points to shortpixel FAQ.
964
  $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>' );
 
965
  // translators: links points to shortpixel TOS & Privacy Policy.
966
  $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>' );
967
  echo apply_filters( 'autoptimize_imgopt_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
@@ -1052,10 +1137,12 @@ class autoptimizeImages
1052
  */
1053
  public function get_imgopt_status_notice() {
1054
  if ( $this->imgopt_active() ) {
1055
- $_imgopt_notice = '';
1056
- $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
1057
- $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
1058
- $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
 
 
1059
 
1060
  if ( is_array( $_stat ) ) {
1061
  if ( 1 == $_stat['Status'] ) {
@@ -1063,24 +1150,35 @@ class autoptimizeImages
1063
  $_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>' );
1064
  } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1065
  // translators: "add more credits" will appear in a "a href".
1066
- $_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>' );
 
 
 
 
 
 
 
 
 
 
 
 
 
1067
  $_imgopt_stats_refresh_url = add_query_arg( array(
1068
  'page' => 'autoptimize_imgopt',
1069
  'refreshImgProvStats' => '1',
1070
  ), admin_url( 'options-general.php' ) );
1071
  if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
1072
- $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( get_option( 'time_format' ), $_stat['timestamp'] );
1073
  } else {
1074
  $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1075
  }
1076
  $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1077
  // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
1078
  $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1079
- } else {
1080
- $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1081
- // translators: "log in to check your account" will appear in a "a href".
1082
- $_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>' );
1083
  }
 
 
1084
  $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
1085
 
1086
  return array(
@@ -1124,7 +1222,7 @@ class autoptimizeImages
1124
  if ( ! is_wp_error( $response ) ) {
1125
  if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
1126
  $stats = json_decode( wp_remote_retrieve_body( $response ), true );
1127
- update_option( 'autoptimize_imgopt_provider_stat', $stats );
1128
  }
1129
  }
1130
  }
@@ -1150,12 +1248,12 @@ class autoptimizeImages
1150
  if ( null === $launch_status ) {
1151
  $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
1152
  $magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
1153
- $has_launched = get_option( 'autoptimize_imgopt_launched', '' );
1154
  $launch_status = false;
1155
  if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
1156
  $launch_status = true;
1157
  if ( ! $has_launched ) {
1158
- update_option( 'autoptimize_imgopt_launched', 'on' );
1159
  }
1160
  }
1161
  }
@@ -1173,7 +1271,7 @@ class autoptimizeImages
1173
  static $_provider_userstatus = null;
1174
 
1175
  if ( is_null( $_provider_userstatus ) ) {
1176
- $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
1177
  if ( is_array( $_stat ) ) {
1178
  if ( array_key_exists( 'Status', $_stat ) ) {
1179
  $_provider_userstatus['Status'] = $_stat['Status'];
@@ -1187,41 +1285,13 @@ class autoptimizeImages
1187
  // if no timestamp then we return "".
1188
  $_provider_userstatus['timestamp'] = '';
1189
  }
 
 
 
 
1190
  }
1191
  }
1192
 
1193
  return $_provider_userstatus;
1194
  }
1195
-
1196
- public function get_status_notice() {
1197
- if ( $this->imgopt_active() ) {
1198
- $notice = '';
1199
- $stat = $this->get_imgopt_provider_userstatus();
1200
- $upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
1201
- $assoc = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1202
-
1203
- if ( is_array( $stat ) ) {
1204
- if ( 1 == $stat['Status'] ) {
1205
- // translators: "add more credits" will appear in a "a href".
1206
- $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>' );
1207
- } elseif ( -1 == $stat['Status'] || -2 == $stat['Status'] ) {
1208
- // translators: "add more credits" will appear in a "a href".
1209
- $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>' );
1210
- // translators: "associate your domain" will appear in a "a href".
1211
- $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>' );
1212
- } else {
1213
- $upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1214
- // translators: "log in to check your account" will appear in a "a href".
1215
- $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>' );
1216
- }
1217
- $notice = apply_filters( 'autoptimize_filter_imgopt_notice', $notice );
1218
-
1219
- return array(
1220
- 'status' => $stat['Status'],
1221
- 'notice' => $notice,
1222
- );
1223
- }
1224
- }
1225
- return false;
1226
- }
1227
  }
42
 
43
  public static function fetch_options()
44
  {
45
+ $value = autoptimizeOptionWrapper::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'] = autoptimizeOptionWrapper::get_option( 'autoptimize_service_availablity' );
53
 
54
  if ( empty( $value['availabilities'] ) ) {
55
  $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
66
  static $imgopt_active = null;
67
 
68
  if ( null === $imgopt_active ) {
69
+ $opts = autoptimizeOptionWrapper::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 {
97
  public function run()
98
  {
99
  if ( is_admin() ) {
100
+ if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
101
+ add_action( 'network_admin_menu', array( $this, 'imgopt_admin_menu' ) );
102
+ } else {
103
+ add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
104
+ }
105
  add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_imgopt_tab' ), 9 );
106
  } else {
107
+ add_action( 'wp', array( $this, 'run_on_frontend' ) );
108
  }
109
  }
110
 
161
  if ( $this->should_lazyload() ) {
162
  add_action(
163
  'wp_footer',
164
+ array( $this, 'add_lazyload_js_footer' ),
165
+ 10,
166
+ 0
167
  );
168
  }
169
  }
181
 
182
  $do_cdn = true;
183
  $_userstatus = $this->get_imgopt_provider_userstatus();
184
+ if ( isset( $_userstatus['Status'] ) && ( -2 == $_userstatus['Status'] || -3 == $_userstatus['Status'] ) ) {
185
+ // don't even attempt to put images on CDN if heavily exceeded threshold or if site not reachable.
186
  $do_cdn = false;
187
  }
188
 
309
  // get CDN domain once.
310
  static $cdn_domain = null;
311
  if ( is_null( $cdn_domain ) ) {
312
+ $cdn_url = $this->get_cdn_url();
313
  if ( ! empty( $cdn_url ) ) {
314
  $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
315
  } else {
333
 
334
  // Do the work on cache miss only.
335
  if ( ! isset( $cache[ $in ] ) ) {
336
+ // Default to (the trimmed version of) what was given to us.
337
  $result = trim( $in );
338
+
339
+ // Some silly plugins wrap background images in html-encoded quotes, so remove those from the img url.
340
+ if ( strpos( $result, '&quot;' ) !== false ) {
341
+ $result = str_replace( '&quot;', '', $result );
342
+ }
343
+
344
+ if ( autoptimizeUtils::is_protocol_relative( $result ) ) {
345
+ $result = $parsed_site_url['scheme'] . ':' . $result;
346
+ } elseif ( 0 === strpos( $result, '/' ) ) {
347
  // Root-relative...
348
+ $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $result;
349
+ } elseif ( ! empty( $cdn_domain ) && strpos( $result, $cdn_domain ) !== 0 ) {
350
+ $result = str_replace( $cdn_domain, $parsed_site_url['host'], $result );
 
351
  }
352
 
353
  $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );
393
  if ( null === $cdn_url ) {
394
  $cdn_url = apply_filters(
395
  'autoptimize_filter_base_cdnurl',
396
+ autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' )
397
  );
398
  }
399
 
555
  $_url = $url;
556
  }
557
 
558
+ $placeholder = '';
559
  if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
560
  $lqip_w = '';
561
  $lqip_h = '';
599
  );
600
  }
601
 
602
+ // lazyload: restore noscript tags + lazyload picture source tags and bgimage.
603
  if ( $this->should_lazyload() ) {
604
  $out = autoptimizeBase::restore_marked_content(
605
  'SCRIPT',
607
  );
608
 
609
  $out = $this->process_picture_tag( $out, true, true );
610
+ $out = $this->process_bgimage( $out );
611
  } else {
612
  $out = $this->process_picture_tag( $out, true, false );
613
  }
662
  }
663
 
664
  public function should_lazyload( $context = '' ) {
665
+ if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && false === $this->check_nolazy() ) {
666
  $lazyload_return = true;
667
  } else {
668
  $lazyload_return = false;
672
  return $lazyload_return;
673
  }
674
 
675
+ public function check_nolazy() {
676
+ if ( array_key_exists( 'ao_nolazy', $_GET ) && '1' === $_GET['ao_nolazy'] ) {
677
+ return true;
678
+ } else {
679
+ return false;
680
+ }
681
+ }
682
+
683
  public function filter_lazyload_images( $in )
684
  {
685
  // only used is image optimization is NOT active but lazyload is.
706
  // and also lazyload picture tag.
707
  $out = $this->process_picture_tag( $out, false, true );
708
 
709
+ // and inline style blocks with background-image.
710
+ $out = $this->process_bgimage( $out );
711
+
712
  // restore noscript tags.
713
  $out = autoptimizeBase::restore_marked_content(
714
  'SCRIPT',
724
  $tag = $this->maybe_fix_missing_quotes( $tag );
725
 
726
  // store original tag for use in noscript version.
727
+ $noscript_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>';
728
+
729
+ $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
730
 
731
  // insert lazyload class.
732
+ $tag = $this->inject_classes_in_tag( $tag, "$lazyload_class " );
733
 
734
  if ( ! $placeholder || empty( $placeholder ) ) {
735
  // get image width & heigth for placeholder fun (and to prevent content reflow).
747
  // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
748
  $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
749
  }
750
+
751
+ $tag = preg_replace( '/(\s)src=/', ' src=\'' . $placeholder . '\' data-src=', $tag );
752
+ $tag = preg_replace( '/(\s)srcset=/', ' data-srcset=', $tag );
753
 
754
  // move sizes to data-sizes unless filter says no.
755
  if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
756
+ $tag = str_replace( ' sizes=', ' data-sizes=', $tag );
757
  }
758
 
759
  // add the noscript-tag from earlier.
765
  }
766
 
767
  public function add_lazyload_js_footer() {
768
+ if ( false === autoptimizeMain::should_buffer() ) {
769
+ return;
770
+ }
771
+
772
  // The JS will by default be excluded form autoptimization but this can be changed with a filter.
773
  $noptimize_flag = '';
774
  if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
775
  $noptimize_flag = ' data-noptimize="1"';
776
  }
777
 
778
+ $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js', __FILE__ );
779
+ $cdn_url = $this->get_cdn_url();
780
+ if ( ! empty( $cdn_url ) ) {
781
+ $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
782
+ }
783
+
784
  // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
785
  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>' );
786
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
787
+ echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
788
 
789
  // And add webp detection and loading JS.
790
  if ( $this->should_webp() ) {
794
  }
795
  }
796
 
797
+ public function get_cdn_url() {
798
+ // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
799
+ static $cdn_url = null;
800
+
801
+ if ( null === $cdn_url ) {
802
+ $cdn_url = autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' );
803
+ $cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $cdn_url );
804
+ $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url );
805
+ }
806
+
807
+ return $cdn_url;
808
+ }
809
+
810
  public function get_lazyload_exclusions() {
811
  // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
812
  static $exclude_lazyload_array = null;
836
  if ( strpos( $tag, 'class=' ) !== false ) {
837
  $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
838
  } else {
839
+ $tag = preg_replace( '/(<img)\s/', '$1 class="' . trim( $target_class ) . '" ', $tag );
840
  }
841
 
842
  return $tag;
883
  $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
884
  }
885
  // should we lazy-load?
886
+ if ( $lazy && $this->should_lazyload() && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
887
  $_picture_replacement = str_replace( ' srcset=', ' data-srcset=', $_picture_replacement );
888
  }
889
  $to_replace_pict[ $_source[0] ] = $_picture_replacement;
897
  return $out;
898
  }
899
 
900
+ public function process_bgimage( $in ) {
901
+ if ( strpos( $in, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_lazyload_backgroundimages', true ) ) {
902
+ $out = preg_replace_callback(
903
+ '/(<(?:article|aside|body|div|footer|header|p|section|table)[^>]*)\sstyle=(?:"|\').*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)[^>]*/',
904
+ array( $this, 'lazyload_bgimg_callback' ),
905
+ $in
906
+ );
907
+ return $out;
908
+ }
909
+ return $in;
910
+ }
911
+
912
+ public function lazyload_bgimg_callback( $matches ) {
913
+ if ( str_ireplace( $this->get_lazyload_exclusions(), '', $matches[0] ) === $matches[0] ) {
914
+ // get placeholder & lazyload class strings.
915
+ $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( 500, 300 ) );
916
+ $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
917
+ // replace background-image URL with SVG placeholder.
918
+ $out = str_replace( $matches[2], $placeholder, $matches[0] );
919
+ // add data-bg attribute with real background-image URL for lazyload to pick up.
920
+ $out = str_replace( $matches[1], $matches[1] . ' data-bg="' . $matches[2] . '"', $out );
921
+ // add lazyload class to tag.
922
+ $out = $this->inject_classes_in_tag( $out, "$lazyload_class " );
923
+ return $out;
924
+ }
925
+ return $matches[0];
926
+ }
927
+
928
  public function maybe_fix_missing_quotes( $tag_in ) {
929
+ // W3TC's Minify_HTML class removes quotes around attribute value, this re-adds them for the class attribute only so we can safely add the lazyload class.
930
  if ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) {
931
+ return preg_replace( '/class\s?=([^("|\')]*)(\s|>)/U', 'class=\'$1\'$2', $tag_in );
932
  } else {
933
  return $tag_in;
934
  }
939
  */
940
  public function imgopt_admin_menu()
941
  {
942
+ // no acces if multisite and not network admin and no site config allowed.
943
+ if ( autoptimizeConfig::should_show_menu_tabs() ) {
944
+ add_submenu_page(
945
+ null,
946
+ 'autoptimize_imgopt',
947
+ 'autoptimize_imgopt',
948
+ 'manage_options',
949
+ 'autoptimize_imgopt',
950
+ array( $this, 'imgopt_options_page' )
951
+ );
952
+ register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
953
+ }
954
  }
955
 
956
  public function add_imgopt_tab( $in )
957
  {
958
+ if ( autoptimizeConfig::should_show_menu_tabs() ) {
959
+ $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
960
+ }
961
 
962
  return $in;
963
  }
977
  #ao_settings_form .form-table th {font-weight: normal;}
978
  #autoptimize_imgopt_descr{font-size: 120%;}
979
  </style>
980
+ <script>document.title = "Autoptimize: <?php _e( 'Images', 'autoptimize' ); ?> " + document.title;</script>
981
  <div class="wrap">
982
  <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
983
  <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
1004
  ?>
1005
  </p></div>
1006
  <?php } ?>
1007
+ <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
1008
  <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
1009
  <h2><?php _e( 'Image optimization', 'autoptimize' ); ?></h2>
1010
  <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>
1015
  <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>
1016
  <?php
1017
  // show shortpixel status.
1018
+ $_notice = autoptimizeImages::instance()->get_imgopt_status_notice();
1019
  if ( $_notice ) {
1020
  switch ( $_notice['status'] ) {
1021
  case 2:
1025
  $_notice_color = 'orange';
1026
  break;
1027
  case -1:
 
 
1028
  case -2:
1029
+ case -3:
1030
  $_notice_color = 'red';
1031
  break;
1032
  default:
1035
  echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
1036
  } else {
1037
  // translators: link points to shortpixel.
1038
+ $upsell_msg_1 = '<p>' . sprintf( __( 'Get more Google love and improve your website\'s loading speed by having your publicly available images optimized on the fly (also in the "next-gen" WebP image format) by %1$sShortPixel%2$s and then cached and served fast from Shortpixel\'s global CDN.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
1039
  if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
1040
  $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' );
1041
  } else {
1046
  }
1047
  // translators: link points to shortpixel FAQ.
1048
  $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>' );
1049
+ $faqcopy = $faqcopy . ' ' . __( 'Only works for sites/ images that are publicly available.', 'autoptimize' );
1050
  // translators: links points to shortpixel TOS & Privacy Policy.
1051
  $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>' );
1052
  echo apply_filters( 'autoptimize_imgopt_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
1137
  */
1138
  public function get_imgopt_status_notice() {
1139
  if ( $this->imgopt_active() ) {
1140
+ $_imgopt_notice = '';
1141
+ $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1142
+ $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
1143
+ $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
1144
+ $_imgopt_assoc = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1145
+ $_imgopt_unreach = 'https://shortpixel.helpscoutdocs.com/article/148-why-are-my-images-redirected-from-cdn-shortpixel-ai';
1146
 
1147
  if ( is_array( $_stat ) ) {
1148
  if ( 1 == $_stat['Status'] ) {
1150
  $_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>' );
1151
  } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1152
  // translators: "add more credits" will appear in a "a href".
1153
+ $_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>' );
1154
+ // translators: "associate your domain" will appear in a "a href".
1155
+ $_imgopt_notice = $_imgopt_notice . ' ' . sprintf( __( 'If you already have enough credits then you may need to %1$sassociate your domain%2$s to your Shortpixel account.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $_imgopt_assoc . '" target="_blank">', '</a>' );
1156
+ } elseif ( -3 == $_stat['Status'] ) {
1157
+ // translators: "add more credits" will appear in a "a href".
1158
+ $_imgopt_notice = sprintf( __( 'It seems ShortPixel image optimization is not able to fetch images from your site, %1$scheck the documentation here%2$s for more information', 'autoptimize' ), '<a href="' . $_imgopt_unreach . '" target="_blank">', '</a>' );
1159
+ } else {
1160
+ $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1161
+ // translators: "log in to check your account" will appear in a "a href".
1162
+ $_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>' );
1163
+ }
1164
+
1165
+ // add info on freshness + refresh link if status is not 2 (good shape).
1166
+ if ( 2 != $_stat['Status'] ) {
1167
  $_imgopt_stats_refresh_url = add_query_arg( array(
1168
  'page' => 'autoptimize_imgopt',
1169
  'refreshImgProvStats' => '1',
1170
  ), admin_url( 'options-general.php' ) );
1171
  if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
1172
+ $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( autoptimizeOptionWrapper::get_option( 'time_format' ), $_stat['timestamp'] );
1173
  } else {
1174
  $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1175
  }
1176
  $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1177
  // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
1178
  $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
 
 
 
 
1179
  }
1180
+
1181
+ // and make the full notice filterable.
1182
  $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
1183
 
1184
  return array(
1222
  if ( ! is_wp_error( $response ) ) {
1223
  if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
1224
  $stats = json_decode( wp_remote_retrieve_body( $response ), true );
1225
+ autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_provider_stat', $stats );
1226
  }
1227
  }
1228
  }
1248
  if ( null === $launch_status ) {
1249
  $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
1250
  $magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
1251
+ $has_launched = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_launched', '' );
1252
  $launch_status = false;
1253
  if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
1254
  $launch_status = true;
1255
  if ( ! $has_launched ) {
1256
+ autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_launched', 'on' );
1257
  }
1258
  }
1259
  }
1271
  static $_provider_userstatus = null;
1272
 
1273
  if ( is_null( $_provider_userstatus ) ) {
1274
+ $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1275
  if ( is_array( $_stat ) ) {
1276
  if ( array_key_exists( 'Status', $_stat ) ) {
1277
  $_provider_userstatus['Status'] = $_stat['Status'];
1285
  // if no timestamp then we return "".
1286
  $_provider_userstatus['timestamp'] = '';
1287
  }
1288
+ } else {
1289
+ // no provider_stat yet, assume/ return all OK.
1290
+ $_provider_userstatus['Status'] = 2;
1291
+ $_provider_userstatus['timestamp'] = '';
1292
  }
1293
  }
1294
 
1295
  return $_provider_userstatus;
1296
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1297
  }
classes/autoptimizeMain.php CHANGED
@@ -61,10 +61,16 @@ class autoptimizeMain
61
  add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) );
62
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_extra' ) );
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' ) );
69
  }
70
 
@@ -81,7 +87,7 @@ class autoptimizeMain
81
  public function setup()
82
  {
83
  // Do we gzip in php when caching or is the webserver doing it?
84
- define( 'AUTOPTIMIZE_CACHE_NOGZIP', (bool) get_option( 'autoptimize_cache_nogzip' ) );
85
 
86
  // These can be overridden by specifying them in wp-config.php or such.
87
  if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_NAME' ) ) {
@@ -159,22 +165,24 @@ class autoptimizeMain
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(
165
- 'init',
166
- array( $this, 'start_buffering' ),
167
- self::INIT_EARLIER_PRIORITY
168
- );
169
- } else {
170
- if ( ! defined( 'AUTOPTIMIZE_HOOK_INTO' ) ) {
171
- define( 'AUTOPTIMIZE_HOOK_INTO', 'template_redirect' );
 
 
 
 
 
 
 
172
  }
173
- add_action(
174
- constant( 'AUTOPTIMIZE_HOOK_INTO' ),
175
- array( $this, 'start_buffering' ),
176
- self::DEFAULT_HOOK_PRIORITY
177
- );
178
  }
179
 
180
  // And disable Jetpack's site accelerator if JS or CSS opt. are active.
@@ -208,6 +216,14 @@ class autoptimizeMain
208
  }
209
  }
210
 
 
 
 
 
 
 
 
 
211
  public function hook_page_cache_purge()
212
  {
213
  // hook into a collection of page cache purge actions if filter allows.
@@ -218,10 +234,13 @@ class autoptimizeMain
218
  'w3tc_flush_posts', // exits.
219
  'w3tc_flush_all', // exists.
220
  'ce_action_cache_cleared', // Sven confirmed this will be added.
 
221
  'comet_cache_wipe_cache', // still to be confirmed by Raam.
222
  'wp_cache_cleared', // cfr. https://github.com/Automattic/wp-super-cache/pull/537.
223
  'wpfc_delete_cache', // Emre confirmed this will be added this.
224
  'swift_performance_after_clear_all_cache', // swift perf. yeah!
 
 
225
  );
226
  $page_cache_purge_actions = apply_filters( 'autoptimize_filter_main_pagecachepurgeactions', $page_cache_purge_actions );
227
  foreach ( $page_cache_purge_actions as $purge_action ) {
@@ -279,7 +298,7 @@ class autoptimizeMain
279
  * deciding once per request (for use in tests).
280
  * @return bool
281
  */
282
- public function should_buffer( $doing_tests = false )
283
  {
284
  static $do_buffering = null;
285
 
@@ -310,13 +329,24 @@ class autoptimizeMain
310
  }
311
  }
312
 
 
 
 
 
 
 
 
 
 
 
 
313
  // If setting says not to optimize logged in user and user is logged in...
314
- if ( 'on' !== get_option( 'autoptimize_optimize_logged', 'on' ) && is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
315
  $ao_noptimize = true;
316
  }
317
 
318
  // If setting says not to optimize cart/checkout.
319
- if ( 'on' !== get_option( 'autoptimize_optimize_checkout', 'on' ) ) {
320
  // Checking for woocommerce, easy digital downloads and wp ecommerce...
321
  foreach ( array( 'is_checkout', 'is_cart', 'edd_is_checkout', 'wpsc_is_cart', 'wpsc_is_checkout' ) as $func ) {
322
  if ( function_exists( $func ) && $func() ) {
@@ -342,7 +372,7 @@ class autoptimizeMain
342
  * while the main query hasn't been ran yet. Thats why we use
343
  * AUTOPTIMIZE_INIT_EARLIER in tests.
344
  */
345
- $do_buffering = ( ! is_admin() && ! is_feed() && ! $ao_noptimize && ! $is_customize_preview );
346
  }
347
 
348
  return $do_buffering;
@@ -361,8 +391,9 @@ class autoptimizeMain
361
  $valid = true;
362
 
363
  $has_no_html_tag = ( false === stripos( $content, '<html' ) );
364
- $has_xsl_stylesheet = ( false !== stripos( $content, '<xsl:stylesheet' ) );
365
  $has_html5_doctype = ( preg_match( '/^<!DOCTYPE.+html>/i', ltrim( $content ) ) > 0 );
 
366
 
367
  if ( $has_no_html_tag ) {
368
  // Can't be valid amp markup without an html tag preceding it.
@@ -372,7 +403,7 @@ class autoptimizeMain
372
  }
373
 
374
  // If it's not html, or if it's amp or contains xsl stylesheets we don't touch it.
375
- if ( $has_no_html_tag && ! $has_html5_doctype || $is_amp_markup || $has_xsl_stylesheet ) {
376
  $valid = false;
377
  }
378
 
@@ -475,6 +506,20 @@ class autoptimizeMain
475
  return $content;
476
  }
477
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
  public static function on_uninstall()
479
  {
480
  autoptimizeCache::clearall();
@@ -492,6 +537,7 @@ class autoptimizeMain
492
  'autoptimize_css_exclude',
493
  'autoptimize_html',
494
  'autoptimize_html_keepcomments',
 
495
  'autoptimize_js',
496
  'autoptimize_js_aggregate',
497
  'autoptimize_js_exclude',
61
  add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) );
62
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_extra' ) );
63
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_partners_tab' ) );
64
+ add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_criticalcss_tab' ) );
65
 
66
  add_action( 'init', array( $this, 'load_textdomain' ) );
67
  add_action( 'admin_init', array( 'PAnD', 'init' ) );
68
 
69
+ if ( is_multisite() && is_admin() ) {
70
+ // Only if multisite and if in admin we want to check if we need to save options on network level.
71
+ add_action( 'init', 'autoptimizeOptionWrapper::check_multisite_on_saving_options' );
72
+ }
73
+
74
  register_activation_hook( $this->filepath, array( $this, 'on_activate' ) );
75
  }
76
 
87
  public function setup()
88
  {
89
  // Do we gzip in php when caching or is the webserver doing it?
90
+ define( 'AUTOPTIMIZE_CACHE_NOGZIP', (bool) autoptimizeOptionWrapper::get_option( 'autoptimize_cache_nogzip' ) );
91
 
92
  // These can be overridden by specifying them in wp-config.php or such.
93
  if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_NAME' ) ) {
165
  if ( autoptimizeCache::cacheavail() ) {
166
  $conf = autoptimizeConfig::instance();
167
  if ( $conf->get( 'autoptimize_html' ) || $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) || autoptimizeImages::imgopt_active() || autoptimizeImages::should_lazyload_wrapper() ) {
168
+ if ( ! defined( 'AUTOPTIMIZE_NOBUFFER_OPTIMIZE' ) ) {
169
+ // Hook into WordPress frontend.
170
+ if ( defined( 'AUTOPTIMIZE_INIT_EARLIER' ) ) {
171
+ add_action(
172
+ 'init',
173
+ array( $this, 'start_buffering' ),
174
+ self::INIT_EARLIER_PRIORITY
175
+ );
176
+ } else {
177
+ if ( ! defined( 'AUTOPTIMIZE_HOOK_INTO' ) ) {
178
+ define( 'AUTOPTIMIZE_HOOK_INTO', 'template_redirect' );
179
+ }
180
+ add_action(
181
+ constant( 'AUTOPTIMIZE_HOOK_INTO' ),
182
+ array( $this, 'start_buffering' ),
183
+ self::DEFAULT_HOOK_PRIORITY
184
+ );
185
  }
 
 
 
 
 
186
  }
187
 
188
  // And disable Jetpack's site accelerator if JS or CSS opt. are active.
216
  }
217
  }
218
 
219
+ public function maybe_run_criticalcss_tab()
220
+ {
221
+ // Loads criticalcss tab code if in admin (and not in admin-ajax.php)!
222
+ if ( autoptimizeConfig::is_admin_and_not_ajax() && ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
223
+ new autoptimizeCriticalCSSSettings();
224
+ }
225
+ }
226
+
227
  public function hook_page_cache_purge()
228
  {
229
  // hook into a collection of page cache purge actions if filter allows.
234
  'w3tc_flush_posts', // exits.
235
  'w3tc_flush_all', // exists.
236
  'ce_action_cache_cleared', // Sven confirmed this will be added.
237
+ 'aoce_action_cache_cleared', // Some other cache enabler.
238
  'comet_cache_wipe_cache', // still to be confirmed by Raam.
239
  'wp_cache_cleared', // cfr. https://github.com/Automattic/wp-super-cache/pull/537.
240
  'wpfc_delete_cache', // Emre confirmed this will be added this.
241
  'swift_performance_after_clear_all_cache', // swift perf. yeah!
242
+ 'wpo_cache_flush', // wp-optimize.
243
+ 'rt_nginx_helper_after_fastcgi_purge_all', // nginx helper.
244
  );
245
  $page_cache_purge_actions = apply_filters( 'autoptimize_filter_main_pagecachepurgeactions', $page_cache_purge_actions );
246
  foreach ( $page_cache_purge_actions as $purge_action ) {
298
  * deciding once per request (for use in tests).
299
  * @return bool
300
  */
301
+ public static function should_buffer( $doing_tests = false )
302
  {
303
  static $do_buffering = null;
304
 
329
  }
330
  }
331
 
332
+ // also honor PageSpeed=off parameter as used by mod_pagespeed, in use by some pagebuilders,
333
+ // see https://www.modpagespeed.com/doc/experiment#ModPagespeed for info on that.
334
+ if ( false === $ao_noptimize && array_key_exists( 'PageSpeed', $_GET ) && 'off' === $_GET['PageSpeed'] ) {
335
+ $ao_noptimize = true;
336
+ }
337
+
338
+ // and make sure Thrive editor doesn't get optimized HTML.
339
+ if ( false === $ao_noptimize && array_key_exists( 'tve', $_GET ) && 'true' === $_GET['tve'] ) {
340
+ $ao_noptimize = true;
341
+ }
342
+
343
  // If setting says not to optimize logged in user and user is logged in...
344
+ if ( false === $ao_noptimize && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_logged', 'on' ) && is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
345
  $ao_noptimize = true;
346
  }
347
 
348
  // If setting says not to optimize cart/checkout.
349
+ if ( false === $ao_noptimize && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_checkout', 'off' ) ) {
350
  // Checking for woocommerce, easy digital downloads and wp ecommerce...
351
  foreach ( array( 'is_checkout', 'is_cart', 'edd_is_checkout', 'wpsc_is_cart', 'wpsc_is_checkout' ) as $func ) {
352
  if ( function_exists( $func ) && $func() ) {
372
  * while the main query hasn't been ran yet. Thats why we use
373
  * AUTOPTIMIZE_INIT_EARLIER in tests.
374
  */
375
+ $do_buffering = ( ! is_admin() && ! is_feed() && ! is_embed() && ! $ao_noptimize && ! $is_customize_preview );
376
  }
377
 
378
  return $do_buffering;
391
  $valid = true;
392
 
393
  $has_no_html_tag = ( false === stripos( $content, '<html' ) );
394
+ $has_xsl_stylesheet = ( false !== stripos( $content, '<xsl:stylesheet' ) || false !== stripos( $content, '<?xml-stylesheet' ) );
395
  $has_html5_doctype = ( preg_match( '/^<!DOCTYPE.+html>/i', ltrim( $content ) ) > 0 );
396
+ $has_noptimize_page = ( false !== stripos( $content, '<!-- noptimize-page -->' ) );
397
 
398
  if ( $has_no_html_tag ) {
399
  // Can't be valid amp markup without an html tag preceding it.
403
  }
404
 
405
  // If it's not html, or if it's amp or contains xsl stylesheets we don't touch it.
406
+ if ( $has_no_html_tag && ! $has_html5_doctype || $is_amp_markup || $has_xsl_stylesheet || $has_noptimize_page ) {
407
  $valid = false;
408
  }
409
 
506
  return $content;
507
  }
508
 
509
+ public static function autoptimize_nobuffer_optimize( $html_in ) {
510
+ $html_out = $html_in;
511
+
512
+ if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
513
+ $ao_speedupper = new autoptimizeSpeedupper();
514
+ }
515
+
516
+ $self = new self( AUTOPTIMIZE_PLUGIN_VERSION, AUTOPTIMIZE_PLUGIN_FILE );
517
+ if ( $self->should_buffer() ) {
518
+ $html_out = $self->end_buffering( $html_in );
519
+ }
520
+ return $html_out;
521
+ }
522
+
523
  public static function on_uninstall()
524
  {
525
  autoptimizeCache::clearall();
537
  'autoptimize_css_exclude',
538
  'autoptimize_html',
539
  'autoptimize_html_keepcomments',
540
+ 'autoptimize_enable_site_config',
541
  'autoptimize_js',
542
  'autoptimize_js_aggregate',
543
  'autoptimize_js_exclude',
classes/autoptimizeOptionWrapper.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Autoptimize options handler.
4
+ */
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit;
8
+ }
9
+
10
+ /**
11
+ * This class takes care of the set and get of option for standalone and multisite WordPress instances.
12
+ */
13
+ class autoptimizeOptionWrapper {
14
+ /**
15
+ * Constructor, add filter on saving options.
16
+ */
17
+ public function __construct() {
18
+ }
19
+
20
+ /**
21
+ * Ensure that is_plugin_active_for_network function is declared.
22
+ */
23
+ public static function maybe_include_plugin_functions() {
24
+ if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
25
+ include_once ABSPATH . 'wp-admin/includes/plugin.php';
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Retrieves the option in standalone and multisite instances.
31
+ *
32
+ * @param string $option Name of option to retrieve. Expected to not be SQL-escaped.
33
+ * @param mixed $default Optional. Default value to return if the option does not exist.
34
+ * @return mixed Value set for the option.
35
+ */
36
+ public static function get_option( $option, $default = false ) {
37
+ // This is always a network setting.
38
+ if ( 'autoptimize_enable_site_config' === $option ) {
39
+ return get_network_option( get_main_network_id(), $option );
40
+ }
41
+
42
+ // If the plugin is network activated and our per site setting is not on, use the network configuration.
43
+ $configuration_per_site = get_network_option( get_main_network_id(), 'autoptimize_enable_site_config' );
44
+ if ( self::is_ao_active_for_network() && ( 'on' !== $configuration_per_site || is_network_admin() ) ) {
45
+ return get_network_option( get_main_network_id(), $option );
46
+ }
47
+
48
+ return get_option( $option, $default );
49
+ }
50
+
51
+ /**
52
+ * Saves the option in standalone and multisite instances.
53
+ *
54
+ * @param string $option Option name. Expected to not be SQL-escaped.
55
+ * @param mixed $value Option value. Must be serializable if non-scalar. Expected to not be SQL-escaped.
56
+ * @param string|bool $autoload Optional. Whether to load the option when WordPress starts up. For existing options,
57
+ * `$autoload` can only be updated using `update_option()` if `$value` is also changed.
58
+ * Accepts 'yes'|true to enable or 'no'|false to disable. For non-existent options,
59
+ * the default value is 'yes'. Default null.
60
+ * @return bool False if value was not updated and true if value was updated.
61
+ */
62
+ public static function update_option( $option, $value, $autoload = null ) {
63
+ if ( self::is_ao_active_for_network() && is_network_admin() ) {
64
+ return update_network_option( get_main_network_id(), $option, $value );
65
+ } elseif ( 'autoptimize_enable_site_config' !== $option ) {
66
+ return update_option( $option, $value, $autoload );
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Use the pre_update_option filter to check if the option to be saved if from autoptimize and
72
+ * in that case, take care of multisite case.
73
+ */
74
+ public static function check_multisite_on_saving_options() {
75
+ if ( self::is_ao_active_for_network() ) {
76
+ add_filter( 'pre_update_option', 'autoptimizeOptionWrapper::update_autoptimize_option_on_network', 10, 3 );
77
+ }
78
+ }
79
+
80
+ /**
81
+ * The actual magic to differentiate between network options and per-site options.
82
+ *
83
+ * @param mixed $value Option value.
84
+ * @param string $option Option name.
85
+ * @param string $old_value Old value.
86
+ */
87
+ public static function update_autoptimize_option_on_network( $value, $option, $old_value ) {
88
+ if ( strpos( $option, 'autoptimize_' ) === 0 && self::is_options_from_network_admin() ) {
89
+ if ( self::is_ao_active_for_network() ) {
90
+ update_network_option( get_main_network_id(), $option, $value );
91
+ // Return old value, to stop update_option logic.
92
+ return $old_value;
93
+ }
94
+ if ( apply_filters( 'autoptimize_filter_optionwrapper_wp_cache_delete', false ) ) {
95
+ // in some (rare) cases options seem to get stuck in WP's Object cache, this should clear it there.
96
+ wp_cache_delete( $option );
97
+ }
98
+ }
99
+ return $value;
100
+ }
101
+
102
+ /**
103
+ * As options are POST-ed to wp-admin/options.php checking is_network_admin() does not
104
+ * work (yet). Instead we compare the network_admin_url with the _wp_http_referer
105
+ * (which should always be available as part of a hidden form field).
106
+ */
107
+ public static function is_options_from_network_admin() {
108
+ static $_really_is_network_admin = null;
109
+
110
+ if ( null === $_really_is_network_admin ) {
111
+ if ( array_key_exists( '_wp_http_referer', $_POST ) && strpos( network_admin_url( 'settings.php' ), strtok( $_POST['_wp_http_referer'], '?' ) ) !== false ) {
112
+ $_really_is_network_admin = true;
113
+ } else {
114
+ $_really_is_network_admin = false;
115
+ }
116
+ }
117
+
118
+ return $_really_is_network_admin;
119
+ }
120
+
121
+ /**
122
+ * Function to check if AO (including beta) is active for network.
123
+ */
124
+ public static function is_ao_active_for_network() {
125
+ static $_is_ao_active_for_network = null;
126
+ if ( null === $_is_ao_active_for_network || defined( 'TEST_MULTISITE_FORCE_AO_ON_NETWORK' ) ) {
127
+ self::maybe_include_plugin_functions();
128
+ if ( is_plugin_active_for_network( 'autoptimize/autoptimize.php' ) || is_plugin_active_for_network( 'autoptimize-beta/autoptimize.php' ) || defined( 'TEST_MULTISITE_FORCE_AO_ON_NETWORK' ) ) {
129
+ $_is_ao_active_for_network = true;
130
+ } else {
131
+ $_is_ao_active_for_network = false;
132
+ }
133
+ }
134
+ return $_is_ao_active_for_network;
135
+ }
136
+ }
137
+ new autoptimizeOptionWrapper();
classes/autoptimizePartners.php CHANGED
@@ -20,7 +20,11 @@ class autoptimizePartners
20
  if ( $this->enabled() ) {
21
  add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_partner_tabs' ), 10, 1 );
22
  }
23
- add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
 
 
 
 
24
  }
25
 
26
  protected function enabled()
@@ -132,6 +136,7 @@ class autoptimizePartners
132
  color: #23282d;
133
  }
134
  </style>
 
135
  <div class="wrap">
136
  <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
137
  <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
20
  if ( $this->enabled() ) {
21
  add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_partner_tabs' ), 10, 1 );
22
  }
23
+ if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
24
+ add_action( 'network_admin_menu', array( $this, 'add_admin_menu' ) );
25
+ } else {
26
+ add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
27
+ }
28
  }
29
 
30
  protected function enabled()
136
  color: #23282d;
137
  }
138
  </style>
139
+ <script>document.title = "Autoptimize: <?php _e( 'Optimize More!', 'autoptimize' ); ?> " + document.title;</script>
140
  <div class="wrap">
141
  <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
142
  <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
classes/autoptimizeScripts.php CHANGED
@@ -43,7 +43,7 @@ class autoptimizeScripts extends autoptimizeBase
43
  private $minify_excluded = true;
44
 
45
  // Reads the page and collects script tags.
46
- public function read($options)
47
  {
48
  $noptimizeJS = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content );
49
  if ( $noptimizeJS ) {
@@ -58,7 +58,7 @@ class autoptimizeScripts extends autoptimizeBase
58
 
59
  // is there JS we should simply remove?
60
  $removableJS = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
61
- if (!empty($removableJS)) {
62
  $this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removableJS ) ) );
63
  }
64
 
@@ -87,14 +87,15 @@ class autoptimizeScripts extends autoptimizeBase
87
  $this->inject_min_late = apply_filters( 'autoptimize_filter_js_inject_min_late', true );
88
 
89
  // filters to override hardcoded do(nt)move(last) array contents (array in, array out!).
90
- $this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove );
91
  $this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast );
92
- $this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove );
93
 
94
  // Determine whether excluded files should be minified if not yet so.
95
  if ( ! $options['minify_excluded'] && $options['aggregate'] ) {
96
  $this->minify_excluded = false;
97
  }
 
98
 
99
  // get extra exclusions settings or filter.
100
  $excludeJS = $options['js_exclude'];
@@ -181,7 +182,7 @@ class autoptimizeScripts extends autoptimizeBase
181
  // Should we minify the non-aggregated script?
182
  // -> if aggregate is on and exclude minify is on
183
  // -> if aggregate is off and the file is not in dontmove.
184
- if ( $path && ( $this->minify_excluded || apply_filters( 'autoptimize_filter_js_minify_excluded', false, $url ) ) ) {
185
  $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
186
  if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
187
  $minified_url = $this->minify_single( $path );
@@ -396,9 +397,13 @@ class autoptimizeScripts extends autoptimizeBase
396
  $defer = 'defer ';
397
  }
398
 
399
- $defer = apply_filters( 'autoptimize_filter_js_defer', $defer );
 
 
 
 
400
 
401
- $bodyreplacementpayload = '<script type="text/javascript" ' . $defer . 'src="' . $this->url . '"></script>';
402
  $bodyreplacementpayload = apply_filters( 'autoptimize_filter_js_bodyreplacementpayload', $bodyreplacementpayload );
403
 
404
  $bodyreplacement = implode( '', $this->move['first'] );
43
  private $minify_excluded = true;
44
 
45
  // Reads the page and collects script tags.
46
+ public function read( $options )
47
  {
48
  $noptimizeJS = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content );
49
  if ( $noptimizeJS ) {
58
 
59
  // is there JS we should simply remove?
60
  $removableJS = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
61
+ if ( !empty( $removableJS ) ) {
62
  $this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removableJS ) ) );
63
  }
64
 
87
  $this->inject_min_late = apply_filters( 'autoptimize_filter_js_inject_min_late', true );
88
 
89
  // filters to override hardcoded do(nt)move(last) array contents (array in, array out!).
90
+ $this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove );
91
  $this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast );
92
+ $this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove );
93
 
94
  // Determine whether excluded files should be minified if not yet so.
95
  if ( ! $options['minify_excluded'] && $options['aggregate'] ) {
96
  $this->minify_excluded = false;
97
  }
98
+ $this->minify_excluded = apply_filters( 'autoptimize_filter_js_minify_excluded', $this->minify_excluded );
99
 
100
  // get extra exclusions settings or filter.
101
  $excludeJS = $options['js_exclude'];
182
  // Should we minify the non-aggregated script?
183
  // -> if aggregate is on and exclude minify is on
184
  // -> if aggregate is off and the file is not in dontmove.
185
+ if ( $path && $this->minify_excluded ) {
186
  $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
187
  if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
188
  $minified_url = $this->minify_single( $path );
397
  $defer = 'defer ';
398
  }
399
 
400
+ $defer = apply_filters( 'autoptimize_filter_js_defer', $defer );
401
+ $type_js = '';
402
+ if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
403
+ $type_js = 'type="text/javascript" ';
404
+ }
405
 
406
+ $bodyreplacementpayload = '<script ' . $type_js . $defer . 'src="' . $this->url . '"></script>';
407
  $bodyreplacementpayload = apply_filters( 'autoptimize_filter_js_bodyreplacementpayload', $bodyreplacementpayload );
408
 
409
  $bodyreplacement = implode( '', $this->move['first'] );
classes/autoptimizeStyles.php CHANGED
@@ -129,6 +129,7 @@ class autoptimizeStyles extends autoptimizeBase
129
  if ( ! $options['minify_excluded'] && $options['aggregate'] ) {
130
  $this->minify_excluded = false;
131
  }
 
132
 
133
  // noptimize me.
134
  $this->content = $this->hide_noptimize( $this->content );
@@ -218,7 +219,7 @@ class autoptimizeStyles extends autoptimizeBase
218
  // Excluded CSS, minify that file:
219
  // -> if aggregate is on and exclude minify is on
220
  // -> if aggregate is off and the file is not in dontmove.
221
- if ( $path && ( $this->minify_excluded || apply_filters( 'autoptimize_filter_css_minify_excluded', false, $url ) ) ) {
222
  $consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false );
223
  if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
224
  $minified_url = $this->minify_single( $path );
@@ -266,7 +267,7 @@ class autoptimizeStyles extends autoptimizeBase
266
  $url
267
  );
268
  // Adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback.
269
- $new_tag = '<noscript>' . $tag . '</noscript>' . str_replace(
270
  array(
271
  "rel='stylesheet'",
272
  'rel="stylesheet"',
@@ -821,18 +822,25 @@ class autoptimizeStyles extends autoptimizeBase
821
  $this->restofcontent = '';
822
  }
823
 
 
 
 
 
 
 
824
  // Inject the new stylesheets.
825
  $replaceTag = array( '<title', 'before' );
826
  $replaceTag = apply_filters( 'autoptimize_filter_css_replacetag', $replaceTag, $this->content );
827
 
828
  if ( $this->inline ) {
829
  foreach ( $this->csscode as $media => $code ) {
830
- $this->inject_in_html( '<style type="text/css" media="' . $media . '">' . $code . '</style>', $replaceTag );
831
  }
832
  } else {
833
  if ( $this->defer ) {
834
- $preloadCssBlock = '';
835
- $noScriptCssBlock = "<noscript id=\"aonoscrcss\">";
 
836
 
837
  $defer_inline_code = $this->defer_inline;
838
  if ( ! empty( $defer_inline_code ) ) {
@@ -855,7 +863,7 @@ class autoptimizeStyles extends autoptimizeBase
855
  }
856
  // inlined critical css set here, but injected when full CSS is injected
857
  // to avoid CSS containing SVG with <title tag receiving the full CSS link.
858
- $inlined_ccss_block = '<style type="text/css" id="aoatfcss" media="all">' . $defer_inline_code . '</style>';
859
  }
860
  }
861
 
@@ -867,13 +875,12 @@ class autoptimizeStyles extends autoptimizeBase
867
  $preloadOnLoad = autoptimizeConfig::get_ao_css_preload_onload();
868
 
869
  $preloadCssBlock .= '<link rel="preload" as="style" media="' . $media . '" href="' . $url . '" onload="' . $preloadOnLoad . '" />';
870
- $noScriptCssBlock .= '<link type="text/css" media="' . $media . '" href="' . $url . '" rel="stylesheet" />';
871
  } else {
872
- // $this->inject_in_html('<link type="text/css" media="' . $media . '" href="' . $url . '" rel="stylesheet" />', $replaceTag);
873
  if ( strlen( $this->csscode[$media] ) > $this->cssinlinesize ) {
874
- $this->inject_in_html( '<link type="text/css" media="' . $media . '" href="' . $url . '" rel="stylesheet" />', $replaceTag );
875
  } elseif ( strlen( $this->csscode[$media] ) > 0 ) {
876
- $this->inject_in_html( '<style type="text/css" media="' . $media . '">' . $this->csscode[$media] . '</style>', $replaceTag );
877
  }
878
  }
879
  }
@@ -945,7 +952,7 @@ class autoptimizeStyles extends autoptimizeBase
945
  }
946
 
947
  $url = $noQurl;
948
- if ( '/' === $url{0} || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) {
949
  // URL is protocol-relative, host-relative or something we don't touch.
950
  continue;
951
  } else {
@@ -1052,13 +1059,16 @@ class autoptimizeStyles extends autoptimizeBase
1052
  // Check cache.
1053
  $hash = 'single_' . md5( $contents );
1054
  $cache = new autoptimizeCache( $hash, 'css' );
 
1055
 
1056
  // If not in cache already, minify...
1057
  if ( ! $cache->check() || $cache_miss ) {
1058
  // Fixurls...
1059
  $contents = self::fixurls( $filepath, $contents );
1060
  // CDN-replace any referenced assets if needed...
 
1061
  $contents = $this->replace_urls( $contents );
 
1062
  // Now minify...
1063
  $cssmin = new autoptimizeCSSmin();
1064
  $contents = trim( $cssmin->run( $contents ) );
129
  if ( ! $options['minify_excluded'] && $options['aggregate'] ) {
130
  $this->minify_excluded = false;
131
  }
132
+ $this->minify_excluded = apply_filters( 'autoptimize_filter_css_minify_excluded', $this->minify_excluded );
133
 
134
  // noptimize me.
135
  $this->content = $this->hide_noptimize( $this->content );
219
  // Excluded CSS, minify that file:
220
  // -> if aggregate is on and exclude minify is on
221
  // -> if aggregate is off and the file is not in dontmove.
222
+ if ( $path && $this->minify_excluded ) {
223
  $consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false );
224
  if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
225
  $minified_url = $this->minify_single( $path );
267
  $url
268
  );
269
  // Adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback.
270
+ $new_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>' . str_replace(
271
  array(
272
  "rel='stylesheet'",
273
  'rel="stylesheet"',
822
  $this->restofcontent = '';
823
  }
824
 
825
+ // type is not added by default.
826
+ $type_css = '';
827
+ if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
828
+ $type_css = 'type="text/css" ';
829
+ }
830
+
831
  // Inject the new stylesheets.
832
  $replaceTag = array( '<title', 'before' );
833
  $replaceTag = apply_filters( 'autoptimize_filter_css_replacetag', $replaceTag, $this->content );
834
 
835
  if ( $this->inline ) {
836
  foreach ( $this->csscode as $media => $code ) {
837
+ $this->inject_in_html( '<style ' . $type_css . 'media="' . $media . '">' . $code . '</style>', $replaceTag );
838
  }
839
  } else {
840
  if ( $this->defer ) {
841
+ $preloadCssBlock = '';
842
+ $inlined_ccss_block = '';
843
+ $noScriptCssBlock = "<noscript id=\"aonoscrcss\">";
844
 
845
  $defer_inline_code = $this->defer_inline;
846
  if ( ! empty( $defer_inline_code ) ) {
863
  }
864
  // inlined critical css set here, but injected when full CSS is injected
865
  // to avoid CSS containing SVG with <title tag receiving the full CSS link.
866
+ $inlined_ccss_block = '<style ' . $type_css . 'id="aoatfcss" media="all">' . $defer_inline_code . '</style>';
867
  }
868
  }
869
 
875
  $preloadOnLoad = autoptimizeConfig::get_ao_css_preload_onload();
876
 
877
  $preloadCssBlock .= '<link rel="preload" as="style" media="' . $media . '" href="' . $url . '" onload="' . $preloadOnLoad . '" />';
878
+ $noScriptCssBlock .= '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />';
879
  } else {
 
880
  if ( strlen( $this->csscode[$media] ) > $this->cssinlinesize ) {
881
+ $this->inject_in_html( '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />', $replaceTag );
882
  } elseif ( strlen( $this->csscode[$media] ) > 0 ) {
883
+ $this->inject_in_html( '<style ' . $type_css . 'media="' . $media . '">' . $this->csscode[$media] . '</style>', $replaceTag );
884
  }
885
  }
886
  }
952
  }
953
 
954
  $url = $noQurl;
955
+ if ( '/' === $url[0] || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) {
956
  // URL is protocol-relative, host-relative or something we don't touch.
957
  continue;
958
  } else {
1059
  // Check cache.
1060
  $hash = 'single_' . md5( $contents );
1061
  $cache = new autoptimizeCache( $hash, 'css' );
1062
+ do_action( 'autoptimize_action_css_hash', $hash );
1063
 
1064
  // If not in cache already, minify...
1065
  if ( ! $cache->check() || $cache_miss ) {
1066
  // Fixurls...
1067
  $contents = self::fixurls( $filepath, $contents );
1068
  // CDN-replace any referenced assets if needed...
1069
+ $contents = $this->hide_fontface_and_maybe_cdn( $contents );
1070
  $contents = $this->replace_urls( $contents );
1071
+ $contents = $this->restore_fontface( $contents );
1072
  // Now minify...
1073
  $cssmin = new autoptimizeCSSmin();
1074
  $contents = trim( $cssmin->run( $contents ) );
classes/autoptimizeToolbar.php CHANGED
@@ -134,7 +134,7 @@ class autoptimizeToolbar
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
  ) );
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
@@ -314,7 +314,7 @@ class autoptimizeUtils
314
  if ( '200' == wp_remote_retrieve_response_code( $service_availability_resp ) ) {
315
  $availabilities = json_decode( wp_remote_retrieve_body( $service_availability_resp ), true );
316
  if ( is_array( $availabilities ) ) {
317
- update_option( 'autoptimize_service_availablity', $availabilities );
318
  if ( $return_result ) {
319
  return $availabilities;
320
  }
@@ -359,4 +359,39 @@ class autoptimizeUtils
359
 
360
  return $ipa_exists && \is_plugin_active( $plugin_file );
361
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  }
314
  if ( '200' == wp_remote_retrieve_response_code( $service_availability_resp ) ) {
315
  $availabilities = json_decode( wp_remote_retrieve_body( $service_availability_resp ), true );
316
  if ( is_array( $availabilities ) ) {
317
+ autoptimizeOptionWrapper::update_option( 'autoptimize_service_availablity', $availabilities );
318
  if ( $return_result ) {
319
  return $availabilities;
320
  }
359
 
360
  return $ipa_exists && \is_plugin_active( $plugin_file );
361
  }
362
+
363
+ /**
364
+ * Returns a node without ID attrib for use in noscript tags
365
+ *
366
+ * @param string $node an html tag.
367
+ *
368
+ * @return string
369
+ */
370
+ public static function remove_id_from_node( $node ) {
371
+ if ( strpos( $node, 'id=' ) === false || apply_filters( 'autoptimize_filter_utils_keep_ids', false ) ) {
372
+ return $node;
373
+ } else {
374
+ return preg_replace( '#(.*) id=[\'|"].*[\'|"] (.*)#Um', '$1 $2', $node );
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Returns true if given $str ends with given $test.
380
+ *
381
+ * @param string $str String to check.
382
+ * @param string $test Ending to match.
383
+ *
384
+ * @return bool
385
+ */
386
+ public static function str_ends_in( $str, $test )
387
+ {
388
+ // @codingStandardsIgnoreStart
389
+ // substr_compare() is bugged on 5.5.11: https://3v4l.org/qGYBH
390
+ // return ( 0 === substr_compare( $str, $test, -strlen( $test ) ) );
391
+ // @codingStandardsIgnoreEnd
392
+
393
+ $length = strlen( $test );
394
+
395
+ return ( substr( $str, -$length, $length ) === $test );
396
+ }
397
  }
classes/autoptimizeVersionUpdatesHandler.php CHANGED
@@ -47,7 +47,7 @@ class autoptimizeVersionUpdatesHandler
47
  $major_update = true;
48
  // No break, intentionally, so all upgrades are ran during a single request...
49
  case '2.4':
50
- if ( get_option( 'autoptimize_version', 'none' ) == '2.4.2' ) {
51
  $this->upgrade_from_2_4_2();
52
  }
53
  $this->upgrade_from_2_4();
@@ -69,7 +69,7 @@ class autoptimizeVersionUpdatesHandler
69
  */
70
  public static function check_installed_and_update( $target )
71
  {
72
- $db_version = get_option( 'autoptimize_version', 'none' );
73
  if ( $db_version !== $target ) {
74
  if ( 'none' === $db_version ) {
75
  add_action( 'admin_notices', 'autoptimizeMain::notice_installed' );
@@ -79,7 +79,7 @@ class autoptimizeVersionUpdatesHandler
79
  }
80
 
81
  // Versions differed, upgrades happened if needed, store the new version.
82
- update_option( 'autoptimize_version', $target );
83
  }
84
  }
85
 
@@ -103,7 +103,7 @@ class autoptimizeVersionUpdatesHandler
103
  private function upgrade_from_1_6()
104
  {
105
  // If user was on version 1.6.x, force advanced options to be shown by default.
106
- update_option( 'autoptimize_show_adv', '1' );
107
 
108
  // And remove old options.
109
  $to_delete_options = array(
@@ -128,26 +128,26 @@ class autoptimizeVersionUpdatesHandler
128
  private function upgrade_from_1_7()
129
  {
130
  if ( ! is_multisite() ) {
131
- $css_exclude = get_option( 'autoptimize_css_exclude' );
132
  if ( empty( $css_exclude ) ) {
133
  $css_exclude = 'admin-bar.min.css, dashicons.min.css';
134
  } elseif ( false === strpos( $css_exclude, 'dashicons.min.css' ) ) {
135
  $css_exclude .= ', dashicons.min.css';
136
  }
137
- update_option( 'autoptimize_css_exclude', $css_exclude );
138
  } else {
139
  global $wpdb;
140
  $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
141
  $original_blog_id = get_current_blog_id();
142
  foreach ( $blog_ids as $blog_id ) {
143
  switch_to_blog( $blog_id );
144
- $css_exclude = get_option( 'autoptimize_css_exclude' );
145
  if ( empty( $css_exclude ) ) {
146
  $css_exclude = 'admin-bar.min.css, dashicons.min.css';
147
  } elseif ( false === strpos( $css_exclude, 'dashicons.min.css' ) ) {
148
  $css_exclude .= ', dashicons.min.css';
149
  }
150
- update_option( 'autoptimize_css_exclude', $css_exclude );
151
  }
152
  switch_to_blog( $original_blog_id );
153
  }
@@ -162,16 +162,16 @@ class autoptimizeVersionUpdatesHandler
162
  private function upgrade_from_1_9()
163
  {
164
  if ( ! is_multisite() ) {
165
- update_option( 'autoptimize_css_include_inline', 'on' );
166
- update_option( 'autoptimize_js_include_inline', 'on' );
167
  } else {
168
  global $wpdb;
169
  $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
170
  $original_blog_id = get_current_blog_id();
171
  foreach ( $blog_ids as $blog_id ) {
172
  switch_to_blog( $blog_id );
173
- update_option( 'autoptimize_css_include_inline', 'on' );
174
- update_option( 'autoptimize_js_include_inline', 'on' );
175
  }
176
  switch_to_blog( $original_blog_id );
177
  }
@@ -203,10 +203,10 @@ class autoptimizeVersionUpdatesHandler
203
  */
204
  private function do_2_2_settings_update()
205
  {
206
- $nogooglefont = get_option( 'autoptimize_css_nogooglefont', '' );
207
- $ao_extrasetting = get_option( 'autoptimize_extra_settings', '' );
208
  if ( ( $nogooglefont ) && ( empty( $ao_extrasetting ) ) ) {
209
- update_option( 'autoptimize_extra_settings', autoptimizeConfig::get_ao_extra_default_options() );
210
  }
211
  delete_option( 'autoptimize_css_nogooglefont' );
212
  }
@@ -236,8 +236,8 @@ class autoptimizeVersionUpdatesHandler
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 ) && ! empty( $extra_settings ) ) {
242
  $imgopt_settings = autoptimizeConfig::get_ao_imgopt_default_options();
243
  if ( array_key_exists( 'autoptimize_extra_checkbox_field_5', $extra_settings ) ) {
@@ -246,7 +246,7 @@ class autoptimizeVersionUpdatesHandler
246
  if ( array_key_exists( 'autoptimize_extra_select_field_6', $extra_settings ) ) {
247
  $imgopt_settings['autoptimize_imgopt_select_field_2'] = $extra_settings['autoptimize_extra_select_field_6'];
248
  }
249
- update_option( 'autoptimize_imgopt_settings', $imgopt_settings );
250
  }
251
  }
252
  }
47
  $major_update = true;
48
  // No break, intentionally, so all upgrades are ran during a single request...
49
  case '2.4':
50
+ if ( autoptimizeOptionWrapper::get_option( 'autoptimize_version', 'none' ) == '2.4.2' ) {
51
  $this->upgrade_from_2_4_2();
52
  }
53
  $this->upgrade_from_2_4();
69
  */
70
  public static function check_installed_and_update( $target )
71
  {
72
+ $db_version = autoptimizeOptionWrapper::get_option( 'autoptimize_version', 'none' );
73
  if ( $db_version !== $target ) {
74
  if ( 'none' === $db_version ) {
75
  add_action( 'admin_notices', 'autoptimizeMain::notice_installed' );
79
  }
80
 
81
  // Versions differed, upgrades happened if needed, store the new version.
82
+ autoptimizeOptionWrapper::update_option( 'autoptimize_version', $target );
83
  }
84
  }
85
 
103
  private function upgrade_from_1_6()
104
  {
105
  // If user was on version 1.6.x, force advanced options to be shown by default.
106
+ autoptimizeOptionWrapper::update_option( 'autoptimize_show_adv', '1' );
107
 
108
  // And remove old options.
109
  $to_delete_options = array(
128
  private function upgrade_from_1_7()
129
  {
130
  if ( ! is_multisite() ) {
131
+ $css_exclude = autoptimizeOptionWrapper::get_option( 'autoptimize_css_exclude' );
132
  if ( empty( $css_exclude ) ) {
133
  $css_exclude = 'admin-bar.min.css, dashicons.min.css';
134
  } elseif ( false === strpos( $css_exclude, 'dashicons.min.css' ) ) {
135
  $css_exclude .= ', dashicons.min.css';
136
  }
137
+ autoptimizeOptionWrapper::update_option( 'autoptimize_css_exclude', $css_exclude );
138
  } else {
139
  global $wpdb;
140
  $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
141
  $original_blog_id = get_current_blog_id();
142
  foreach ( $blog_ids as $blog_id ) {
143
  switch_to_blog( $blog_id );
144
+ $css_exclude = autoptimizeOptionWrapper::get_option( 'autoptimize_css_exclude' );
145
  if ( empty( $css_exclude ) ) {
146
  $css_exclude = 'admin-bar.min.css, dashicons.min.css';
147
  } elseif ( false === strpos( $css_exclude, 'dashicons.min.css' ) ) {
148
  $css_exclude .= ', dashicons.min.css';
149
  }
150
+ autoptimizeOptionWrapper::update_option( 'autoptimize_css_exclude', $css_exclude );
151
  }
152
  switch_to_blog( $original_blog_id );
153
  }
162
  private function upgrade_from_1_9()
163
  {
164
  if ( ! is_multisite() ) {
165
+ autoptimizeOptionWrapper::update_option( 'autoptimize_css_include_inline', 'on' );
166
+ autoptimizeOptionWrapper::update_option( 'autoptimize_js_include_inline', 'on' );
167
  } else {
168
  global $wpdb;
169
  $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
170
  $original_blog_id = get_current_blog_id();
171
  foreach ( $blog_ids as $blog_id ) {
172
  switch_to_blog( $blog_id );
173
+ autoptimizeOptionWrapper::update_option( 'autoptimize_css_include_inline', 'on' );
174
+ autoptimizeOptionWrapper::update_option( 'autoptimize_js_include_inline', 'on' );
175
  }
176
  switch_to_blog( $original_blog_id );
177
  }
203
  */
204
  private function do_2_2_settings_update()
205
  {
206
+ $nogooglefont = autoptimizeOptionWrapper::get_option( 'autoptimize_css_nogooglefont', '' );
207
+ $ao_extrasetting = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings', '' );
208
  if ( ( $nogooglefont ) && ( empty( $ao_extrasetting ) ) ) {
209
+ autoptimizeOptionWrapper::update_option( 'autoptimize_extra_settings', autoptimizeConfig::get_ao_extra_default_options() );
210
  }
211
  delete_option( 'autoptimize_css_nogooglefont' );
212
  }
236
  * Migrate imgopt options from autoptimize_extra_settings to autoptimize_imgopt_settings
237
  */
238
  private function upgrade_from_2_4() {
239
+ $extra_settings = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings', '' );
240
+ $imgopt_settings = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings', '' );
241
  if ( empty( $imgopt_settings ) && ! empty( $extra_settings ) ) {
242
  $imgopt_settings = autoptimizeConfig::get_ao_imgopt_default_options();
243
  if ( array_key_exists( 'autoptimize_extra_checkbox_field_5', $extra_settings ) ) {
246
  if ( array_key_exists( 'autoptimize_extra_select_field_6', $extra_settings ) ) {
247
  $imgopt_settings['autoptimize_imgopt_select_field_2'] = $extra_settings['autoptimize_extra_select_field_6'];
248
  }
249
+ autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_settings', $imgopt_settings );
250
  }
251
  }
252
  }
classes/external/js/lazysizes.min.js CHANGED
@@ -1,2 +1,3 @@
1
- /*! lazysizes - v5.0.0 */
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]);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++;f<a;f++)if(u[f]&&!u[f]._lazyRace)if(!M||c.prematureUnveil&&c.prematureUnveil(u[f]))aa(u[f]);else if((p=u[f][i]("data-expand"))&&(m=1*p)||(m=O),r||(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),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]);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(){var b=a.complete&&a.naturalWidth>1;p&&!b||(b&&s(a,"ls-is-cached"),W(o),a._lazyCache=!0,k(function(){"_lazyCache"in a&&delete a._lazyCache},9)),"lazy"==a.loading&&P--},!0)}),aa=function(a){if(!a._lazyRace){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=C(function(){d.loadMode=3,V()}),ca=function(){3==d.loadMode&&(d.loadMode=2),ba()},da=function(){if(!l){if(f.now()-p<999)return void k(da,999);l=!0,d.loadMode=3,V(),j("scroll",ca,!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"].forEach(function(a){b[h](a,V,!0)}),/d$|^c/.test(b.readyState)?da():(j("load",da),b[h]("DOMContentLoaded",V),k(da,2e4)),c.elements.length?(U(),z._lsFlush()):V()},checkElems:V,unveil:aa,_aLSL:ca}}(),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}}});
 
1
+ /*! lazysizes + ls unveilhooks - v5.2.0 */
2
+ !function(a,b){var c=b(a,a.document,Date);a.lazySizes=c,"object"==typeof module&&module.exports&&(module.exports=c)}("undefined"!=typeof window?window:{},function(a,b,c){"use strict";var d,e;if(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};e=a.lazySizesConfig||a.lazysizesConfig||{};for(b in c)b in e||(e[b]=c[b])}(),!b||!b.getElementsByClassName)return{init:function(){},cfg:e,noSupport:!0};var f=b.documentElement,g=a.HTMLPictureElement,h="addEventListener",i="getAttribute",j=a[h].bind(a),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,c,e,f,g){var h=b.createEvent("Event");return e||(e={}),e.instance=d,h.initEvent(c,!f,!g),h.detail=e,a.dispatchEvent(h),h},w=function(b,c){var d;!g&&(d=a.picturefill||e.pf)?(c&&c.src&&!b[i]("srcset")&&b.setAttribute("srcset",c.src),d({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<e.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,d=0,f=e.throttleDelay,g=e.ricTimeout,h=function(){b=!1,d=c.now(),a()},i=m&&g>49?function(){m(h,{timeout:g}),g!==e.ricTimeout&&(g=e.ricTimeout)}:A(function(){k(h)},!0);return function(a){var e;(a=!0===a)&&(g=33),b||(b=!0,e=f-(c.now()-d),e<0&&(e=0),a||e<9?i():k(i,e))}},C=function(a){var b,d,e=99,f=function(){b=null,a()},g=function(){var a=c.now()-d;a<e?k(g,e-a):(m||f)(f)};return function(){d=c.now(),b||(b=k(g,e))}},D=function(){var g,m,o,p,y,D,F,G,H,I,J,K,L=/^img$/i,M=/^iframe$/i,N="onscroll"in a&&!/(gle|ing)bot/.test(navigator.userAgent),O=0,P=0,Q=0,R=-1,S=function(a){Q--,(!a||Q<0||!a.target)&&(Q=0)},T=function(a){return null==K&&(K="hidden"==x(b.body,"visibility")),K||!("hidden"==x(a.parentNode,"visibility")&&"hidden"==x(a,"visibility"))},U=function(a,c){var d,e=a,g=T(a);for(G-=c,J+=c,H-=c,I+=c;g&&(e=e.offsetParent)&&e!=b.body&&e!=f;)(g=(x(e,"opacity")||1)>0)&&"visible"!=x(e,"overflow")&&(d=e.getBoundingClientRect(),g=I>d.left&&H<d.right&&J>d.top-1&&G<d.bottom+1);return g},V=function(){var a,c,h,j,k,l,n,o,q,r,s,t,u=d.elements;if((p=e.loadMode)&&Q<8&&(a=u.length)){for(c=0,R++;c<a;c++)if(u[c]&&!u[c]._lazyRace)if(!N||d.prematureUnveil&&d.prematureUnveil(u[c]))ba(u[c]);else if((o=u[c][i]("data-expand"))&&(l=1*o)||(l=P),r||(r=!e.expand||e.expand<1?f.clientHeight>500&&f.clientWidth>500?500:370:e.expand,d._defEx=r,s=r*e.expFactor,t=e.hFac,K=null,P<s&&Q<1&&R>2&&p>2&&!b.hidden?(P=s,R=0):P=p>1&&R>1&&Q<6?r:O),q!==l&&(D=innerWidth+l*t,F=innerHeight+l,n=-1*l,q=l),h=u[c].getBoundingClientRect(),(J=h.bottom)>=n&&(G=h.top)<=F&&(I=h.right)>=n*t&&(H=h.left)<=D&&(J||I||H||G)&&(e.loadHidden||T(u[c]))&&(m&&Q<3&&!o&&(p<3||R<4)||U(u[c],l))){if(ba(u[c]),k=!0,Q>9)break}else!k&&m&&!j&&Q<4&&R<4&&p>2&&(g[0]||e.preloadAfterLoad)&&(g[0]||!o&&(J||I||H||G||"auto"!=u[c][i](e.sizesAttr)))&&(j=g[0]||u[c]);j&&!k&&ba(j)}},W=B(V),X=function(a){var b=a.target;if(b._lazyCache)return void delete b._lazyCache;S(a),s(b,e.loadedClass),t(b,e.loadingClass),u(b,Z),v(b,"lazyloaded")},Y=A(X),Z=function(a){Y({target:a.target})},$=function(a,b){try{a.contentWindow.location.replace(b)}catch(c){a.src=b}},_=function(a){var b,c=a[i](e.srcsetAttr);(b=e.customMedia[a[i]("data-media")||a[i]("media")])&&a.setAttribute("media",b),c&&a.setAttribute("srcset",c)},aa=A(function(a,b,c,d,f){var g,h,j,l,m,p;(m=v(a,"lazybeforeunveil",b)).defaultPrevented||(d&&(c?s(a,e.autosizesClass):a.setAttribute("sizes",d)),h=a[i](e.srcsetAttr),g=a[i](e.srcAttr),f&&(j=a.parentNode,l=j&&n.test(j.nodeName||"")),p=b.firesLoad||"src"in a&&(h||g||l),m={target:a},s(a,e.loadingClass),p&&(clearTimeout(o),o=k(S,2500),u(a,Z,!0)),l&&q.call(j.getElementsByTagName("source"),_),h?a.setAttribute("srcset",h):g&&!l&&(M.test(a.nodeName)?$(a,g):a.src=g),f&&(h||l)&&w(a,{src:g})),a._lazyRace&&delete a._lazyRace,t(a,e.lazyClass),z(function(){var b=a.complete&&a.naturalWidth>1;p&&!b||(b&&s(a,"ls-is-cached"),X(m),a._lazyCache=!0,k(function(){"_lazyCache"in a&&delete a._lazyCache},9)),"lazy"==a.loading&&Q--},!0)}),ba=function(a){if(!a._lazyRace){var b,c=L.test(a.nodeName),d=c&&(a[i](e.sizesAttr)||a[i]("sizes")),f="auto"==d;(!f&&m||!c||!a[i]("src")&&!a.srcset||a.complete||r(a,e.errorClass)||!r(a,e.lazyClass))&&(b=v(a,"lazyunveilread").detail,f&&E.updateElem(a,!0,a.offsetWidth),a._lazyRace=!0,Q++,aa(a,b,f,d,c))}},ca=C(function(){e.loadMode=3,W()}),da=function(){3==e.loadMode&&(e.loadMode=2),ca()},ea=function(){if(!m){if(c.now()-y<999)return void k(ea,999);m=!0,e.loadMode=3,W(),j("scroll",da,!0)}};return{_:function(){y=c.now(),d.elements=b.getElementsByClassName(e.lazyClass),g=b.getElementsByClassName(e.lazyClass+" "+e.preloadClass),j("scroll",W,!0),j("resize",W,!0),j("pageshow",function(a){if(a.persisted){var c=b.querySelectorAll("."+e.loadingClass);c.length&&c.forEach&&l(function(){c.forEach(function(a){a.complete&&ba(a)})})}}),a.MutationObserver?new MutationObserver(W).observe(f,{childList:!0,subtree:!0,attributes:!0}):(f[h]("DOMNodeInserted",W,!0),f[h]("DOMAttrModified",W,!0),setInterval(W,999)),j("hashchange",W,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach(function(a){b[h](a,W,!0)}),/d$|^c/.test(b.readyState)?ea():(j("load",ea),b[h]("DOMContentLoaded",W),k(ea,2e4)),d.elements.length?(V(),z._lsFlush()):W()},checkElems:W,unveil:ba,_aLSL:da}}(),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)}),d=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++)d(a[b])},g=C(f);return{_:function(){a=b.getElementsByClassName(e.autosizesClass),j("resize",g)},checkElems:g,updateElem:d}}(),F=function(){!F.i&&b.getElementsByClassName&&(F.i=!0,E._(),D._())};return k(function(){e.init&&F()}),d={cfg:e,autoSizer:E,loader:D,init:F,uP:w,aC:s,rC:t,hC:r,fire:v,gW:y,rAF:z}});
3
+ !function(a,b){var c=function(){b(a.lazySizes),a.removeEventListener("lazyunveilread",c,!0)};b=b.bind(null,a,a.document),"object"==typeof module&&module.exports?b(require("lazysizes")):a.lazySizes?c():a.addEventListener("lazyunveilread",c,!0)}(window,function(a,b,c){"use strict";function d(a,c){if(!g[a]){var d=b.createElement(c?"link":"script"),e=b.getElementsByTagName("script")[0];c?(d.rel="stylesheet",d.href=a):d.src=a,g[a]=!0,g[d.src||d.href]=!0,e.parentNode.insertBefore(d,e)}}var e,f,g={};b.addEventListener&&(f=/\(|\)|\s|'/,e=function(a,c){var d=b.createElement("img");d.onload=function(){d.onload=null,d.onerror=null,d=null,c()},d.onerror=d.onload,d.src=a,d&&d.complete&&d.onload&&d.onload()},addEventListener("lazybeforeunveil",function(a){if(a.detail.instance==c){var b,g,h,i;if(!a.defaultPrevented){var j=a.target;if("none"==j.preload&&(j.preload=j.getAttribute("data-preload")||"auto"),null!=j.getAttribute("data-autoplay"))if(j.getAttribute("data-expand")&&!j.autoplay)try{j.play()}catch(a){}else requestAnimationFrame(function(){j.setAttribute("data-expand","-10"),c.aC(j,c.cfg.lazyClass)});b=j.getAttribute("data-link"),b&&d(b,!0),b=j.getAttribute("data-script"),b&&d(b),b=j.getAttribute("data-require"),b&&(c.cfg.requireJs?c.cfg.requireJs([b]):d(b)),h=j.getAttribute("data-bg"),h&&(a.detail.firesLoad=!0,g=function(){j.style.backgroundImage="url("+(f.test(h)?JSON.stringify(h):h)+")",a.detail.firesLoad=!1,c.fire(j,"_lazyloaded",{},!0,!0)},e(h,g)),i=j.getAttribute("data-poster"),i&&(a.detail.firesLoad=!0,g=function(){j.poster=i,a.detail.firesLoad=!1,c.fire(j,"_lazyloaded",{},!0,!0)},e(i,g))}}},!1))});
classes/static/toolbar.css CHANGED
@@ -207,7 +207,7 @@
207
  font-weight : 600 !important;
208
  font-size : 9px !important;
209
 
210
- margin-top : -10px !important;
211
 
212
  display : inline-block;
213
  vertical-align : top;
207
  font-weight : 600 !important;
208
  font-size : 9px !important;
209
 
210
+ margin-top : -5px !important;
211
 
212
  display : inline-block;
213
  vertical-align : top;
config/autoptimize_404_handler.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php exit;
2
+ /**
3
+ * Autoptimize's magic 404 handler.
4
+ *
5
+ * Configure your webserver to have requests for files that are no longer in
6
+ * /wp-content/cache/autoptimize/ to redirect to this file. AO's .htaccess file
7
+ * will have a "Errordocument:" directive to automatically do this.
8
+ *
9
+ * This file has simple logic to redirect to the "fallback" files that are
10
+ * created automatically by AO to avoid visitors seeing broken pages or
11
+ * Googlebot getting utterly confused.
12
+ *
13
+ * Error logging is off by default (don't want to flood your php errorlog, but
14
+ * can be enabled by this code snippet:
15
+ *
16
+ * add_filter( 'autoptimize_filter_cache_fallback_log_errors', '__return_true' );
17
+ *
18
+ * Warning: the fallback files might not apply to all pages, so this is a just
19
+ * a temporary solution, you really should clear any page cache to avoid requests
20
+ * to files that don't exist in AO's cache.
21
+ */
22
+
23
+ $original_request = strtok( $_SERVER['REQUEST_URI'], '?' );
24
+ $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );
25
+ $ao_cache_dir = '<!--ao-cache-dir-->';
26
+ $js_or_css = pathinfo( $original_request, PATHINFO_EXTENSION );
27
+ $fallback_path = $ao_cache_dir . $js_or_css . '/autoptimize_fallback.' . $js_or_css;
28
+
29
+ if ( $original_request !== $fallback_target && file_exists( $fallback_path ) ) {
30
+ // error_log( 'Autoptimize file ' . $original_request . ' not found, using fallback instead.' );
31
+ header( 'HTTP/1.1 301 Moved Permanently' );
32
+ header( 'Location: ' . $fallback_target );
33
+ } else {
34
+ // error_log( 'Autoptimize file ' . $original_request . ' not found, sending 410 gone response.' );
35
+ header( 'HTTP/1.1 410 Gone' );
36
+ }
37
+
38
+ exit();
readme.txt CHANGED
@@ -3,16 +3,15 @@ Contributors: futtta, optimizingmatters, zytzagoo, turl
3
  Tags: optimize, minify, performance, pagespeed, images, lazy-load, 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.1
9
 
10
  Autoptimize speeds up your website by optimizing JS, CSS, images (incl. lazy-load), HTML and Google Fonts, asyncing 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 lazy-load 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
 
18
  > <strong>Premium Support</strong><br>
@@ -235,7 +234,11 @@ JavaScript files that are not autoptimized (because they were excluded or becaus
235
 
236
  = How does image optimization work? =
237
 
238
- When image optimization is on, Autoptimize will look for png, gif, jpeg (.jpg) files in image tags and in your CSS files that are loaded from your own domain and change the src (source) to the ShortPixel CDN for those.
 
 
 
 
239
 
240
  = Where can I get more info on image optimization? =
241
 
@@ -275,6 +278,17 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
275
 
276
  == Changelog ==
277
 
 
 
 
 
 
 
 
 
 
 
 
278
  = 2.5.1 =
279
  * Images: Also optimize & lazyload &lt;picture>&lt;source>
280
  * Images: Misc. improvements to lazyload
3
  Tags: optimize, minify, performance, pagespeed, images, lazy-load, 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.3
7
+ Requires PHP: 5.6
8
+ Stable tag: 2.6.0
9
 
10
  Autoptimize speeds up your website by optimizing JS, CSS, images (incl. lazy-load), HTML and Google Fonts, asyncing 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 lazy-load 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
  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).
16
 
17
  > <strong>Premium Support</strong><br>
234
 
235
  = How does image optimization work? =
236
 
237
+ When image optimization is on, Autoptimize will look for png, gif, jpeg (.jpg) files in image tags and in your CSS files that are loaded from your own domain and change the src (source) to the ShortPixel CDN for those. Important: this can only work for publicly available images, otherwise the image optimization proxy will not be able to get the image to optimize it, so firewalls or proxies or password protection or even hotlinking-prevention might break image optimization.
238
+
239
+ = Can I use image optimization for my intranet/ protected site? =
240
+
241
+ No; Image optimization depends on the ability of the external image optimization service to fetch the original image from your site, optimize it and save it on the CDN. If you images cannot be downloaded by anonymous visitors (due to firewall/ proxy/ password protection/ hotlinking-protection), image optimization will not work.
242
 
243
  = Where can I get more info on image optimization? =
244
 
278
 
279
  == Changelog ==
280
 
281
+ = 2.6.0 =
282
+ * New: Autoptimize can be configured at network level or at individual site-level when on multisite.
283
+ * Extra: new option to specify what resources need to be preloaded.
284
+ * Extra: add `display=swap` to Autoptimized (CSS-based) Google Fonts.
285
+ * Images: support for lazyloading of background-images when set in the inline style attribute of a div.
286
+ * Images: updated to lazysizes 5.2.
287
+ * CSS/ JS: no longer add type attributes to Autoptimized resources.
288
+ * Improvement: cache clearing now also integrates with Kinsta, WP-Optimize & Nginx helper.
289
+ * Added "Critical CSS" tab to highlight the criticalcss.com integration, which will be fully included in Autoptimize 2.7.
290
+ * Batch of misc. smaller improvements & fixes, more info in the [GitHub commit log](https://github.com/futtta/autoptimize/commits/beta).
291
+
292
  = 2.5.1 =
293
  * Images: Also optimize & lazyload &lt;picture>&lt;source>
294
  * Images: Misc. improvements to lazyload