Autoptimize - Version 2.7.4

Version Description

  • Image optimization: also optimize icon links
  • Image optimization: fix webp-detection for Safari (contributed by @pinkasey)
  • Image lazyload: remove CSS that hides the placeholder image/ sets transistion between placeholder and final image
  • Critical CSS: new advanced option to unload CCSS on onLoad
  • Critical CSS improvement: cache templates in a transient to avoid overhead of having to search filesystem time and time again (contributed by @pratham2003)
  • Critical CSS improvement: better but still experimental jQuery deferring logic
  • Critical CSS fix: prevent MANUAL template-based rules being overwritten
  • CSS Inline & defer: move away from old loadCSS-based approach to Filamentgroup's new, simpler method
  • 404 fallback enabled by default for new installations
  • changed all occurences of blacklist/ whitelist to blocklist/ allowlist. The filters autoptimize_filter_js_whitelist and autoptimize_filter_css_whitelist still work in 2.7.4 but usage is deprecated and should be replaced with autoptimize_filter_js_allowlist and autoptimize_filter_css_allowlist.
  • updated readme to explicitly confirm this is GPL + praise open source projects used in Autoptimize as praise was long overdue!
  • tested and confirmed working on WordPress 5.5 beta 2
Download this release

Release Info

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

Code changes from version 2.7.3 to 2.7.4

autoptimize.php CHANGED
@@ -3,12 +3,13 @@
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.7.3
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
  /**
@@ -20,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {
20
  exit;
21
  }
22
 
23
- define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.7.3' );
24
 
25
  // plugin_dir_path() returns the trailing slash!
26
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
3
  * Plugin Name: Autoptimize
4
  * Plugin URI: https://autoptimize.com/
5
  * Description: Makes your site faster by optimizing CSS, JS, Images, Google fonts and more.
6
+ * Version: 2.7.4
7
  * Author: Frank Goossens (futtta)
8
  * Author URI: https://autoptimize.com/
9
  * Text Domain: autoptimize
10
+ * License: GPLv2
11
  * Released under the GNU General Public License (GPL)
12
+ * https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
13
  */
14
 
15
  /**
21
  exit;
22
  }
23
 
24
+ define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.7.4' );
25
 
26
  // plugin_dir_path() returns the trailing slash!
27
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
classes/autoptimizeConfig.php CHANGED
@@ -407,9 +407,9 @@ echo __( 'A comma-separated list of CSS you want to exclude from being optimized
407
  <?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>
408
  </tr>
409
  <tr valign="top">
410
- <th scope="row"><?php _e( 'Experimental: enable 404 fallbacks.', 'autoptimize' ); ?></th>
411
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_fallback" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_cache_fallback', '' ) ? 'checked="checked" ' : ''; ?>/>
412
- <?php _e( 'Sometimes Autoptimized JS/ CSS is referenced in cached HTML but is already removed, resulting in broken sites. This experimental feature tries to redirect those not-found files to "fallback"-versions, keeping the page/ site somewhat intact. In some cases this will require extra web-server level configuration to ensure <code>wp-content/autoptimize_404_handler.php</code> is set to handle 404\'s in <code>wp-content/cache/autoptimize</code>.', 'autoptimize' ); ?></label></td>
413
  </tr>
414
  <tr valign="top">
415
  <th scope="row"><?php _e( 'Also optimize for logged in editors/ administrators?', 'autoptimize' ); ?></th>
@@ -743,7 +743,7 @@ echo __( 'A comma-separated list of CSS you want to exclude from being optimized
743
  'autoptimize_optimize_logged' => 1,
744
  'autoptimize_optimize_checkout' => 0,
745
  'autoptimize_minify_excluded' => 1,
746
- 'autoptimize_cache_fallback' => '',
747
  );
748
 
749
  return $config;
@@ -785,25 +785,16 @@ echo __( 'A comma-separated list of CSS you want to exclude from being optimized
785
  return $defaults;
786
  }
787
 
788
- /**
789
- * Returns preload polyfill JS.
790
- *
791
- * @return string
792
- */
793
- public static function get_ao_css_preload_polyfill()
794
- {
795
- $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>' );
796
- return $preload_poly;
797
- }
798
-
799
  /**
800
  * Returns preload JS onload handler.
801
  *
 
 
802
  * @return string
803
  */
804
- public static function get_ao_css_preload_onload()
805
  {
806
- $preload_onload = apply_filters( 'autoptimize_filter_css_preload_onload', "this.onload=null;this.rel='stylesheet'" );
807
  return $preload_onload;
808
  }
809
 
407
  <?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>
408
  </tr>
409
  <tr valign="top">
410
+ <th scope="row"><?php _e( 'Enable 404 fallbacks?', 'autoptimize' ); ?></th>
411
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_fallback" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_cache_fallback', '1' ) ? 'checked="checked" ' : ''; ?>/>
412
+ <?php _e( 'Sometimes Autoptimized JS/ CSS is referenced in cached HTML but is already removed, resulting in broken sites. With this option on, Autoptimize will try to redirect those not-found files to "fallback"-versions, keeping the page/ site somewhat intact. In some cases this will require extra web-server level configuration to ensure <code>wp-content/autoptimize_404_handler.php</code> is set to handle 404\'s in <code>wp-content/cache/autoptimize</code>.', 'autoptimize' ); ?></label></td>
413
  </tr>
414
  <tr valign="top">
415
  <th scope="row"><?php _e( 'Also optimize for logged in editors/ administrators?', 'autoptimize' ); ?></th>
743
  'autoptimize_optimize_logged' => 1,
744
  'autoptimize_optimize_checkout' => 0,
745
  'autoptimize_minify_excluded' => 1,
746
+ 'autoptimize_cache_fallback' => 1,
747
  );
748
 
749
  return $config;
785
  return $defaults;
786
  }
787
 
 
 
 
 
 
 
 
 
 
 
 
788
  /**
789
  * Returns preload JS onload handler.
790
  *
791
+ * @param string $media media attribute value the JS to use.
792
+ *
793
  * @return string
794
  */
795
+ public static function get_ao_css_preload_onload( $media = 'all' )
796
  {
797
+ $preload_onload = apply_filters( 'autoptimize_filter_css_preload_onload', "this.onload=null;this.media='" . $media . "';" );
798
  return $preload_onload;
799
  }
800
 
classes/autoptimizeCriticalCSSBase.php CHANGED
@@ -20,9 +20,6 @@ class autoptimizeCriticalCSSBase {
20
  {
21
  // define constant, but only once.
22
  if ( ! defined( 'AO_CCSS_DIR' ) ) {
23
- // Define plugin version.
24
- define( 'AO_CCSS_VER', 'AO_' . AUTOPTIMIZE_PLUGIN_VERSION );
25
-
26
  // Define a constant with the directory to store critical CSS in.
27
  if ( is_multisite() ) {
28
  $blog_id = get_current_blog_id();
@@ -30,11 +27,10 @@ class autoptimizeCriticalCSSBase {
30
  } else {
31
  define( 'AO_CCSS_DIR', WP_CONTENT_DIR . '/uploads/ao_ccss/' );
32
  }
33
-
34
- // Define support files locations.
35
- define( 'AO_CCSS_LOCK', AO_CCSS_DIR . 'queue.lock' );
36
- define( 'AO_CCSS_LOG', AO_CCSS_DIR . 'queuelog.html' );
37
- define( 'AO_CCSS_DEBUG', AO_CCSS_DIR . 'queue.json' );
38
 
39
  // Define constants for criticalcss.com base path and API endpoints.
40
  // fixme: AO_CCSS_URL should be read from the autoptimize availability json stored as option.
@@ -43,6 +39,17 @@ class autoptimizeCriticalCSSBase {
43
  define( 'AO_CCSS_SLEEP', 10 );
44
  }
45
 
 
 
 
 
 
 
 
 
 
 
 
46
  $this->filepath = __FILE__;
47
 
48
  $this->setup();
@@ -108,6 +115,7 @@ class autoptimizeCriticalCSSBase {
108
  $autoptimize_ccss_options['ao_ccss_servicestatus'] = get_option( 'autoptimize_service_availablity' );
109
  $autoptimize_ccss_options['ao_ccss_deferjquery'] = get_option( 'autoptimize_ccss_deferjquery', false );
110
  $autoptimize_ccss_options['ao_ccss_domain'] = get_option( 'autoptimize_ccss_domain' );
 
111
 
112
  if ( strpos( $autoptimize_ccss_options['ao_ccss_domain'], 'http' ) === false && strpos( $autoptimize_ccss_options['ao_ccss_domain'], 'uggc' ) === 0 ) {
113
  $autoptimize_ccss_options['ao_ccss_domain'] = str_rot13( $autoptimize_ccss_options['ao_ccss_domain'] );
20
  {
21
  // define constant, but only once.
22
  if ( ! defined( 'AO_CCSS_DIR' ) ) {
 
 
 
23
  // Define a constant with the directory to store critical CSS in.
24
  if ( is_multisite() ) {
25
  $blog_id = get_current_blog_id();
27
  } else {
28
  define( 'AO_CCSS_DIR', WP_CONTENT_DIR . '/uploads/ao_ccss/' );
29
  }
30
+ }
31
+ if ( ! defined( 'AO_CCSS_VER' ) ) {
32
+ // Define plugin version.
33
+ define( 'AO_CCSS_VER', 'AO_' . AUTOPTIMIZE_PLUGIN_VERSION );
 
34
 
35
  // Define constants for criticalcss.com base path and API endpoints.
36
  // fixme: AO_CCSS_URL should be read from the autoptimize availability json stored as option.
39
  define( 'AO_CCSS_SLEEP', 10 );
40
  }
41
 
42
+ // Define support files locations, in case they are not already defined.
43
+ if ( ! defined( 'AO_CCSS_LOCK' ) ) {
44
+ define( 'AO_CCSS_LOCK', AO_CCSS_DIR . 'queue.lock' );
45
+ }
46
+ if ( ! defined( 'AO_CCSS_LOG' ) ) {
47
+ define( 'AO_CCSS_LOG', AO_CCSS_DIR . 'queuelog.html' );
48
+ }
49
+ if ( ! defined( 'AO_CCSS_DEBUG' ) ) {
50
+ define( 'AO_CCSS_DEBUG', AO_CCSS_DIR . 'queue.json' );
51
+ }
52
+
53
  $this->filepath = __FILE__;
54
 
55
  $this->setup();
115
  $autoptimize_ccss_options['ao_ccss_servicestatus'] = get_option( 'autoptimize_service_availablity' );
116
  $autoptimize_ccss_options['ao_ccss_deferjquery'] = get_option( 'autoptimize_ccss_deferjquery', false );
117
  $autoptimize_ccss_options['ao_ccss_domain'] = get_option( 'autoptimize_ccss_domain' );
118
+ $autoptimize_ccss_options['ao_ccss_unloadccss'] = get_option( 'autoptimize_ccss_unloadccss', false );
119
 
120
  if ( strpos( $autoptimize_ccss_options['ao_ccss_domain'], 'http' ) === false && strpos( $autoptimize_ccss_options['ao_ccss_domain'], 'uggc' ) === 0 ) {
121
  $autoptimize_ccss_options['ao_ccss_domain'] = str_rot13( $autoptimize_ccss_options['ao_ccss_domain'] );
classes/autoptimizeCriticalCSSCore.php CHANGED
@@ -25,6 +25,7 @@ class autoptimizeCriticalCSSCore {
25
  global $ao_css_defer;
26
  global $ao_ccss_deferjquery;
27
  global $ao_ccss_key;
 
28
 
29
  // add all filters to do CCSS if key present.
30
  if ( $ao_css_defer && isset( $ao_ccss_key ) && ! empty( $ao_ccss_key ) ) {
@@ -40,6 +41,11 @@ class autoptimizeCriticalCSSCore {
40
  add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_defer_jquery' ), 11, 1 );
41
  }
42
 
 
 
 
 
 
43
  // Order paths by length, as longest ones have greater priority in the rules.
44
  if ( ! empty( $ao_ccss_rules['paths'] ) ) {
45
  $keys = array_map( 'strlen', array_keys( $ao_ccss_rules['paths'] ) );
@@ -53,6 +59,9 @@ class autoptimizeCriticalCSSCore {
53
 
54
  // Extend conditional tags on plugin initalization.
55
  add_action( apply_filters( 'autoptimize_filter_ccss_extend_types_hook', 'init' ), array( $this, 'ao_ccss_extend_types' ) );
 
 
 
56
  }
57
  }
58
 
@@ -161,15 +170,19 @@ class autoptimizeCriticalCSSCore {
161
  }
162
 
163
  public function ao_ccss_defer_jquery( $in ) {
164
- // try to defer all JS (main goal being jquery.js as AO by default does not aggregate that).
 
165
  if ( ( ! is_user_logged_in() || $ao_ccss_loggedin ) && preg_match_all( '#<script.*>(.*)</script>#Usmi', $in, $matches, PREG_SET_ORDER ) ) {
166
  foreach ( $matches as $match ) {
167
- if ( ( ! preg_match( '/<script.* type\s?=.*>/', $match[0] ) || preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $match[0] ) ) && '' !== $match[1] && ( false !== strpos( $match[1], 'jQuery' ) || false !== strpos( $match[1], '$' ) ) ) {
168
- // inline js that requires jquery, wrap deferring JS around it to defer it.
169
- $new_match = 'var aoDeferInlineJQuery=function(){' . $match[1] . '}; if (document.readyState === "loading") {document.addEventListener("DOMContentLoaded", aoDeferInlineJQuery);} else {aoDeferInlineJQuery();}';
170
- $in = str_replace( $match[1], $new_match, $in );
171
- } elseif ( '' === $match[1] && false !== strpos( $match[0], 'src=' ) && false === strpos( $match[0], 'defer' ) ) {
172
- // linked non-aggregated JS, defer it.
 
 
 
173
  $new_match = str_replace( '<script ', '<script defer ', $match[0] );
174
  $in = str_replace( $match[0], $new_match, $in );
175
  }
@@ -178,6 +191,13 @@ class autoptimizeCriticalCSSCore {
178
  return $in;
179
  }
180
 
 
 
 
 
 
 
 
181
  public function ao_ccss_extend_types() {
182
  // Extend contidional tags
183
  // Attach the conditional tags array.
@@ -203,7 +223,12 @@ class autoptimizeCriticalCSSCore {
203
  }
204
 
205
  // Templates.
206
- $templates = wp_get_theme()->get_page_templates();
 
 
 
 
 
207
  foreach ( $templates as $tplfile => $tplname ) {
208
  array_unshift( $ao_ccss_types, 'template_' . $tplfile );
209
  }
@@ -529,17 +554,17 @@ class autoptimizeCriticalCSSCore {
529
  // Perform basic exploit avoidance and CSS validation.
530
  if ( ! empty( $ccss ) ) {
531
  // Try to avoid code injection.
532
- $blacklist = array( '#!/', 'function(', '<script', '<?php' );
533
- foreach ( $blacklist as $blacklisted ) {
534
- if ( strpos( $ccss, $blacklisted ) !== false ) {
535
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received contained blacklisted content.', 2 );
536
  return false;
537
  }
538
  }
539
 
540
  // Check for most basics CSS structures.
541
- $pinklist = array( '{', '}', ':' );
542
- foreach ( $pinklist as $needed ) {
543
  if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
544
  autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
545
  return false;
@@ -589,4 +614,10 @@ class autoptimizeCriticalCSSCore {
589
  error_log( $message, 3, AO_CCSS_LOG );
590
  }
591
  }
 
 
 
 
 
 
592
  }
25
  global $ao_css_defer;
26
  global $ao_ccss_deferjquery;
27
  global $ao_ccss_key;
28
+ global $ao_ccss_unloadccss;
29
 
30
  // add all filters to do CCSS if key present.
31
  if ( $ao_css_defer && isset( $ao_ccss_key ) && ! empty( $ao_ccss_key ) ) {
41
  add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_defer_jquery' ), 11, 1 );
42
  }
43
 
44
+ // conditionally add filter to unload the CCSS.
45
+ if ( $ao_ccss_unloadccss ) {
46
+ add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_unloadccss' ), 12, 1 );
47
+ }
48
+
49
  // Order paths by length, as longest ones have greater priority in the rules.
50
  if ( ! empty( $ao_ccss_rules['paths'] ) ) {
51
  $keys = array_map( 'strlen', array_keys( $ao_ccss_rules['paths'] ) );
59
 
60
  // Extend conditional tags on plugin initalization.
61
  add_action( apply_filters( 'autoptimize_filter_ccss_extend_types_hook', 'init' ), array( $this, 'ao_ccss_extend_types' ) );
62
+
63
+ // When autoptimize cache is cleared, also clear transient cache for page templates.
64
+ add_action( 'autoptimize_action_cachepurged', array( 'autoptimizeCriticalCSSCore', 'ao_ccss_clear_page_tpl_cache' ), 10, 0 );
65
  }
66
  }
67
 
170
  }
171
 
172
  public function ao_ccss_defer_jquery( $in ) {
173
+ global $ao_ccss_loggedin;
174
+ // defer all linked and inline JS.
175
  if ( ( ! is_user_logged_in() || $ao_ccss_loggedin ) && preg_match_all( '#<script.*>(.*)</script>#Usmi', $in, $matches, PREG_SET_ORDER ) ) {
176
  foreach ( $matches as $match ) {
177
+ if ( str_replace( apply_filters( 'autoptimize_filter_ccss_core_defer_exclude', array( 'data-noptimize="1"', 'data-cfasync="false"', 'data-pagespeed-no-defer' ) ), '', $match[0] ) !== $match[0] ) {
178
+ // do not touch JS with noptimize/ cfasync/ pagespeed-no-defer flags.
179
+ continue;
180
+ } elseif ( '' !== $match[1] && ( ! preg_match( '/<script.* type\s?=.*>/', $match[0] ) || preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $match[0] ) ) ) {
181
+ // base64-encode and defer all inline JS.
182
+ $base64_js = '<script defer src="data:text/javascript;base64,' . base64_encode( $match[1] ) . '"></script>';
183
+ $in = str_replace( $match[0], $base64_js, $in );
184
+ } elseif ( str_replace( array( ' defer', ' async' ), '', $match[0] ) === $match[0] ) {
185
+ // and defer linked JS unless already deferred or asynced.
186
  $new_match = str_replace( '<script ', '<script defer ', $match[0] );
187
  $in = str_replace( $match[0], $new_match, $in );
188
  }
191
  return $in;
192
  }
193
 
194
+ public function ao_ccss_unloadccss( $html_in ) {
195
+ // set media attrib of inline CCSS to none at onLoad to avoid it impacting full CSS (rarely needed).
196
+ $_unloadccss_js = apply_filters( 'autoptimize_filter_ccss_core_unloadccss_js', '<script>window.addEventListener("load", function(event) {document.getElementById("aoatfcss").media="none";})</script>' );
197
+
198
+ return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
199
+ }
200
+
201
  public function ao_ccss_extend_types() {
202
  // Extend contidional tags
203
  // Attach the conditional tags array.
223
  }
224
 
225
  // Templates.
226
+ // Transient cache to avoid frequent disk reads.
227
+ $templates = get_transient( 'autoptimize_ccss_page_templates' );
228
+ if ( ! $templates ) {
229
+ $templates = wp_get_theme()->get_page_templates();
230
+ set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
231
+ }
232
  foreach ( $templates as $tplfile => $tplname ) {
233
  array_unshift( $ao_ccss_types, 'template_' . $tplfile );
234
  }
554
  // Perform basic exploit avoidance and CSS validation.
555
  if ( ! empty( $ccss ) ) {
556
  // Try to avoid code injection.
557
+ $blocklist = array( '#!/', 'function(', '<script', '<?php' );
558
+ foreach ( $blocklist as $blocklisted ) {
559
+ if ( strpos( $ccss, $blocklisted ) !== false ) {
560
+ autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
561
  return false;
562
  }
563
  }
564
 
565
  // Check for most basics CSS structures.
566
+ $needlist = array( '{', '}', ':' );
567
+ foreach ( $needlist as $needed ) {
568
  if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
569
  autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
570
  return false;
614
  error_log( $message, 3, AO_CCSS_LOG );
615
  }
616
  }
617
+
618
+ public static function ao_ccss_clear_page_tpl_cache() {
619
+ // Clears transient cache for page templates.
620
+ delete_transient( 'autoptimize_ccss_page_templates' );
621
+ }
622
+
623
  }
classes/autoptimizeCriticalCSSCron.php CHANGED
@@ -141,7 +141,11 @@ class autoptimizeCriticalCSSCron {
141
  // Update job properties.
142
  $jprops['jid'] = $apireq['job']['id'];
143
  $jprops['jqstat'] = $apireq['job']['status'];
144
- $jprops['jrstat'] = $apireq['error'];
 
 
 
 
145
  $jprops['jvstat'] = 'NONE';
146
  $jprops['jftime'] = microtime( true );
147
  autoptimizeCriticalCSSCore::ao_ccss_log( 'Concurrent requests when processing job id <' . $jprops['ljid'] . '>, job status is now <' . $jprops['jqstat'] . '>', 3 );
@@ -269,9 +273,10 @@ class autoptimizeCriticalCSSCron {
269
  // ERROR: failed job
270
  // Update job properties.
271
  $jprops['jqstat'] = $apireq['job']['status'];
272
- if ( $apireq['error'] ) {
273
  $jprops['jrstat'] = $apireq['job']['error'];
274
  } else {
 
275
  }
276
  $jprops['jvstat'] = 'NONE';
277
  $jprops['jftime'] = microtime( true );
141
  // Update job properties.
142
  $jprops['jid'] = $apireq['job']['id'];
143
  $jprops['jqstat'] = $apireq['job']['status'];
144
+ if ( $apireq['job']['error'] ) {
145
+ $jprops['jrstat'] = $apireq['job']['error'];
146
+ } else {
147
+ $jprops['jrstat'] = 'Baby did a bad bad thing';
148
+ }
149
  $jprops['jvstat'] = 'NONE';
150
  $jprops['jftime'] = microtime( true );
151
  autoptimizeCriticalCSSCore::ao_ccss_log( 'Concurrent requests when processing job id <' . $jprops['ljid'] . '>, job status is now <' . $jprops['jqstat'] . '>', 3 );
273
  // ERROR: failed job
274
  // Update job properties.
275
  $jprops['jqstat'] = $apireq['job']['status'];
276
+ if ( $apireq['job']['error'] ) {
277
  $jprops['jrstat'] = $apireq['job']['error'];
278
  } else {
279
+ $jprops['jrstat'] = 'Baby did a bad bad thing';
280
  }
281
  $jprops['jvstat'] = 'NONE';
282
  $jprops['jftime'] = microtime( true );
classes/autoptimizeCriticalCSSEnqueue.php CHANGED
@@ -27,7 +27,7 @@ class autoptimizeCriticalCSSEnqueue {
27
  $enqueue = true;
28
 
29
  // ... which are not the ones below.
30
- if ( is_user_logged_in() || is_feed() || is_404() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || $self->ao_ccss_ua() || 'nokey' == $key['status'] || 'invalid' == $key['status'] ) {
31
  $enqueue = false;
32
  autoptimizeCriticalCSSCore::ao_ccss_log( "Job queuing is not available for WordPress's logged in users, feeds, error pages, ajax calls, to criticalcss.com itself or when a valid API key is not found", 3 );
33
  }
@@ -91,8 +91,8 @@ class autoptimizeCriticalCSSEnqueue {
91
  }
92
  }
93
 
94
- if ( $job_qualify && false == $rule_properties['hash'] && false != $rule_properties['file'] ) {
95
- // If job qualifies but rule hash is false and file isn't false (MANUAL rule), job does not qualify despite what previous evaluations says.
96
  $job_qualify = false;
97
  autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission DISQUALIFIED by MANUAL rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
98
  } elseif ( ! $job_qualify && empty( $rule_properties ) ) {
@@ -205,7 +205,11 @@ class autoptimizeCriticalCSSEnqueue {
205
  break;
206
  }
207
  } elseif ( strpos( $type, 'template_' ) !== false ) {
208
- // If templates; don't break, templates become manual-only rules.
 
 
 
 
209
  } else {
210
  // Match all other existing types
211
  // but remove prefix to be able to check if the function exists & returns true.
27
  $enqueue = true;
28
 
29
  // ... which are not the ones below.
30
+ if ( is_user_logged_in() || is_feed() || is_404() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || $self->ao_ccss_ua() || 'nokey' == $key['status'] || 'invalid' == $key['status'] || false === apply_filters( 'autoptimize_filter_ccss_enqueue_should_enqueue', true ) ) {
31
  $enqueue = false;
32
  autoptimizeCriticalCSSCore::ao_ccss_log( "Job queuing is not available for WordPress's logged in users, feeds, error pages, ajax calls, to criticalcss.com itself or when a valid API key is not found", 3 );
33
  }
91
  }
92
  }
93
 
94
+ if ( $job_qualify && ( ( false == $rule_properties['hash'] && false != $rule_properties['file'] ) || strpos( $req_type, 'template_' ) !== false ) ) {
95
+ // If job qualifies but rule hash is false and file isn't false (MANUAL rule) or if template, job does not qualify despite what previous evaluations says.
96
  $job_qualify = false;
97
  autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission DISQUALIFIED by MANUAL rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
98
  } elseif ( ! $job_qualify && empty( $rule_properties ) ) {
205
  break;
206
  }
207
  } elseif ( strpos( $type, 'template_' ) !== false ) {
208
+ // Match templates.
209
+ if ( is_page_template( substr( $type, 9 ) ) ) {
210
+ $page_type = $type;
211
+ break;
212
+ }
213
  } else {
214
  // Match all other existing types
215
  // but remove prefix to be able to check if the function exists & returns true.
classes/autoptimizeCriticalCSSSettings.php CHANGED
@@ -67,6 +67,7 @@ class autoptimizeCriticalCSSSettings {
67
  register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_forcepath' );
68
  register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_deferjquery' );
69
  register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_domain' );
 
70
 
71
  // And add submenu-page.
72
  add_submenu_page( null, 'Critical CSS', 'Critical CSS', 'manage_options', 'ao_critcss', array( $this, 'ao_criticalcsssettings_page' ) );
@@ -106,6 +107,7 @@ class autoptimizeCriticalCSSSettings {
106
  ${$_option} = $_value;
107
  }
108
  ?>
 
109
  <div class="wrap">
110
  <div id="autoptimize_main">
111
  <div id="ao_title_and_button">
67
  register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_forcepath' );
68
  register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_deferjquery' );
69
  register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_domain' );
70
+ register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_unloadccss' );
71
 
72
  // And add submenu-page.
73
  add_submenu_page( null, 'Critical CSS', 'Critical CSS', 'manage_options', 'ao_critcss', array( $this, 'ao_criticalcsssettings_page' ) );
107
  ${$_option} = $_value;
108
  }
109
  ?>
110
+ <script>document.title = "Autoptimize: <?php _e( 'Critical CSS', 'autoptimize' ); ?> " + document.title;</script>
111
  <div class="wrap">
112
  <div id="autoptimize_main">
113
  <div id="ao_title_and_button">
classes/autoptimizeImages.php CHANGED
@@ -489,6 +489,19 @@ class autoptimizeImages
489
  }
490
  }
491
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  public function filter_optimize_images( $in )
493
  {
494
  /*
@@ -609,6 +622,15 @@ class autoptimizeImages
609
  );
610
  }
611
 
 
 
 
 
 
 
 
 
 
612
  // lazyload: restore noscript tags + lazyload picture source tags and bgimage.
613
  if ( $this->should_lazyload() ) {
614
  $out = autoptimizeBase::restore_marked_content(
@@ -788,6 +810,7 @@ class autoptimizeImages
788
  $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
789
  $cdn_url = $this->get_cdn_url();
790
  if ( ! empty( $cdn_url ) ) {
 
791
  $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
792
  }
793
 
@@ -797,14 +820,14 @@ class autoptimizeImages
797
  }
798
 
799
  // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
800
- 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>' );
801
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
802
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
803
 
804
  // And add webp detection and loading JS.
805
  if ( $this->should_webp() ) {
806
  $_webp_detect = "function c_webp(A){var n=new Image;n.onload=function(){var e=0<n.width&&0<n.height;A(e)},n.onerror=function(){A(!1)},n.src='data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=='}function s_webp(e){window.supportsWebP=e}c_webp(s_webp);";
807
- $_webp_load = "document.addEventListener('lazybeforeunveil',function({target:c}){supportsWebP&&['data-src','data-srcset'].forEach(function(a){attr=c.getAttribute(a),null!==attr&&c.setAttribute(a,attr.replace(/\/client\//,'/client/to_webp,'))})});";
808
  echo apply_filters( 'autoptimize_filter_imgopt_webp_js', '<script' . $type_js . $noptimize_flag . '>' . $_webp_detect . $_webp_load . '</script>' );
809
  }
810
  }
489
  }
490
  }
491
 
492
+ public function replace_icon_callback( $matches )
493
+ {
494
+ if ( array_key_exists( '2', $matches ) ) {
495
+ $sizes = explode( 'x', $matches[2] );
496
+ $width = $sizes[0];
497
+ $height = $sizes[1];
498
+ } else {
499
+ $width = 180;
500
+ $height = 180;
501
+ }
502
+ return $this->replace_img_callback( $matches, $width, $height );
503
+ }
504
+
505
  public function filter_optimize_images( $in )
506
  {
507
  /*
622
  );
623
  }
624
 
625
+ // act on icon links.
626
+ if ( ( strpos( $out, '<link rel="icon"' ) !== false || ( strpos( $out, "<link rel='icon'" ) !== false ) ) && apply_filters( 'autoptimize_filter_imgopt_linkicon', true ) ) {
627
+ $out = preg_replace_callback(
628
+ '/<link\srel=(?:"|\')(?:apple-touch-)?icon(?:"|\').*\shref=(?:"|\')(.*)(?:"|\')(?:\ssizes=(?:"|\')(\d*x\d*)(?:"|\'))?\s\/>/Um',
629
+ array( $this, 'replace_icon_callback' ),
630
+ $out
631
+ );
632
+ }
633
+
634
  // lazyload: restore noscript tags + lazyload picture source tags and bgimage.
635
  if ( $this->should_lazyload() ) {
636
  $out = autoptimizeBase::restore_marked_content(
810
  $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
811
  $cdn_url = $this->get_cdn_url();
812
  if ( ! empty( $cdn_url ) ) {
813
+ $cdn_url = rtrim( $cdn_url, '/' );
814
  $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
815
  }
816
 
820
  }
821
 
822
  // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
823
+ echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<noscript><style>.lazyload{display:none;}</style></noscript>' );
824
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
825
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
826
 
827
  // And add webp detection and loading JS.
828
  if ( $this->should_webp() ) {
829
  $_webp_detect = "function c_webp(A){var n=new Image;n.onload=function(){var e=0<n.width&&0<n.height;A(e)},n.onerror=function(){A(!1)},n.src='data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=='}function s_webp(e){window.supportsWebP=e}c_webp(s_webp);";
830
+ $_webp_load = "document.addEventListener('lazybeforeunveil',function({target:b}){window.supportsWebP&&['data-src','data-srcset'].forEach(function(c){attr=b.getAttribute(c),null!==attr&&-1==attr.indexOf('/client/to_webp')&&b.setAttribute(c,attr.replace(/\/client\//,'/client/to_webp,'))})});";
831
  echo apply_filters( 'autoptimize_filter_imgopt_webp_js', '<script' . $type_js . $noptimize_flag . '>' . $_webp_detect . $_webp_load . '</script>' );
832
  }
833
  }
classes/autoptimizeMain.php CHANGED
@@ -585,6 +585,7 @@ class autoptimizeMain
585
  'autoptimize_ccss_forcepath',
586
  'autoptimize_ccss_deferjquery',
587
  'autoptimize_ccss_domain',
 
588
  );
589
 
590
  if ( ! is_multisite() ) {
585
  'autoptimize_ccss_forcepath',
586
  'autoptimize_ccss_deferjquery',
587
  'autoptimize_ccss_domain',
588
+ 'autoptimize_ccss_unloadccss',
589
  );
590
 
591
  if ( ! is_multisite() ) {
classes/autoptimizeScripts.php CHANGED
@@ -165,11 +165,11 @@ class autoptimizeScripts extends autoptimizeBase
165
  private $md5hash = '';
166
 
167
  /**
168
- * Setting (filter); whitelist of to be aggregated JS.
169
  *
170
  * @var string
171
  */
172
- private $whitelist = '';
173
 
174
  /**
175
  * Setting (filter); holds JS that should be removed.
@@ -206,9 +206,10 @@ class autoptimizeScripts extends autoptimizeBase
206
  }
207
 
208
  // only optimize known good JS?
209
- $whitelist_js = apply_filters( 'autoptimize_filter_js_whitelist', '', $this->content );
210
- if ( ! empty( $whitelist_js ) ) {
211
- $this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelist_js ) ) );
 
212
  }
213
 
214
  // is there JS we should simply remove?
@@ -301,7 +302,7 @@ class autoptimizeScripts extends autoptimizeBase
301
  // Get script files.
302
  if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) {
303
  foreach ( $matches[0] as $tag ) {
304
- // only consider script aggregation for types whitelisted in should_aggregate-function.
305
  $should_aggregate = $this->should_aggregate( $tag );
306
  if ( ! $should_aggregate ) {
307
  $tag = '';
@@ -602,7 +603,7 @@ class autoptimizeScripts extends autoptimizeBase
602
  }
603
 
604
  /**
605
- * Checks against the white- and blacklists.
606
  *
607
  * @param string $tag JS tag.
608
  */
@@ -612,13 +613,13 @@ class autoptimizeScripts extends autoptimizeBase
612
  return false;
613
  }
614
 
615
- if ( ! empty( $this->whitelist ) ) {
616
- foreach ( $this->whitelist as $match ) {
617
  if ( false !== strpos( $tag, $match ) ) {
618
  return true;
619
  }
620
  }
621
- // No match with whitelist.
622
  return false;
623
  } else {
624
  foreach ( $this->domove as $match ) {
@@ -645,9 +646,9 @@ class autoptimizeScripts extends autoptimizeBase
645
  }
646
 
647
  /**
648
- * Checks agains the blacklist.
649
  *
650
- * @param string $tag tag to check for blacklist (exclusions).
651
  */
652
  private function ismovable( $tag )
653
  {
@@ -757,6 +758,9 @@ class autoptimizeScripts extends autoptimizeBase
757
  return false;
758
  }
759
 
 
 
 
760
  // Store in cache.
761
  $cache->cache( $contents, 'text/javascript' );
762
  }
165
  private $md5hash = '';
166
 
167
  /**
168
+ * Setting (filter); allowlist of to be aggregated JS.
169
  *
170
  * @var string
171
  */
172
+ private $allowlist = '';
173
 
174
  /**
175
  * Setting (filter); holds JS that should be removed.
206
  }
207
 
208
  // only optimize known good JS?
209
+ $allowlist_js = apply_filters( 'autoptimize_filter_js_allowlist', '', $this->content );
210
+ $allowlist_js = apply_filters( 'autoptimize_filter_js_whitelist', $allowlist_js, $this->content ); // fixme: to be removed in next version.
211
+ if ( ! empty( $allowlist_js ) ) {
212
+ $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_js ) ) );
213
  }
214
 
215
  // is there JS we should simply remove?
302
  // Get script files.
303
  if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) {
304
  foreach ( $matches[0] as $tag ) {
305
+ // only consider script aggregation for types allowlisted in should_aggregate-function.
306
  $should_aggregate = $this->should_aggregate( $tag );
307
  if ( ! $should_aggregate ) {
308
  $tag = '';
603
  }
604
 
605
  /**
606
+ * Checks against the allow- and blocklists.
607
  *
608
  * @param string $tag JS tag.
609
  */
613
  return false;
614
  }
615
 
616
+ if ( ! empty( $this->allowlist ) ) {
617
+ foreach ( $this->allowlist as $match ) {
618
  if ( false !== strpos( $tag, $match ) ) {
619
  return true;
620
  }
621
  }
622
+ // No match with allowlist.
623
  return false;
624
  } else {
625
  foreach ( $this->domove as $match ) {
646
  }
647
 
648
  /**
649
+ * Checks agains the blocklist.
650
  *
651
+ * @param string $tag tag to check for blocklist (exclusions).
652
  */
653
  private function ismovable( $tag )
654
  {
758
  return false;
759
  }
760
 
761
+ // Filter contents of excluded minified CSS.
762
+ $contents = apply_filters( 'autoptimize_filter_js_single_after_minify', $contents );
763
+
764
  // Store in cache.
765
  $cache->cache( $contents, 'text/javascript' );
766
  }
classes/autoptimizeStyles.php CHANGED
@@ -94,11 +94,11 @@ class autoptimizeStyles extends autoptimizeBase
94
  private $defer_inline = '';
95
 
96
  /**
97
- * Setting for whitelist of what should be aggregated.
98
  *
99
  * @var string
100
  */
101
- private $whitelist = '';
102
 
103
  /**
104
  * Setting (only filter) for size under which CSS should be inlined instead of linked.
@@ -168,9 +168,10 @@ class autoptimizeStyles extends autoptimizeBase
168
  return false;
169
  }
170
 
171
- $whitelist_css = apply_filters( 'autoptimize_filter_css_whitelist', '', $this->content );
172
- if ( ! empty( $whitelist_css ) ) {
173
- $this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelist_css ) ) );
 
174
  }
175
 
176
  $removable_css = apply_filters( 'autoptimize_filter_css_removables', '' );
@@ -383,23 +384,27 @@ class autoptimizeStyles extends autoptimizeBase
383
  {
384
  // Defer single CSS if "inline & defer" is ON and there is inline CSS.
385
  if ( ! empty( $tag ) && false === strpos( $tag, ' onload=' ) && $this->defer && ! empty( $this->defer_inline ) && apply_filters( 'autoptimize_filter_css_defer_excluded', true, $tag ) ) {
386
- // Get/ set (via filter) the JS to be triggers onload of the preloaded CSS.
387
- $_preload_onload = apply_filters(
388
- 'autoptimize_filter_css_preload_onload',
389
- "this.onload=null;this.rel='stylesheet'",
390
- $url
391
- );
 
 
392
 
393
  // Adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback.
394
  $new_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>' . str_replace(
395
- array(
396
- "rel='stylesheet'",
397
- 'rel="stylesheet"',
398
- ),
399
- "rel='preload' as='style' onload=\"" . $_preload_onload . '"',
400
  $tag
401
  );
402
 
 
 
 
 
 
403
  return $new_tag;
404
  }
405
 
@@ -978,7 +983,7 @@ class autoptimizeStyles extends autoptimizeBase
978
 
979
  if ( $this->inline ) {
980
  foreach ( $this->csscode as $media => $code ) {
981
- $this->inject_in_html( '<style ' . $type_css . 'media="' . $media . '">' . $code . '</style>', $replace_tag );
982
  }
983
  } else {
984
  if ( $this->defer ) {
@@ -1018,28 +1023,24 @@ class autoptimizeStyles extends autoptimizeBase
1018
  if ( $this->defer ) {
1019
  $preload_onload = autoptimizeConfig::get_ao_css_preload_onload();
1020
 
1021
- $preload_css_block .= '<link rel="preload" as="style" media="' . $media . '" href="' . $url . '" onload="' . $preload_onload . '" />';
 
 
 
1022
  $noscript_css_block .= '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />';
1023
  } else {
1024
  if ( strlen( $this->csscode[ $media ] ) > $this->cssinlinesize ) {
1025
- $this->inject_in_html( '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />', $replace_tag );
1026
  } elseif ( strlen( $this->csscode[ $media ] ) > 0 ) {
1027
- $this->inject_in_html( '<style ' . $type_css . 'media="' . $media . '">' . $this->csscode[ $media ] . '</style>', $replace_tag );
1028
  }
1029
  }
1030
  }
1031
 
1032
  if ( $this->defer ) {
1033
- $preload_polyfill = autoptimizeConfig::get_ao_css_preload_polyfill();
1034
  $noscript_css_block .= '</noscript>';
1035
  // Inject inline critical CSS, the preloaded full CSS and the noscript-CSS.
1036
- $this->inject_in_html( $inlined_ccss_block . $preload_css_block . $noscript_css_block, $replace_tag );
1037
-
1038
- // Adds preload polyfill at end of body tag.
1039
- $this->inject_in_html(
1040
- apply_filters( 'autoptimize_css_preload_polyfill', $preload_polyfill ),
1041
- apply_filters( 'autoptimize_css_preload_polyfill_injectat', array( '</body>', 'before' ) )
1042
- );
1043
  }
1044
  }
1045
 
@@ -1142,13 +1143,13 @@ class autoptimizeStyles extends autoptimizeBase
1142
  return false;
1143
  }
1144
 
1145
- if ( ! empty( $this->whitelist ) ) {
1146
- foreach ( $this->whitelist as $match ) {
1147
  if ( false !== strpos( $tag, $match ) ) {
1148
  return true;
1149
  }
1150
  }
1151
- // no match with whitelist.
1152
  return false;
1153
  } else {
1154
  if ( is_array( $this->dontmove ) && ! empty( $this->dontmove ) ) {
@@ -1228,6 +1229,9 @@ class autoptimizeStyles extends autoptimizeBase
1228
  return false;
1229
  }
1230
 
 
 
 
1231
  // Store in cache.
1232
  $cache->cache( $contents, 'text/css' );
1233
  }
94
  private $defer_inline = '';
95
 
96
  /**
97
+ * Setting for allowlist of what should be aggregated.
98
  *
99
  * @var string
100
  */
101
+ private $allowlist = '';
102
 
103
  /**
104
  * Setting (only filter) for size under which CSS should be inlined instead of linked.
168
  return false;
169
  }
170
 
171
+ $allowlist_css = apply_filters( 'autoptimize_filter_css_allowlist', '', $this->content );
172
+ $allowlist_css = apply_filters( 'autoptimize_filter_css_whitelist', $allowlist_css, $this->content ); // fixme: to be removed in next version.
173
+ if ( ! empty( $allowlist_css ) ) {
174
+ $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_css ) ) );
175
  }
176
 
177
  $removable_css = apply_filters( 'autoptimize_filter_css_removables', '' );
384
  {
385
  // Defer single CSS if "inline & defer" is ON and there is inline CSS.
386
  if ( ! empty( $tag ) && false === strpos( $tag, ' onload=' ) && $this->defer && ! empty( $this->defer_inline ) && apply_filters( 'autoptimize_filter_css_defer_excluded', true, $tag ) ) {
387
+ // get media attribute and based on that create onload JS attribute value.
388
+ if ( false !== strpos( $tag, 'media=' ) ) {
389
+ preg_match( '#media=(?:"|\')([^>]*)(?:"|\')#Ui', $tag, $_medias );
390
+ $_media = $_medias[1];
391
+ } else {
392
+ $_media = 'all';
393
+ }
394
+ $_preload_onload = autoptimizeConfig::get_ao_css_preload_onload( $_media );
395
 
396
  // Adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback.
397
  $new_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>' . str_replace(
398
+ $_medias[0],
399
+ "media='print' onload=\"" . $_preload_onload . '"',
 
 
 
400
  $tag
401
  );
402
 
403
+ // Optionally (but default false) preload the (excluded) CSS-file.
404
+ if ( apply_filters( 'autoptimize_fitler_css_preload_and_print', false ) && 'none' !== $url ) {
405
+ $new_tag = '<link rel="preload" as="stylesheet" href="' . $url . '"/>' . $new_tag;
406
+ }
407
+
408
  return $new_tag;
409
  }
410
 
983
 
984
  if ( $this->inline ) {
985
  foreach ( $this->csscode as $media => $code ) {
986
+ $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<style ' . $type_css . 'media="' . $media . '">' . $code . '</style>' ), $replace_tag );
987
  }
988
  } else {
989
  if ( $this->defer ) {
1023
  if ( $this->defer ) {
1024
  $preload_onload = autoptimizeConfig::get_ao_css_preload_onload();
1025
 
1026
+ $preload_css_block .= '<link rel="stylesheet" media="print" href="' . $url . '" onload="' . $preload_onload . '" />';
1027
+ if ( apply_filters( 'autoptimize_fitler_css_preload_and_print', false ) ) {
1028
+ $preload_css_block = '<link rel="preload" as="stylesheet" href="' . $url . '"/>' . $preload_css_block;
1029
+ }
1030
  $noscript_css_block .= '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />';
1031
  } else {
1032
  if ( strlen( $this->csscode[ $media ] ) > $this->cssinlinesize ) {
1033
+ $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />' ), $replace_tag );
1034
  } elseif ( strlen( $this->csscode[ $media ] ) > 0 ) {
1035
+ $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<style ' . $type_css . 'media="' . $media . '">' . $this->csscode[ $media ] . '</style>' ), $replace_tag );
1036
  }
1037
  }
1038
  }
1039
 
1040
  if ( $this->defer ) {
 
1041
  $noscript_css_block .= '</noscript>';
1042
  // Inject inline critical CSS, the preloaded full CSS and the noscript-CSS.
1043
+ $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', $inlined_ccss_block . $preload_css_block . $noscript_css_block ), $replace_tag );
 
 
 
 
 
 
1044
  }
1045
  }
1046
 
1143
  return false;
1144
  }
1145
 
1146
+ if ( ! empty( $this->allowlist ) ) {
1147
+ foreach ( $this->allowlist as $match ) {
1148
  if ( false !== strpos( $tag, $match ) ) {
1149
  return true;
1150
  }
1151
  }
1152
+ // no match with allowlist.
1153
  return false;
1154
  } else {
1155
  if ( is_array( $this->dontmove ) && ! empty( $this->dontmove ) ) {
1229
  return false;
1230
  }
1231
 
1232
+ // Filter contents of excluded minified CSS.
1233
+ $contents = apply_filters( 'autoptimize_filter_css_single_after_minify', $contents );
1234
+
1235
  // Store in cache.
1236
  $cache->cache( $contents, 'text/css' );
1237
  }
classes/critcss-inc/admin_settings_adv.php CHANGED
@@ -16,6 +16,7 @@ function ao_ccss_render_adv() {
16
  global $ao_ccss_forcepath;
17
  global $ao_ccss_deferjquery;
18
  global $ao_ccss_domain;
 
19
 
20
  // In case domain is not set yet (done in cron.php).
21
  if ( empty( $ao_ccss_domain ) ) {
@@ -111,6 +112,17 @@ function ao_ccss_render_adv() {
111
  </p>
112
  </td>
113
  </tr>
 
 
 
 
 
 
 
 
 
 
 
114
  <tr>
115
  <th scope="row">
116
  <?php _e( 'Bound domain', 'autoptimize' ); ?>
16
  global $ao_ccss_forcepath;
17
  global $ao_ccss_deferjquery;
18
  global $ao_ccss_domain;
19
+ global $ao_ccss_unloadccss;
20
 
21
  // In case domain is not set yet (done in cron.php).
22
  if ( empty( $ao_ccss_domain ) ) {
112
  </p>
113
  </td>
114
  </tr>
115
+ <tr>
116
+ <th scope="row">
117
+ <?php _e( 'Unload critical CSS after page load?', 'autoptimize' ); ?>
118
+ </th>
119
+ <td>
120
+ <input type="checkbox" id="autoptimize_ccss_unloadccss" name="autoptimize_ccss_unloadccss" value="1" <?php checked( 1 == $ao_ccss_unloadccss ); ?>>
121
+ <p class="notes">
122
+ <?php _e( 'In rare cases the critical CSS needs to be removed once the full CSS loads, this option makes it so!', 'autoptimize' ); ?>
123
+ </p>
124
+ </td>
125
+ </tr>
126
  <tr>
127
  <th scope="row">
128
  <?php _e( 'Bound domain', 'autoptimize' ); ?>
readme.txt CHANGED
@@ -3,9 +3,9 @@ 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.9
6
- Tested up to: 5.4
7
  Requires PHP: 5.6
8
- Stable tag: 2.7.3
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
 
@@ -270,7 +270,7 @@ When both Autoptimize 2.7 and the separate Critical CSS power-up are installed a
270
 
271
  Autoptimize caches aggregated & optimized CSS/ JS and links to those cached files are stored in the HTML, which will be stored in a page cache (which can be a plugin, can be at host level, can be at 3rd party, in the Google cache, in a browser). If there is HTML in a page cache that links to Autoptimized CSS/ JS that has been removed in the mean time (when the cache was cleared) then the page from cache will not look/ work as expected as the CSS or JS were not found (a 404 error).
272
 
273
- This (new, experimental) setting aims to prevent things from breaking by serving "fallback" CSS or JS. The fallback-files are copies of the first Autoptimized CSS & JS files created after the cache was emptied and as such will based on the homepage. This means that the CSS/ JS migth not apply 100% on other pages, but at least the impact of missing CSS/ JS will be lessened (often significantly).
274
 
275
  When the option is enabled, Autoptimize adds an `ErrorDocument 404` to the .htaccess (as used by Apache) and will also hook into WordPress core `template_redirect` to capture 404's handled by Wordpress. When using NGINX something like below should work (I'm not an NGINX specialist, but it does work for me);
276
 
@@ -279,6 +279,22 @@ location ~* /wp-content/cache/autoptimize/.*\.(js|css)$ {
279
  try_files $uri $uri/ /wp-content/autoptimize_404_handler.php;
280
  }`
281
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  = Where can I get help? =
283
 
284
  You can get help on the [wordpress.org support forum](http://wordpress.org/support/plugin/autoptimize). If you are 100% sure this your problem cannot be solved using Autoptimize configuration and that you in fact discovered a bug in the code, you can [create an issue on GitHub](https://github.com/futtta/autoptimize/issues). If you're looking for premium support, check out our [Autoptimize Pro Support and Web Performance Optimization services](http://autoptimize.com/).
@@ -295,6 +311,20 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
295
 
296
  == Changelog ==
297
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  = 2.7.3 =
299
  * Critical CSS: cache settings in the PHP process instead of re-fetching them
300
  * Critical CSS: shorter intervals between calls to criticalcss.com (shortening the asynchronous job queue processing time)
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.9
6
+ Tested up to: 5.5
7
  Requires PHP: 5.6
8
+ Stable tag: 2.7.4
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
 
270
 
271
  Autoptimize caches aggregated & optimized CSS/ JS and links to those cached files are stored in the HTML, which will be stored in a page cache (which can be a plugin, can be at host level, can be at 3rd party, in the Google cache, in a browser). If there is HTML in a page cache that links to Autoptimized CSS/ JS that has been removed in the mean time (when the cache was cleared) then the page from cache will not look/ work as expected as the CSS or JS were not found (a 404 error).
272
 
273
+ This setting aims to prevent things from breaking by serving "fallback" CSS or JS. The fallback-files are copies of the first Autoptimized CSS & JS files created after the cache was emptied and as such will based on the homepage. This means that the CSS/ JS migth not apply 100% on other pages, but at least the impact of missing CSS/ JS will be lessened (often significantly).
274
 
275
  When the option is enabled, Autoptimize adds an `ErrorDocument 404` to the .htaccess (as used by Apache) and will also hook into WordPress core `template_redirect` to capture 404's handled by Wordpress. When using NGINX something like below should work (I'm not an NGINX specialist, but it does work for me);
276
 
279
  try_files $uri $uri/ /wp-content/autoptimize_404_handler.php;
280
  }`
281
 
282
+ = What open source software/ projects are used in Autoptimize? =
283
+
284
+ The following great open source projects are used in Autoptimize in some form or another:
285
+
286
+ * [Mr Clay's Minify](https://github.com/mrclay/minify/) for JS & HTML minification
287
+ * [YUI CSS compressor PHP Port](https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port) for CSS minification
288
+ * [Lazysizes](https://github.com/aFarkas/lazysizes) for lazyload
289
+ * [Persist Admin Notices Dismissal](https://github.com/w3guy/persist-admin-notices-dismissal) for notices in the administration screens
290
+ * [Plugin Update Checker](https://github.com/YahnisElsts/plugin-update-checker/) for automated updates from Github for the beta version
291
+ * [LoadCSS](https://github.com/filamentgroup/loadCSS) for deferring full CSS
292
+ * [jQuery cookie](https://github.com/carhartl/jquery-cookie) to store the "futtta about" category selection in a cookie
293
+ * [jQuery tablesorter](https://github.com/christianbach/tablesorter) for the critical CSS rules/ jobs display
294
+ * [jQuery unslider](https://github.com/idiot/unslider/) for the mini-slider in the top right corner on the main settings page (repo gone)
295
+ * [JavaScript-md5](https://github.com/blueimp/JavaScript-MD5) for critical CSS rules editing
296
+ * [Speed Booster Pack](https://wordpress.org/plugins/speed-booster-pack/) for advanced JS deferring
297
+
298
  = Where can I get help? =
299
 
300
  You can get help on the [wordpress.org support forum](http://wordpress.org/support/plugin/autoptimize). If you are 100% sure this your problem cannot be solved using Autoptimize configuration and that you in fact discovered a bug in the code, you can [create an issue on GitHub](https://github.com/futtta/autoptimize/issues). If you're looking for premium support, check out our [Autoptimize Pro Support and Web Performance Optimization services](http://autoptimize.com/).
311
 
312
  == Changelog ==
313
 
314
+ = 2.7.4 =
315
+ * Image optimization: also optimize icon links
316
+ * Image optimization: fix webp-detection for Safari (contributed by @pinkasey)
317
+ * Image lazyload: remove CSS that hides the placeholder image/ sets transistion between placeholder and final image
318
+ * Critical CSS: new advanced option to unload CCSS on onLoad
319
+ * Critical CSS improvement: cache templates in a transient to avoid overhead of having to search filesystem time and time again (contributed by @pratham2003)
320
+ * Critical CSS improvement: better but still experimental jQuery deferring logic
321
+ * Critical CSS fix: prevent MANUAL template-based rules being overwritten
322
+ * CSS Inline & defer: move away from old loadCSS-based approach to [Filamentgroup's new, simpler method](https://www.filamentgroup.com/lab/load-css-simpler/)
323
+ * 404 fallback enabled by default for new installations
324
+ * changed all occurences of blacklist/ whitelist to blocklist/ allowlist. The filters `autoptimize_filter_js_whitelist` and `autoptimize_filter_css_whitelist` still work in 2.7.4 but usage is deprecated and should be replaced with `autoptimize_filter_js_allowlist` and `autoptimize_filter_css_allowlist`.
325
+ * updated readme to explicitly confirm this is GPL + praise open source projects used in Autoptimize as praise was long overdue!
326
+ * tested and confirmed working on WordPress 5.5 beta 2
327
+
328
  = 2.7.3 =
329
  * Critical CSS: cache settings in the PHP process instead of re-fetching them
330
  * Critical CSS: shorter intervals between calls to criticalcss.com (shortening the asynchronous job queue processing time)