Autoptimize - Version 2.2.0

Version Description

  • new: Autoptimize minifies first (caching the individual snippets) and aggregrates the minified snippets, resulting in huge performance improvements for uncached JS/ CSS.
  • new: option to enable/ disable AO for logged in users (on by default)
  • new: option to enable/ disable AO on WooCommerce, Easy Digital Downloads or WP eCommerce cart/ checkout page (on by default)
  • improvement: switched to rel=preload + Filamentgroups loadCSS for CSS deferring
  • improvement: switched to YUI CSS minifier PHP-port 2.8.4-p10 (so not to the 3.x branch yet)
  • improvements to the logic of which JS/ CSS can be optimized (getPath function) increasing reliability of the aggregation process
  • security: made placeholder replacement less naive to protect against XSS and LFI vulnerability as reported by Matthew Barry and fixed with great help from Matthew and Tomas Trkulja. Thanks guys!!
  • API: Lots of extra filters, making AO (even) more flexible.
  • Lots of bugfixes and smaller improvements (see GitHub commit log)
  • tested and confirmed working in WordPress 4.8
Download this release

Release Info

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

Code changes from version 2.1.1 to 2.2.0

autoptimize.php CHANGED
@@ -1,9 +1,9 @@
1
  <?php
2
  /*
3
  Plugin Name: Autoptimize
4
- Plugin URI: http://blog.futtta.be/autoptimize
5
  Description: Optimizes your website, concatenating the CSS and JavaScript code, and compressing it.
6
- Version: 2.1.1
7
  Author: Frank Goossens (futtta)
8
  Author URI: http://blog.futtta.be/
9
  Domain Path: localization/
@@ -54,7 +54,7 @@ $conf = autoptimizeConfig::instance();
54
  /* Check if we're updating, in which case we might need to do stuff and flush the cache
55
  to avoid old versions of aggregated files lingering around */
56
 
57
- $autoptimize_version="2.1.0";
58
  $autoptimize_db_version=get_option('autoptimize_version','none');
59
 
60
  if ($autoptimize_db_version !== $autoptimize_version) {
@@ -78,17 +78,17 @@ add_action( 'init', 'autoptimize_load_plugin_textdomain' );
78
  function autoptimize_uninstall(){
79
  autoptimizeCache::clearall();
80
 
81
- $delete_options=array("autoptimize_cache_clean", "autoptimize_cache_nogzip", "autoptimize_css", "autoptimize_css_datauris", "autoptimize_css_justhead", "autoptimize_css_defer", "autoptimize_css_defer_inline", "autoptimize_css_inline", "autoptimize_css_exclude", "autoptimize_html", "autoptimize_html_keepcomments", "autoptimize_js", "autoptimize_js_exclude", "autoptimize_js_forcehead", "autoptimize_js_justhead", "autoptimize_js_trycatch", "autoptimize_version", "autoptimize_show_adv", "autoptimize_cdn_url", "autoptimize_cachesize_notice","autoptimize_css_include_inline","autoptimize_js_include_inline","autoptimize_css_nogooglefont");
82
 
83
  if ( !is_multisite() ) {
84
- foreach ($delete_options as $del_opt) { delete_option( $del_opt ); }
85
  } else {
86
  global $wpdb;
87
  $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
88
  $original_blog_id = get_current_blog_id();
89
  foreach ( $blog_ids as $blog_id ) {
90
  switch_to_blog( $blog_id );
91
- foreach ($delete_options as $del_opt) { delete_option( $del_opt ); }
92
  }
93
  switch_to_blog( $original_blog_id );
94
  }
@@ -112,7 +112,7 @@ function autoptimize_update_config_notice() {
112
 
113
  function autoptimize_cache_unavailable_notice() {
114
  echo '<div class="error"><p>';
115
- _e('Autoptimize cannot write to the cache directory (default: /wp-content/cache/autoptimize), please fix to enable CSS/ JS optimization!', 'autoptimize' );
116
  echo '</p></div>';
117
  }
118
 
@@ -132,10 +132,31 @@ function autoptimize_start_buffering() {
132
  $ao_noptimize = true;
133
  }
134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  // filter you can use to block autoptimization on your own terms
136
  $ao_noptimize = (bool) apply_filters( 'autoptimize_filter_noptimize', $ao_noptimize );
137
 
138
  if (!is_feed() && !$ao_noptimize && !is_admin() && ( !function_exists('is_customize_preview') || !is_customize_preview() ) ) {
 
 
 
 
 
139
  // Config element
140
  $conf = autoptimizeConfig::instance();
141
 
@@ -178,7 +199,7 @@ function autoptimize_start_buffering() {
178
  }
179
  } else {
180
  if (!class_exists('CSSmin')) {
181
- @include(AUTOPTIMIZE_PLUGIN_DIR.'classes/external/php/yui-php-cssmin-2.4.8-4_fgo.php');
182
  }
183
  }
184
  if ( ! defined( 'COMPRESS_CSS' )) {
@@ -192,7 +213,6 @@ function autoptimize_start_buffering() {
192
  ob_end_clean();
193
  }
194
  }
195
-
196
  // Now, start the real thing!
197
  ob_start('autoptimize_end_buffering');
198
  }
@@ -201,7 +221,7 @@ function autoptimize_start_buffering() {
201
  // Action on end, this is where the magic happens
202
  function autoptimize_end_buffering($content) {
203
  if ( ((stripos($content,"<html") === false) && (stripos($content,"<!DOCTYPE html") === false)) || preg_match('/<html[^>]*(?:amp|⚡)/',$content) === 1 || stripos($content,"<xsl:stylesheet") !== false ) { return $content; }
204
-
205
  // load URL constants as late as possible to allow domain mapper to kick in
206
  if (function_exists("domain_mapping_siteurl")) {
207
  define('AUTOPTIMIZE_WP_SITE_URL',domain_mapping_siteurl(get_current_blog_id()));
@@ -282,14 +302,19 @@ if ( autoptimizeCache::cacheavail() ) {
282
  if (defined('AUTOPTIMIZE_INIT_EARLIER')) {
283
  add_action('init','autoptimize_start_buffering',-1);
284
  } else {
285
- add_action('template_redirect','autoptimize_start_buffering',2);
 
286
  }
287
  }
288
  } else {
289
  add_action('admin_notices', 'autoptimize_cache_unavailable_notice');
290
  }
291
 
292
- register_uninstall_hook(__FILE__, "autoptimize_uninstall");
 
 
 
 
293
  include_once('classlesses/autoptimizeCacheChecker.php');
294
 
295
  // Do not pollute other plugins
1
  <?php
2
  /*
3
  Plugin Name: Autoptimize
4
+ Plugin URI: http://autoptimize.com/
5
  Description: Optimizes your website, concatenating the CSS and JavaScript code, and compressing it.
6
+ Version: 2.2.0
7
  Author: Frank Goossens (futtta)
8
  Author URI: http://blog.futtta.be/
9
  Domain Path: localization/
54
  /* Check if we're updating, in which case we might need to do stuff and flush the cache
55
  to avoid old versions of aggregated files lingering around */
56
 
57
+ $autoptimize_version="2.2.0";
58
  $autoptimize_db_version=get_option('autoptimize_version','none');
59
 
60
  if ($autoptimize_db_version !== $autoptimize_version) {
78
  function autoptimize_uninstall(){
79
  autoptimizeCache::clearall();
80
 
81
+ $delete_options=array("autoptimize_cache_clean", "autoptimize_cache_nogzip", "autoptimize_css", "autoptimize_css_datauris", "autoptimize_css_justhead", "autoptimize_css_defer", "autoptimize_css_defer_inline", "autoptimize_css_inline", "autoptimize_css_exclude", "autoptimize_html", "autoptimize_html_keepcomments", "autoptimize_js", "autoptimize_js_exclude", "autoptimize_js_forcehead", "autoptimize_js_justhead", "autoptimize_js_trycatch", "autoptimize_version", "autoptimize_show_adv", "autoptimize_cdn_url", "autoptimize_cachesize_notice","autoptimize_css_include_inline","autoptimize_js_include_inline","autoptimize_css_nogooglefont","autoptimize_optimize_logged","autoptimize_optimize_checkout");
82
 
83
  if ( !is_multisite() ) {
84
+ foreach ($delete_options as $del_opt) { delete_option( $del_opt ); }
85
  } else {
86
  global $wpdb;
87
  $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
88
  $original_blog_id = get_current_blog_id();
89
  foreach ( $blog_ids as $blog_id ) {
90
  switch_to_blog( $blog_id );
91
+ foreach ($delete_options as $del_opt) { delete_option( $del_opt ); }
92
  }
93
  switch_to_blog( $original_blog_id );
94
  }
112
 
113
  function autoptimize_cache_unavailable_notice() {
114
  echo '<div class="error"><p>';
115
+ printf( __( 'Autoptimize cannot write to the cache directory (%s), please fix to enable CSS/ JS optimization!', 'autoptimize' ), AUTOPTIMIZE_CACHE_DIR );
116
  echo '</p></div>';
117
  }
118
 
132
  $ao_noptimize = true;
133
  }
134
 
135
+ // if setting says not to optimize logged in user and user is logged in
136
+ if ( get_option('autoptimize_optimize_logged','on') !== 'on' && is_user_logged_in() && current_user_can('edit_posts') ) {
137
+ $ao_noptimize = true;
138
+ }
139
+
140
+ // if setting says not to optimize cart/ checkout
141
+ if ( get_option('autoptimize_optimize_checkout','on') !== 'on' ) {
142
+ // checking for woocommerce, easy digital downloads and wp ecommerce
143
+ foreach ( array("is_checkout","is_cart","edd_is_checkout","wpsc_is_cart","wpsc_is_checkout") as $shopCond ) {
144
+ if ( function_exists($shopCond) && $shopCond() ) {
145
+ $ao_noptimize = true;
146
+ break;
147
+ }
148
+ }
149
+ }
150
+
151
  // filter you can use to block autoptimization on your own terms
152
  $ao_noptimize = (bool) apply_filters( 'autoptimize_filter_noptimize', $ao_noptimize );
153
 
154
  if (!is_feed() && !$ao_noptimize && !is_admin() && ( !function_exists('is_customize_preview') || !is_customize_preview() ) ) {
155
+ // load speedupper conditionally (true by default?)
156
+ if ( apply_filters('autoptimize_filter_speedupper', true) ) {
157
+ include(AUTOPTIMIZE_PLUGIN_DIR.'classlesses/autoptimizeSpeedupper.php');
158
+ }
159
+
160
  // Config element
161
  $conf = autoptimizeConfig::instance();
162
 
199
  }
200
  } else {
201
  if (!class_exists('CSSmin')) {
202
+ @include(AUTOPTIMIZE_PLUGIN_DIR.'classes/external/php/yui-php-cssmin-2.4.8-p10/cssmin.php');
203
  }
204
  }
205
  if ( ! defined( 'COMPRESS_CSS' )) {
213
  ob_end_clean();
214
  }
215
  }
 
216
  // Now, start the real thing!
217
  ob_start('autoptimize_end_buffering');
218
  }
221
  // Action on end, this is where the magic happens
222
  function autoptimize_end_buffering($content) {
223
  if ( ((stripos($content,"<html") === false) && (stripos($content,"<!DOCTYPE html") === false)) || preg_match('/<html[^>]*(?:amp|⚡)/',$content) === 1 || stripos($content,"<xsl:stylesheet") !== false ) { return $content; }
224
+
225
  // load URL constants as late as possible to allow domain mapper to kick in
226
  if (function_exists("domain_mapping_siteurl")) {
227
  define('AUTOPTIMIZE_WP_SITE_URL',domain_mapping_siteurl(get_current_blog_id()));
302
  if (defined('AUTOPTIMIZE_INIT_EARLIER')) {
303
  add_action('init','autoptimize_start_buffering',-1);
304
  } else {
305
+ if (!defined('AUTOPTIMIZE_HOOK_INTO')) { define('AUTOPTIMIZE_HOOK_INTO', 'template_redirect'); }
306
+ add_action(constant("AUTOPTIMIZE_HOOK_INTO"),'autoptimize_start_buffering',2);
307
  }
308
  }
309
  } else {
310
  add_action('admin_notices', 'autoptimize_cache_unavailable_notice');
311
  }
312
 
313
+ function autoptimize_activate() {
314
+ register_uninstall_hook( __FILE__, 'autoptimize_uninstall' );
315
+ }
316
+ register_activation_hook( __FILE__, 'autoptimize_activate' );
317
+
318
  include_once('classlesses/autoptimizeCacheChecker.php');
319
 
320
  // Do not pollute other plugins
classes/autoptimizeBase.php CHANGED
@@ -30,6 +30,7 @@ abstract class autoptimizeBase {
30
  }
31
 
32
  $siteHost=parse_url(AUTOPTIMIZE_WP_SITE_URL,PHP_URL_HOST);
 
33
 
34
  // normalize
35
  if (strpos($url,'//')===0) {
@@ -46,6 +47,10 @@ abstract class autoptimizeBase {
46
  $url = AUTOPTIMIZE_WP_SITE_URL.str_repeat("/..",$subdir_levels).$url;
47
  }
48
  }
 
 
 
 
49
 
50
  // first check; hostname wp site should be hostname of url
51
  $thisHost=@parse_url($url,PHP_URL_HOST);
@@ -82,17 +87,28 @@ abstract class autoptimizeBase {
82
 
83
  // try to remove "wp root url" from url while not minding http<>https
84
  $tmp_ao_root = preg_replace('/https?:/','',AUTOPTIMIZE_WP_ROOT_URL);
 
 
 
 
85
  $tmp_url = preg_replace('/https?:/','',$url);
86
  $path = str_replace($tmp_ao_root,'',$tmp_url);
87
 
88
- // final check; if path starts with :// or //, this is not a URL in the WP context and we have to assume we can't aggregate
89
  if (preg_match('#^:?//#',$path)) {
90
  /** External script/css (adsense, etc) */
91
  return false;
92
  }
93
 
 
94
  $path = str_replace('//','/',WP_ROOT_DIR.$path);
95
- return $path;
 
 
 
 
 
 
96
  }
97
 
98
  // needed for WPML-filter
@@ -283,12 +299,12 @@ abstract class autoptimizeBase {
283
  protected function inject_minified($in) {
284
  if ( strpos( $in, '%%INJECTLATER%%' ) !== false ) {
285
  $out = preg_replace_callback(
286
- '#%%INJECTLATER'.AUTOPTIMIZE_HASH.'%%(.*?)%%INJECTLATER%%#is',
287
  create_function(
288
  '$matches',
289
  '$filepath=base64_decode(strtok($matches[1],"|"));
290
  $filecontent=file_get_contents($filepath);
291
-
292
  // remove BOM
293
  $filecontent = preg_replace("#\x{EF}\x{BB}\x{BF}#","",$filecontent);
294
 
@@ -300,7 +316,7 @@ abstract class autoptimizeBase {
300
  $filecontent=preg_replace("#^\s*\/\*[^!].*\*\/\s?#Um","",$filecontent);
301
  $filecontent=preg_replace("#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $filecontent);
302
 
303
- // specific stuff for JS-files
304
  if (substr($filepath,-3,3)===".js") {
305
  if ((substr($filecontent,-1,1)!==";")&&(substr($filecontent,-1,1)!=="}")) {
306
  $filecontent.=";";
@@ -325,4 +341,78 @@ abstract class autoptimizeBase {
325
  }
326
  return $out;
327
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  }
30
  }
31
 
32
  $siteHost=parse_url(AUTOPTIMIZE_WP_SITE_URL,PHP_URL_HOST);
33
+ $contentHost=parse_url(AUTOPTIMIZE_WP_ROOT_URL,PHP_URL_HOST);
34
 
35
  // normalize
36
  if (strpos($url,'//')===0) {
47
  $url = AUTOPTIMIZE_WP_SITE_URL.str_repeat("/..",$subdir_levels).$url;
48
  }
49
  }
50
+
51
+ if ($siteHost !== $contentHost) {
52
+ $url=str_replace(AUTOPTIMIZE_WP_CONTENT_URL,AUTOPTIMIZE_WP_SITE_URL.AUTOPTIMIZE_WP_CONTENT_NAME,$url);
53
+ }
54
 
55
  // first check; hostname wp site should be hostname of url
56
  $thisHost=@parse_url($url,PHP_URL_HOST);
87
 
88
  // try to remove "wp root url" from url while not minding http<>https
89
  $tmp_ao_root = preg_replace('/https?:/','',AUTOPTIMIZE_WP_ROOT_URL);
90
+ if ($siteHost !== $contentHost) {
91
+ // as we replaced the content-domain with the site-domain, we should match against that
92
+ $tmp_ao_root = preg_replace('/https?:/','',AUTOPTIMIZE_WP_SITE_URL);
93
+ }
94
  $tmp_url = preg_replace('/https?:/','',$url);
95
  $path = str_replace($tmp_ao_root,'',$tmp_url);
96
 
97
+ // if path starts with :// or //, this is not a URL in the WP context and we have to assume we can't aggregate
98
  if (preg_match('#^:?//#',$path)) {
99
  /** External script/css (adsense, etc) */
100
  return false;
101
  }
102
 
103
+ // prepend with WP_ROOT_DIR to have full path to file
104
  $path = str_replace('//','/',WP_ROOT_DIR.$path);
105
+
106
+ // final check: does file exist and is it readable
107
+ if (file_exists($path) && is_file($path) && is_readable($path)) {
108
+ return $path;
109
+ } else {
110
+ return false;
111
+ }
112
  }
113
 
114
  // needed for WPML-filter
299
  protected function inject_minified($in) {
300
  if ( strpos( $in, '%%INJECTLATER%%' ) !== false ) {
301
  $out = preg_replace_callback(
302
+ '#\/\*\!%%INJECTLATER'.AUTOPTIMIZE_HASH.'%%(.*?)%%INJECTLATER%%\*\/#is',
303
  create_function(
304
  '$matches',
305
  '$filepath=base64_decode(strtok($matches[1],"|"));
306
  $filecontent=file_get_contents($filepath);
307
+
308
  // remove BOM
309
  $filecontent = preg_replace("#\x{EF}\x{BB}\x{BF}#","",$filecontent);
310
 
316
  $filecontent=preg_replace("#^\s*\/\*[^!].*\*\/\s?#Um","",$filecontent);
317
  $filecontent=preg_replace("#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $filecontent);
318
 
319
+ // differentiate between JS, CSS and other files
320
  if (substr($filepath,-3,3)===".js") {
321
  if ((substr($filecontent,-1,1)!==";")&&(substr($filecontent,-1,1)!=="}")) {
322
  $filecontent.=";";
341
  }
342
  return $out;
343
  }
344
+
345
+ protected function minify_single($pathIn) {
346
+ // determine JS or CSS and set var (also mimetype), return false if neither
347
+ if ( $this->str_ends_in($pathIn,".js") === true ) {
348
+ $codeType="js";
349
+ $codeMime="text/javascript";
350
+ } else if ( $this->str_ends_in($pathIn,".css") === true ) {
351
+ $codeType="css";
352
+ $codeMime="text/css";
353
+ } else {
354
+ return false;
355
+ }
356
+
357
+ // if min.js or min.css return false
358
+ if (( $this->str_ends_in($pathIn,"-min.".$codeType) === true ) || ( $this->str_ends_in($pathIn,".min.".$codeType) === true ) || ( $this->str_ends_in($pathIn,"js/jquery/jquery.js") === true ) ) {
359
+ return false;
360
+ }
361
+
362
+ // read file, return false if empty
363
+ $_toMinify = file_get_contents($pathIn);
364
+ if ( empty($_toMinify) ) return false;
365
+
366
+ // check cache
367
+ $_md5hash = "single_".md5($_toMinify);
368
+ $_cache = new autoptimizeCache($_md5hash,$codeType);
369
+ if ($_cache->check() ) {
370
+ $_CachedMinifiedUrl = AUTOPTIMIZE_CACHE_URL.$_cache->getname();
371
+ } else {
372
+ // if not in cache first minify
373
+ $_Minified = $_toMinify;
374
+ if ($codeType === "js") {
375
+ if (class_exists('JSMin') && apply_filters( 'autoptimize_js_do_minify' , true)) {
376
+ if (@is_callable(array("JSMin","minify"))) {
377
+ $tmp_code = trim(JSMin::minify($_toMinify));
378
+ }
379
+ }
380
+ } else if ($codeType === "css") {
381
+ if (class_exists('Minify_CSS_Compressor')) {
382
+ $tmp_code = trim(Minify_CSS_Compressor::process($_toMinify));
383
+ } else if(class_exists('CSSmin')) {
384
+ $cssmin = new CSSmin();
385
+ if (method_exists($cssmin,"run")) {
386
+ $tmp_code = trim($cssmin->run($_toMinify));
387
+ } elseif (@is_callable(array($cssmin,"minify"))) {
388
+ $tmp_code = trim(CssMin::minify($_toMinify));
389
+ }
390
+ }
391
+ }
392
+ if (!empty($tmp_code)) {
393
+ $_Minified = $tmp_code;
394
+ unset($tmp_code);
395
+ }
396
+ // and then cache
397
+ $_cache->cache($_Minified,$codeMime);
398
+ $_CachedMinifiedUrl = AUTOPTIMIZE_CACHE_URL.$_cache->getname();
399
+ }
400
+ unset($_cache);
401
+
402
+ // if CDN, then CDN
403
+ $_CachedMinifiedUrl = $this->url_replace_cdn($_CachedMinifiedUrl);
404
+
405
+ return $_CachedMinifiedUrl;
406
+ }
407
+
408
+ protected function str_ends_in($haystack,$needle) {
409
+ $needleLength = strlen($needle);
410
+ $haystackLength = strlen($haystack);
411
+ $lastPos=strrpos($haystack,$needle);
412
+ if ($lastPos === $haystackLength - $needleLength) {
413
+ return true;
414
+ } else {
415
+ return false;
416
+ }
417
+ }
418
  }
classes/autoptimizeCache.php CHANGED
@@ -59,7 +59,7 @@ class autoptimizeCache {
59
  file_put_contents($this->cachedir.$this->filename,$code, LOCK_EX);
60
  if (apply_filters('autoptimize_filter_cache_create_static_gzip', false)) {
61
  // Create an additional cached gzip file
62
- file_put_contents($this->cachedir.$this->filename.'.gzip',gzencode($code,9,FORCE_GZIP), LOCK_EX);
63
  }
64
  }
65
  }
@@ -104,6 +104,13 @@ class autoptimizeCache {
104
  include_once(AUTOPTIMIZE_PLUGIN_DIR.'classlesses/autoptimizePageCacheFlush.php');
105
  add_action("autoptimize_action_cachepurged","autoptimize_flush_pagecache",10,0);
106
 
 
 
 
 
 
 
 
107
  return true;
108
  }
109
 
@@ -179,16 +186,15 @@ class autoptimizeCache {
179
  if (is_file($htaccess_tmpl)) {
180
  $htAccessContent=file_get_contents($htaccess_tmpl);
181
  } else if (is_multisite() || AUTOPTIMIZE_CACHE_NOGZIP == false) {
182
- $htAccessContent='<IfModule mod_headers.c>
183
- Header set Vary "Accept-Encoding"
184
- Header set Cache-Control "max-age=10672000, must-revalidate"
185
- </IfModule>
186
- <IfModule mod_expires.c>
187
  ExpiresActive On
188
  ExpiresByType text/css A30672000
189
  ExpiresByType text/javascript A30672000
190
  ExpiresByType application/javascript A30672000
191
  </IfModule>
 
 
 
192
  <IfModule mod_deflate.c>
193
  <FilesMatch "\.(js|css)$">
194
  SetOutputFilter DEFLATE
@@ -206,16 +212,15 @@ class autoptimizeCache {
206
  </Files>
207
  </IfModule>';
208
  } else {
209
- $htAccessContent='<IfModule mod_headers.c>
210
- Header set Vary "Accept-Encoding"
211
- Header set Cache-Control "max-age=10672000, must-revalidate"
212
- </IfModule>
213
- <IfModule mod_expires.c>
214
  ExpiresActive On
215
  ExpiresByType text/css A30672000
216
  ExpiresByType text/javascript A30672000
217
  ExpiresByType application/javascript A30672000
218
  </IfModule>
 
 
 
219
  <IfModule mod_deflate.c>
220
  <FilesMatch "\.(js|css)$">
221
  SetOutputFilter DEFLATE
59
  file_put_contents($this->cachedir.$this->filename,$code, LOCK_EX);
60
  if (apply_filters('autoptimize_filter_cache_create_static_gzip', false)) {
61
  // Create an additional cached gzip file
62
+ file_put_contents($this->cachedir.$this->filename.'.gz', gzencode($code,9,FORCE_GZIP), LOCK_EX);
63
  }
64
  }
65
  }
104
  include_once(AUTOPTIMIZE_PLUGIN_DIR.'classlesses/autoptimizePageCacheFlush.php');
105
  add_action("autoptimize_action_cachepurged","autoptimize_flush_pagecache",10,0);
106
 
107
+ // warm cache (part of speedupper)?
108
+ if ( apply_filters('autoptimize_filter_speedupper', true) ) {
109
+ $warmCacheUrl = site_url()."/?ao_speedup_cachebuster=".rand(1,100000);
110
+ $warmCache = @wp_remote_get($warmCacheUrl);
111
+ unset($warmCache);
112
+ }
113
+
114
  return true;
115
  }
116
 
186
  if (is_file($htaccess_tmpl)) {
187
  $htAccessContent=file_get_contents($htaccess_tmpl);
188
  } else if (is_multisite() || AUTOPTIMIZE_CACHE_NOGZIP == false) {
189
+ $htAccessContent='<IfModule mod_expires.c>
 
 
 
 
190
  ExpiresActive On
191
  ExpiresByType text/css A30672000
192
  ExpiresByType text/javascript A30672000
193
  ExpiresByType application/javascript A30672000
194
  </IfModule>
195
+ <IfModule mod_headers.c>
196
+ Header append Cache-Control "public, immutable"
197
+ </IfModule>
198
  <IfModule mod_deflate.c>
199
  <FilesMatch "\.(js|css)$">
200
  SetOutputFilter DEFLATE
212
  </Files>
213
  </IfModule>';
214
  } else {
215
+ $htAccessContent='<IfModule mod_expires.c>
 
 
 
 
216
  ExpiresActive On
217
  ExpiresByType text/css A30672000
218
  ExpiresByType text/javascript A30672000
219
  ExpiresByType application/javascript A30672000
220
  </IfModule>
221
+ <IfModule mod_headers.c>
222
+ Header append Cache-Control "public, immutable"
223
+ </IfModule>
224
  <IfModule mod_deflate.c>
225
  <FilesMatch "\.(js|css)$">
226
  SetOutputFilter DEFLATE
classes/autoptimizeConfig.php CHANGED
@@ -70,6 +70,14 @@ class autoptimizeConfig {
70
  content: "\f108 \f140";
71
  }
72
 
 
 
 
 
 
 
 
 
73
  /* form */
74
  .itemDetail {
75
  background: #fff;
@@ -151,7 +159,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
151
  <div class="wrap">
152
 
153
  <?php if (version_compare(PHP_VERSION, '5.3.0') < 0) { ?>
154
- <div class="notice-error notice"><?php _e('<p><strong>You are using a very old version of PHP</strong> (5.2.x or older) which has <a href="http://blog.futtta.be/2016/03/15/why-would-you-still-be-on-php-5-2/" target="_blank">serious security and performance issues</a>. Please ask your hoster to provide you with an upgrade path to 5.6 or 7.0</p>','autoptimize'); ?></div>
155
  <?php } ?>
156
 
157
  <div id="autoptimize_main">
@@ -210,11 +218,11 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
210
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
211
  <th scope="row"><?php _e('Force JavaScript in &lt;head&gt;?','autoptimize'); ?></th>
212
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_forcehead" <?php echo get_option('autoptimize_js_forcehead')?'checked="checked" ':''; ?>/>
213
- <?php _e('Load JavaScript early, reducing the chance of JS-errors but making it render blocking. You can disable this if you\'re not aggregating inline JS and you want JS to be deferred.','autoptimize'); ?></label></td>
214
  </tr>
215
  <?php if (get_option('autoptimize_js_justhead')) { ?>
216
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
217
- <th scope="row"><?php _e('Look for scripts only in &lt;head&gt;?','autoptimize'); _e(' <i>(deprecated)</i>','autoptimize'); ?></th>
218
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_justhead" <?php echo get_option('autoptimize_js_justhead')?'checked="checked" ':''; ?>/>
219
  <?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>
220
  </tr>
@@ -222,7 +230,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
222
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
223
  <th scope="row"><?php _e('Also aggregate inline JS?','autoptimize'); ?></th>
224
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_include_inline" <?php echo get_option('autoptimize_js_include_inline')?'checked="checked" ':''; ?>/>
225
- <?php _e('Check this option for Autoptimize to also aggregate JS in the HTML. If this option is not enabled, you might have to "force JavaScript in head".','autoptimize'); ?></label></td>
226
  </tr>
227
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
228
  <th scope="row"><?php _e('Exclude scripts from Autoptimize:','autoptimize'); ?></th>
@@ -256,7 +264,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
256
  </tr>
257
  <?php if (get_option('autoptimize_css_justhead')) { ?>
258
  <tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv">
259
- <th scope="row"><?php _e('Look for styles only in &lt;head&gt;?','autoptimize'); _e(' <i>(deprecated)</i>','autoptimize'); ?></th>
260
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_justhead" <?php echo get_option('autoptimize_css_justhead')?'checked="checked" ':''; ?>/>
261
  <?php _e('Don\'t autoptimize CSS outside the head-section. If the cache gets big, you might want to enable this.','autoptimize'); ?></label></td>
262
  </tr>
@@ -294,7 +302,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
294
  <tr valign="top">
295
  <th scope="row"><?php _e('CDN Base URL','autoptimize'); ?></th>
296
  <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 />
297
- <?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>).','autoptimize'); ?></label></td>
298
  </tr>
299
  </table>
300
  </li>
@@ -315,14 +323,39 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
315
  <td><?php
316
  $AOstatArr=autoptimizeCache::stats();
317
  $AOcacheSize=round($AOstatArr[1]/1024);
318
- echo $AOstatArr[0].__(' files, totalling ','autoptimize').$AOcacheSize.__(' Kbytes (calculated at ','autoptimize').date("H:i e", $AOstatArr[2]).')';
319
  ?></td>
320
  </tr>
321
- <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
322
- <th scope="row"><?php _e('Save aggregated script/css as static files?','autoptimize'); ?></th>
323
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_nogzip" <?php echo get_option('autoptimize_cache_nogzip','1')?'checked="checked" ':''; ?>/>
324
- <?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>
325
- </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  </table>
327
  </li>
328
 
@@ -355,7 +388,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
355
  echo $AO_banner;
356
  }
357
  ?>
358
- <li><?php _e("Need help? <a href='https://wordpress.org/plugins/autoptimize/faq/'>Check out the FAQ</a> or post your question on <a href='http://wordpress.org/support/plugin/autoptimize'>the support-forum</a>.","autoptimize"); ?></li>
359
  <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>
360
  </ul>
361
  </div>
@@ -380,7 +413,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
380
  </div>
381
  </div>
382
  </div>
383
- <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!"); ?>"></a></div>
384
  </div>
385
 
386
  <script type="text/javascript">
@@ -556,6 +589,8 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
556
  register_setting('autoptimize','autoptimize_cache_clean');
557
  register_setting('autoptimize','autoptimize_cache_nogzip');
558
  register_setting('autoptimize','autoptimize_show_adv');
 
 
559
  }
560
 
561
  public function setmeta($links,$file=null) {
@@ -603,7 +638,9 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
603
  'autoptimize_css_nogooglefont' => 0,
604
  'autoptimize_cdn_url' => "",
605
  'autoptimize_cache_nogzip' => 1,
606
- 'autoptimize_show_adv' => 0
 
 
607
  );
608
 
609
  //Override with user settings
70
  content: "\f108 \f140";
71
  }
72
 
73
+ /* animate "show adv" button */
74
+ #ao_show_adv { animation: watchmenow 3s linear 5s 10; }
75
+ #ao_show_adv:hover { animation: none; }
76
+ @keyframes watchmenow {
77
+ 0% { box-shadow: unset; }
78
+ 100% { box-shadow: 0px 0px 20px yellow; }
79
+ }
80
+
81
  /* form */
82
  .itemDetail {
83
  background: #fff;
159
  <div class="wrap">
160
 
161
  <?php if (version_compare(PHP_VERSION, '5.3.0') < 0) { ?>
162
+ <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>
163
  <?php } ?>
164
 
165
  <div id="autoptimize_main">
218
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
219
  <th scope="row"><?php _e('Force JavaScript in &lt;head&gt;?','autoptimize'); ?></th>
220
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_forcehead" <?php echo get_option('autoptimize_js_forcehead')?'checked="checked" ':''; ?>/>
221
+ <?php _e('Load JavaScript early, this can potentially fix some JS-errors, but makes the JS render blocking.','autoptimize'); ?></label></td>
222
  </tr>
223
  <?php if (get_option('autoptimize_js_justhead')) { ?>
224
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
225
+ <th scope="row"><?php _e('Look for scripts only in &lt;head&gt;?','autoptimize'); echo ' <i>'. __('(deprecated)','autoptimize') . '</i>'; ?></th>
226
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_justhead" <?php echo get_option('autoptimize_js_justhead')?'checked="checked" ':''; ?>/>
227
  <?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>
228
  </tr>
230
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
231
  <th scope="row"><?php _e('Also aggregate inline JS?','autoptimize'); ?></th>
232
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_include_inline" <?php echo get_option('autoptimize_js_include_inline')?'checked="checked" ':''; ?>/>
233
+ <?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>
234
  </tr>
235
  <tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
236
  <th scope="row"><?php _e('Exclude scripts from Autoptimize:','autoptimize'); ?></th>
264
  </tr>
265
  <?php if (get_option('autoptimize_css_justhead')) { ?>
266
  <tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv">
267
+ <th scope="row"><?php _e('Look for styles only in &lt;head&gt;?','autoptimize'); echo ' <i>'. __('(deprecated)','autoptimize') . '</i>'; ?></th>
268
  <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_justhead" <?php echo get_option('autoptimize_css_justhead')?'checked="checked" ':''; ?>/>
269
  <?php _e('Don\'t autoptimize CSS outside the head-section. If the cache gets big, you might want to enable this.','autoptimize'); ?></label></td>
270
  </tr>
302
  <tr valign="top">
303
  <th scope="row"><?php _e('CDN Base URL','autoptimize'); ?></th>
304
  <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 />
305
+ <?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>
306
  </tr>
307
  </table>
308
  </li>
323
  <td><?php
324
  $AOstatArr=autoptimizeCache::stats();
325
  $AOcacheSize=round($AOstatArr[1]/1024);
326
+ printf( __( '%1$s files, totalling %2$s Kbytes (calculated at %3$s)', 'autoptimize'), $AOstatArr[0], $AOcacheSize, date("H:i e", $AOstatArr[2]) );
327
  ?></td>
328
  </tr>
329
+ </table>
330
+ </li>
331
+
332
+ <li class="<?php echo $hiddenClass;?>itemDetail ao_adv">
333
+ <h2 class="itemTitle"><?php _e('Misc Options','autoptimize'); ?></h2>
334
+ <table class="form-table">
335
+ <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
336
+ <th scope="row"><?php _e('Save aggregated script/css as static files?','autoptimize'); ?></th>
337
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_nogzip" <?php echo get_option('autoptimize_cache_nogzip','1')?'checked="checked" ':''; ?>/>
338
+ <?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>
339
+ </td>
340
+ </tr>
341
+ <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
342
+ <th scope="row"><?php _e('Also optimize for logged in users?','autoptimize'); ?></th>
343
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_logged" <?php echo get_option('autoptimize_optimize_logged','1')?'checked="checked" ':''; ?>/>
344
+ <?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>
345
+ </td>
346
+ </tr>
347
+ <?php
348
+ if ( function_exists("is_checkout") || function_exists("is_cart") || function_exists("edd_is_checkout") || function_exists("wpsc_is_cart") || function_exists("wpsc_is_checkout") ) {
349
+ ?>
350
+ <tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
351
+ <th scope="row"><?php _e('Also optimize shop cart/ checkout?','autoptimize'); ?></th>
352
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_checkout" <?php echo get_option('autoptimize_optimize_checkout','1')?'checked="checked" ':''; ?>/>
353
+ <?php _e('By default Autoptimize is also active on your shop\'s cart/ checkout, uncheck not to optimize those.','autoptimize'); ?></label>
354
+ </td>
355
+ </tr>
356
+ <?php
357
+ }
358
+ ?>
359
  </table>
360
  </li>
361
 
388
  echo $AO_banner;
389
  }
390
  ?>
391
+ <li><?php _e("Need help? <a href='https://wordpress.org/plugins/autoptimize/faq/'>Check out the FAQ here</a>.","autoptimize"); ?></li>
392
  <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>
393
  </ul>
394
  </div>
413
  </div>
414
  </div>
415
  </div>
416
+ <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>
417
  </div>
418
 
419
  <script type="text/javascript">
589
  register_setting('autoptimize','autoptimize_cache_clean');
590
  register_setting('autoptimize','autoptimize_cache_nogzip');
591
  register_setting('autoptimize','autoptimize_show_adv');
592
+ register_setting('autoptimize','autoptimize_optimize_logged');
593
+ register_setting('autoptimize','autoptimize_optimize_checkout');
594
  }
595
 
596
  public function setmeta($links,$file=null) {
638
  'autoptimize_css_nogooglefont' => 0,
639
  'autoptimize_cdn_url' => "",
640
  'autoptimize_cache_nogzip' => 1,
641
+ 'autoptimize_show_adv' => 0,
642
+ 'autoptimize_optimize_logged' => 1,
643
+ 'autoptimize_optimize_checkout' => 1
644
  );
645
 
646
  //Override with user settings
classes/autoptimizeHTML.php CHANGED
@@ -66,6 +66,12 @@ class autoptimizeHTML extends autoptimizeBase {
66
  }
67
  }
68
 
 
 
 
 
 
 
69
  return true;
70
  }
71
 
66
  }
67
  }
68
 
69
+ // revslider data attribs somehow suffer from HTML optimization, this fixes that
70
+ if ( class_exists('RevSlider') || apply_filters('autoptimize_filter_html_dataattrib_cleanup', false) ) {
71
+ $this->content = preg_replace('#\n(data-.*$)\n#Um',' $1 ', $this->content);
72
+ $this->content = preg_replace('#(=\"[^"]*\")(\w)#','$1 $2', $this->content);
73
+ }
74
+
75
  return true;
76
  }
77
 
classes/autoptimizeScripts.php CHANGED
@@ -25,26 +25,26 @@ class autoptimizeScripts extends autoptimizeBase {
25
  if ($noptimizeJS) return false;
26
 
27
  // only optimize known good JS?
28
- $whitelistJS = apply_filters( 'autoptimize_filter_js_whitelist', "" );
29
  if (!empty($whitelistJS)) {
30
  $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistJS)));
31
  }
32
 
33
  // is there JS we should simply remove
34
- $removableJS = apply_filters( 'autoptimize_filter_js_removables', '');
35
  if (!empty($removableJS)) {
36
  $this->jsremovables = array_filter(array_map('trim',explode(",",$removableJS)));
37
  }
38
 
39
  // only header?
40
- if( apply_filters('autoptimize_filter_js_justhead',$options['justhead']) == true ) {
41
  $content = explode('</head>',$this->content,2);
42
  $this->content = $content[0].'</head>';
43
  $this->restofcontent = $content[1];
44
  }
45
 
46
  // include inline?
47
- if( apply_filters('autoptimize_js_include_inline',$options['include_inline']) == true ) {
48
  $this->include_inline = true;
49
  }
50
 
@@ -58,9 +58,19 @@ class autoptimizeScripts extends autoptimizeBase {
58
 
59
  // get extra exclusions settings or filter
60
  $excludeJS = $options['js_exclude'];
61
- $excludeJS = apply_filters( 'autoptimize_filter_js_exclude', $excludeJS );
62
  if ($excludeJS!=="") {
63
- $exclJSArr = array_filter(array_map('trim',explode(",",$excludeJS)));
 
 
 
 
 
 
 
 
 
 
64
  $this->dontmove = array_merge($exclJSArr,$this->dontmove);
65
  }
66
 
@@ -88,53 +98,71 @@ class autoptimizeScripts extends autoptimizeBase {
88
  // comments
89
  $this->content = $this->hide_comments($this->content);
90
 
91
- //Get script files
92
- if(preg_match_all('#<script.*</script>#Usmi',$this->content,$matches)) {
93
  foreach($matches[0] as $tag) {
94
- // only consider aggregation whitelisted in should_aggregate-function
95
  if( !$this->should_aggregate($tag) ) {
96
  $tag='';
97
  continue;
98
  }
99
- if(preg_match('#<script[^>]*src=("|\')([^>]*)("|\')#Usmi',$tag,$source)) {
 
 
100
  if ($this->isremovable($tag,$this->jsremovables)) {
101
  $this->content = str_replace($tag,'',$this->content);
102
  continue;
103
  }
104
-
105
- // External script
106
- $url = current(explode('?',$source[2],2));
107
  $path = $this->getpath($url);
108
- if($path !== false && preg_match('#\.js$#',$path)) {
109
- //Inline
110
- if($this->ismergeable($tag)) {
111
- //We can merge it
112
- $this->scripts[] = $path;
113
- } else {
114
- //No merge, but maybe we can move it
115
- if($this->ismovable($tag)) {
116
- //Yeah, move it
117
- if($this->movetolast($tag)) {
118
- $this->move['last'][] = $tag;
119
- } else {
120
- $this->move['first'][] = $tag;
121
  }
122
- } else {
123
- //We shouldn't touch this
124
- $tag = '';
125
  }
126
  }
127
- } else {
128
- //External script (example: google analytics)
129
- //OR Script is dynamic (.php etc)
130
- if($this->ismovable($tag)) {
131
- if($this->movetolast($tag)) {
132
- $this->move['last'][] = $tag;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  } else {
134
- $this->move['first'][] = $tag;
135
  }
136
  } else {
137
- //We shouldn't touch this
 
 
 
 
 
138
  $tag = '';
139
  }
140
  }
@@ -154,8 +182,9 @@ class autoptimizeScripts extends autoptimizeBase {
154
  $this->scripts[] = 'INLINE;'.$code;
155
  } else {
156
  // Can we move this?
157
- if($this->ismovable($tag)) {
158
- if($this->movetolast($tag)) {
 
159
  $this->move['last'][] = $tag;
160
  } else {
161
  $this->move['first'][] = $tag;
@@ -191,7 +220,7 @@ class autoptimizeScripts extends autoptimizeBase {
191
  if($this->trycatch) {
192
  $script = 'try{'.$script.'}catch(e){}';
193
  }
194
- $tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, "" );
195
  if ( has_filter('autoptimize_js_individual_script') && !empty($tmpscript) ) {
196
  $script=$tmpscript;
197
  $this->alreadyminified=true;
@@ -201,8 +230,10 @@ class autoptimizeScripts extends autoptimizeBase {
201
  //External script
202
  if($script !== false && file_exists($script) && is_readable($script)) {
203
  $scriptsrc = file_get_contents($script);
 
204
  $scriptsrc = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$scriptsrc);
205
  $scriptsrc = rtrim($scriptsrc,";\n\t\r").';';
 
206
  //Add try-catch?
207
  if($this->trycatch) {
208
  $scriptsrc = 'try{'.$scriptsrc.'}catch(e){}';
@@ -211,8 +242,8 @@ class autoptimizeScripts extends autoptimizeBase {
211
  if ( has_filter('autoptimize_js_individual_script') && !empty($tmpscriptsrc) ) {
212
  $scriptsrc=$tmpscriptsrc;
213
  $this->alreadyminified=true;
214
- } else if ((( strpos($script,"min.js") !== false ) || ( strpos($script,"wp-includes/js/jquery/jquery.js") !== false )) && ( $this->inject_min_late === true )) {
215
- $scriptsrc="%%INJECTLATER".AUTOPTIMIZE_HASH."%%".base64_encode($script)."|".md5($scriptsrc)."%%INJECTLATER%%";
216
  }
217
  $this->jscode .= "\n".$scriptsrc;
218
  }/*else{
@@ -248,9 +279,10 @@ class autoptimizeScripts extends autoptimizeBase {
248
  }
249
  } else {
250
  $this->jscode = $this->inject_minified($this->jscode);
251
- return false;
252
  }
253
  }
 
254
  return true;
255
  }
256
 
@@ -311,7 +343,9 @@ class autoptimizeScripts extends autoptimizeBase {
311
 
312
  // Checks against the white- and blacklists
313
  private function ismergeable($tag) {
314
- if (!empty($this->whitelist)) {
 
 
315
  foreach ($this->whitelist as $match) {
316
  if(strpos($tag,$match)!==false) {
317
  return true;
@@ -408,4 +442,28 @@ class autoptimizeScripts extends autoptimizeBase {
408
  return false;
409
  }
410
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  }
25
  if ($noptimizeJS) return false;
26
 
27
  // only optimize known good JS?
28
+ $whitelistJS = apply_filters( 'autoptimize_filter_js_whitelist', '', $this->content );
29
  if (!empty($whitelistJS)) {
30
  $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistJS)));
31
  }
32
 
33
  // is there JS we should simply remove
34
+ $removableJS = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
35
  if (!empty($removableJS)) {
36
  $this->jsremovables = array_filter(array_map('trim',explode(",",$removableJS)));
37
  }
38
 
39
  // only header?
40
+ if( apply_filters('autoptimize_filter_js_justhead', $options['justhead']) == true ) {
41
  $content = explode('</head>',$this->content,2);
42
  $this->content = $content[0].'</head>';
43
  $this->restofcontent = $content[1];
44
  }
45
 
46
  // include inline?
47
+ if( apply_filters('autoptimize_js_include_inline', $options['include_inline']) == true ) {
48
  $this->include_inline = true;
49
  }
50
 
58
 
59
  // get extra exclusions settings or filter
60
  $excludeJS = $options['js_exclude'];
61
+ $excludeJS = apply_filters( 'autoptimize_filter_js_exclude', $excludeJS, $this->content );
62
  if ($excludeJS!=="") {
63
+ if (is_array($excludeJS)) {
64
+ if(($removeKeys = array_keys($excludeJS,"remove")) !== false) {
65
+ foreach ($removeKeys as $removeKey) {
66
+ unset($excludeJS[$removeKey]);
67
+ $this->jsremovables[]=$removeKey;
68
+ }
69
+ }
70
+ $exclJSArr = array_keys($excludeJS);
71
+ } else {
72
+ $exclJSArr = array_filter(array_map('trim',explode(",",$excludeJS)));
73
+ }
74
  $this->dontmove = array_merge($exclJSArr,$this->dontmove);
75
  }
76
 
98
  // comments
99
  $this->content = $this->hide_comments($this->content);
100
 
101
+ // Get script files
102
+ if (preg_match_all('#<script.*</script>#Usmi',$this->content,$matches)) {
103
  foreach($matches[0] as $tag) {
104
+ // only consider script aggregation for types whitelisted in should_aggregate-function
105
  if( !$this->should_aggregate($tag) ) {
106
  $tag='';
107
  continue;
108
  }
109
+
110
+ if (preg_match('#<script[^>]*src=("|\')([^>]*)("|\')#Usmi',$tag,$source)) {
111
+ // non-inline script
112
  if ($this->isremovable($tag,$this->jsremovables)) {
113
  $this->content = str_replace($tag,'',$this->content);
114
  continue;
115
  }
116
+ $explUrl = explode('?',$source[2],2);
117
+ $url = $explUrl[0];
 
118
  $path = $this->getpath($url);
119
+ if($path !== false && preg_match('#\.js$#',$path) && $this->ismergeable($tag)) {
120
+ // ok to optimize, add to array
121
+ $this->scripts[] = $path;
122
+ } else {
123
+ $origTag = $tag;
124
+ $newTag = $tag;
125
+
126
+ // non-mergeable script (excluded or dynamic or external)
127
+ if (is_array($excludeJS)) {
128
+ // should we add flags?
129
+ foreach ($excludeJS as $exclTag => $exclFlags) {
130
+ if ( strpos($origTag,$exclTag)!==false && in_array($exclFlags,array("async","defer")) ) {
131
+ $newTag = str_replace('<script ','<script '.$exclFlags.' ',$newTag);
132
  }
 
 
 
133
  }
134
  }
135
+
136
+ // should we minify the non-aggregated script?
137
+ if ($path && apply_filters('autoptimize_filter_js_minify_excluded',false)) {
138
+ $_CachedMinifiedUrl = $this->minify_single($path);
139
+
140
+ // replace orig URL with minified URL from cache if so
141
+ if (!empty($_CachedMinifiedUrl)) {
142
+ $newTag = str_replace($url, $_CachedMinifiedUrl, $newTag);
143
+ }
144
+
145
+ // remove querystring from URL in newTag
146
+ if ( !empty($explUrl[1]) ) {
147
+ $newTag = str_replace("?".$explUrl[1],"",$newTag);
148
+ }
149
+ }
150
+
151
+ // should we move the non-aggregated script?
152
+ if( $this->ismovable($newTag) ) {
153
+ // can be moved, flags and all
154
+ if( $this->movetolast($newTag) ) {
155
+ $this->move['last'][] = $newTag;
156
  } else {
157
+ $this->move['first'][] = $newTag;
158
  }
159
  } else {
160
+ // cannot be moved, so if flag was added re-inject altered tag immediately
161
+ if ( $origTag !== $newTag ) {
162
+ $this->content = str_replace($origTag,$newTag,$this->content);
163
+ $origTag = '';
164
+ }
165
+ // and forget about the $tag (not to be touched any more)
166
  $tag = '';
167
  }
168
  }
182
  $this->scripts[] = 'INLINE;'.$code;
183
  } else {
184
  // Can we move this?
185
+ $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
186
+ if( $this->ismovable($tag) || $autoptimize_js_moveable !== '' ) {
187
+ if( $this->movetolast($tag) || $autoptimize_js_moveable === 'last' ) {
188
  $this->move['last'][] = $tag;
189
  } else {
190
  $this->move['first'][] = $tag;
220
  if($this->trycatch) {
221
  $script = 'try{'.$script.'}catch(e){}';
222
  }
223
+ $tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, '' );
224
  if ( has_filter('autoptimize_js_individual_script') && !empty($tmpscript) ) {
225
  $script=$tmpscript;
226
  $this->alreadyminified=true;
230
  //External script
231
  if($script !== false && file_exists($script) && is_readable($script)) {
232
  $scriptsrc = file_get_contents($script);
233
+ $scripthash = md5($scriptsrc);
234
  $scriptsrc = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$scriptsrc);
235
  $scriptsrc = rtrim($scriptsrc,";\n\t\r").';';
236
+
237
  //Add try-catch?
238
  if($this->trycatch) {
239
  $scriptsrc = 'try{'.$scriptsrc.'}catch(e){}';
242
  if ( has_filter('autoptimize_js_individual_script') && !empty($tmpscriptsrc) ) {
243
  $scriptsrc=$tmpscriptsrc;
244
  $this->alreadyminified=true;
245
+ } else if ($this->can_inject_late($script)) {
246
+ $scriptsrc="/*!%%INJECTLATER".AUTOPTIMIZE_HASH."%%".base64_encode($script)."|".$scripthash."%%INJECTLATER%%*/";
247
  }
248
  $this->jscode .= "\n".$scriptsrc;
249
  }/*else{
279
  }
280
  } else {
281
  $this->jscode = $this->inject_minified($this->jscode);
282
+ return false;
283
  }
284
  }
285
+ $this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode );
286
  return true;
287
  }
288
 
343
 
344
  // Checks against the white- and blacklists
345
  private function ismergeable($tag) {
346
+ if (apply_filters('autoptimize_filter_js_dontaggregate',false)) {
347
+ return false;
348
+ } else if (!empty($this->whitelist)) {
349
  foreach ($this->whitelist as $match) {
350
  if(strpos($tag,$match)!==false) {
351
  return true;
442
  return false;
443
  }
444
  }
445
+
446
+ /**
447
+ * Determines wheter a <script> $tag can be excluded from minification (as already minified) based on:
448
+ * - inject_min_late being active
449
+ * - filename ending in `min.js`
450
+ * - filename matching `js/jquery/jquery.js` (wordpress core jquery, is minified)
451
+ * - filename matching one passed in the consider minified filter
452
+ *
453
+ * @param string $jsPath
454
+ * @return bool
455
+ */
456
+ private function can_inject_late($jsPath) {
457
+ $consider_minified_array = apply_filters('autoptimize_filter_js_consider_minified',false);
458
+ if ( $this->inject_min_late !== true ) {
459
+ // late-inject turned off
460
+ return false;
461
+ } else if ( (strpos($jsPath,"min.js") === false) && ( strpos($jsPath,"wp-includes/js/jquery/jquery.js") === false ) && ( str_replace($consider_minified_array, '', $jsPath) === $jsPath ) ) {
462
+ // file not minified based on filename & filter
463
+ return false;
464
+ } else {
465
+ // phew, all is safe, we can late-inject
466
+ return true;
467
+ }
468
+ }
469
  }
classes/autoptimizeStyles.php CHANGED
@@ -2,11 +2,13 @@
2
  if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
 
4
  class autoptimizeStyles extends autoptimizeBase {
 
 
 
5
  private $css = array();
6
  private $csscode = array();
7
  private $url = array();
8
  private $restofcontent = '';
9
- private $mhtml = '';
10
  private $datauris = false;
11
  private $hashmap = array();
12
  private $alreadyminified = false;
@@ -24,7 +26,7 @@ class autoptimizeStyles extends autoptimizeBase {
24
  $noptimizeCSS = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content );
25
  if ($noptimizeCSS) return false;
26
 
27
- $whitelistCSS = apply_filters( 'autoptimize_filter_css_whitelist', '' );
28
  if (!empty($whitelistCSS)) {
29
  $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistCSS)));
30
  }
@@ -58,27 +60,30 @@ class autoptimizeStyles extends autoptimizeBase {
58
 
59
  // what CSS shouldn't be autoptimized
60
  $excludeCSS = $options['css_exclude'];
61
- $excludeCSS = apply_filters( 'autoptimize_filter_css_exclude', $excludeCSS );
62
  if ($excludeCSS!=="") {
63
  $this->dontmove = array_filter(array_map('trim',explode(",",$excludeCSS)));
64
  } else {
65
  $this->dontmove = "";
66
  }
 
 
 
67
 
68
  // should we defer css?
69
  // value: true/ false
70
  $this->defer = $options['defer'];
71
- $this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer );
72
 
73
  // should we inline while deferring?
74
  // value: inlined CSS
75
  $this->defer_inline = $options['defer_inline'];
76
- $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $this->defer_inline );
77
 
78
  // should we inline?
79
  // value: true/ false
80
  $this->inline = $options['inline'];
81
- $this->inline = apply_filters( 'autoptimize_filter_css_inline', $this->inline );
82
 
83
  // get cdn url
84
  $this->cdn_url = $options['cdn_url'];
@@ -130,7 +135,8 @@ class autoptimizeStyles extends autoptimizeBase {
130
 
131
  if(preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source)) {
132
  // <link>
133
- $url = current(explode('?',$source[2],2));
 
134
  $path = $this->getpath($url);
135
 
136
  if($path!==false && preg_match('#\.css$#',$path)) {
@@ -158,7 +164,33 @@ class autoptimizeStyles extends autoptimizeBase {
158
 
159
  // Remove the original style tag
160
  $this->content = str_replace($tag,'',$this->content);
161
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  }
163
  return true;
164
  }
@@ -183,14 +215,16 @@ class autoptimizeStyles extends autoptimizeBase {
183
  //<link>
184
  if($css !== false && file_exists($css) && is_readable($css)) {
185
  $cssPath = $css;
186
- $css = $this->fixurls($cssPath,file_get_contents($cssPath));
 
 
187
  $css = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$css);
188
  $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, $cssPath );
189
  if (has_filter('autoptimize_css_individual_style') && !empty($tmpstyle)) {
190
  $css=$tmpstyle;
191
  $this->alreadyminified=true;
192
  } else if ($this->can_inject_late($cssPath,$css)) {
193
- $css="%%INJECTLATER".AUTOPTIMIZE_HASH."%%".base64_encode($cssPath)."|".md5($css)."%%INJECTLATER%%";
194
  }
195
  } else {
196
  // Couldn't read CSS. Maybe getpath isn't working?
@@ -233,7 +267,8 @@ class autoptimizeStyles extends autoptimizeBase {
233
  $external_imports = "";
234
 
235
  // remove comments to avoid importing commented-out imports
236
- $thiscss_nocomments=preg_replace('#/\*.*\*/#Um','',$thiscss);
 
237
  while(preg_match_all('#@import.*(?:;|$)#Um',$thiscss_nocomments,$matches)) {
238
  foreach($matches[0] as $import) {
239
  if ($this->isremovable($import,$this->cssremovables)) {
@@ -251,7 +286,7 @@ class autoptimizeStyles extends autoptimizeBase {
251
  $code=$tmpstyle;
252
  $this->alreadyminified=true;
253
  } else if ($this->can_inject_late($path,$code)) {
254
- $code="%%INJECTLATER%%".base64_encode($path)."|".md5($code)."%%INJECTLATER%%";
255
  }
256
 
257
  if(!empty($code)) {
@@ -277,7 +312,7 @@ class autoptimizeStyles extends autoptimizeBase {
277
  $thiscss = preg_replace('#/\*FILESTART2\*/#','/*FILESTART*/',$thiscss);
278
 
279
  // and update $thiscss_nocomments before going into next iteration in while loop
280
- $thiscss_nocomments=preg_replace('#/\*.*\*/#Um','',$thiscss);
281
  }
282
  unset($thiscss_nocomments);
283
 
@@ -289,7 +324,6 @@ class autoptimizeStyles extends autoptimizeBase {
289
  unset($thiscss);
290
 
291
  // $this->csscode has all the uncompressed code now.
292
- $mhtmlcount = 0;
293
  foreach($this->csscode as &$code) {
294
  // Check for already-minified code
295
  $hash = md5($code);
@@ -303,10 +337,10 @@ class autoptimizeStyles extends autoptimizeBase {
303
 
304
  // Do the imaging!
305
  $imgreplace = array();
306
- preg_match_all('#(background[^;{}]*url\((?!\s?"?\'?\s?data)(.*)\)[^;}]*)(?:;|$|})#Usm',$code,$matches);
307
-
308
- if(($this->datauris == true) && (function_exists('base64_encode')) && (is_array($matches))) {
309
- foreach($matches[2] as $count => $quotedurl) {
310
  $iurl = trim($quotedurl," \t\n\r\0\x0B\"'");
311
 
312
  // if querystring, remove it from url
@@ -370,26 +404,18 @@ class autoptimizeStyles extends autoptimizeBase {
370
  unset($icheck);
371
 
372
  // Add it to the list for replacement
373
- $imgreplace[$matches[1][$count]] = str_replace($quotedurl,$headAndData,$matches[1][$count]).";\n*".str_replace($quotedurl,'mhtml:%%MHTML%%!'.$mhtmlcount,$matches[1][$count]).";\n_".$matches[1][$count].';';
374
-
375
- // Store image on the mhtml document
376
- $this->mhtml .= "--_\r\nContent-Location:{$mhtmlcount}\r\nContent-Transfer-Encoding:base64\r\n\r\n{$base64data}\r\n";
377
- $mhtmlcount++;
378
  } else {
379
  // just cdn the URL if applicable
380
  if (!empty($this->cdn_url)) {
381
- $url = trim($quotedurl," \t\n\r\0\x0B\"'");
382
- $cdn_url=$this->url_replace_cdn($url);
383
- $imgreplace[$matches[1][$count]] = str_replace($quotedurl,$cdn_url,$matches[1][$count]);
384
- }
385
  }
386
  }
387
  } else if ((is_array($matches)) && (!empty($this->cdn_url))) {
388
- // change background image urls to cdn-url
389
- foreach($matches[2] as $count => $quotedurl) {
390
- $url = trim($quotedurl," \t\n\r\0\x0B\"'");
391
- $cdn_url=$this->url_replace_cdn($url);
392
- $imgreplace[$matches[1][$count]] = str_replace($quotedurl,$cdn_url,$matches[1][$count]);
393
  }
394
  }
395
 
@@ -397,24 +423,6 @@ class autoptimizeStyles extends autoptimizeBase {
397
  $code = str_replace(array_keys($imgreplace),array_values($imgreplace),$code);
398
  }
399
 
400
- // CDN the fonts!
401
- if ( (!empty($this->cdn_url)) && (apply_filters('autoptimize_filter_css_fonts_cdn',false)) && (version_compare(PHP_VERSION, '5.3.0') >= 0) ) {
402
- $fontreplace = array();
403
- include_once(AUTOPTIMIZE_PLUGIN_DIR.'classlesses/autoptimizeFontRegex.php');
404
-
405
- preg_match_all($fonturl_regex,$code,$matches);
406
- if (is_array($matches)) {
407
- foreach($matches[8] as $count => $quotedurl) {
408
- $url = trim($quotedurl," \t\n\r\0\x0B\"'");
409
- $cdn_url=$this->url_replace_cdn($url);
410
- $fontreplace[$matches[8][$count]] = str_replace($quotedurl,$cdn_url,$matches[8][$count]);
411
- }
412
- if(!empty($fontreplace)) {
413
- $code = str_replace(array_keys($fontreplace),array_values($fontreplace),$code);
414
- }
415
- }
416
- }
417
-
418
  // Minify
419
  if (($this->alreadyminified!==true) && (apply_filters( "autoptimize_css_do_minify", true))) {
420
  if (class_exists('Minify_CSS_Compressor')) {
@@ -435,7 +443,7 @@ class autoptimizeStyles extends autoptimizeBase {
435
 
436
  $code = $this->inject_minified($code);
437
 
438
- $tmp_code = apply_filters( 'autoptimize_css_after_minify',$code );
439
  if (!empty($tmp_code)) {
440
  $code = $tmp_code;
441
  unset($tmp_code);
@@ -449,26 +457,9 @@ class autoptimizeStyles extends autoptimizeBase {
449
 
450
  //Caches the CSS in uncompressed, deflated and gzipped form.
451
  public function cache() {
452
- if($this->datauris) {
453
- // MHTML Preparation
454
- $this->mhtml = "/*\r\nContent-Type: multipart/related; boundary=\"_\"\r\n\r\n".$this->mhtml."*/\r\n";
455
- $md5 = md5($this->mhtml);
456
- $cache = new autoptimizeCache($md5,'txt');
457
- if(!$cache->check()) {
458
- // Cache our images for IE
459
- $cache->cache($this->mhtml,'text/plain');
460
- }
461
- $mhtml = AUTOPTIMIZE_CACHE_URL.$cache->getname();
462
- }
463
-
464
  // CSS cache
465
  foreach($this->csscode as $media => $code) {
466
  $md5 = $this->hashmap[md5($code)];
467
-
468
- if($this->datauris) {
469
- // Images for ie! Get the right url
470
- $code = str_replace('%%MHTML%%',$mhtml,$code);
471
- }
472
 
473
  $cache = new autoptimizeCache($md5,'css');
474
  if(!$cache->check()) {
@@ -510,7 +501,7 @@ class autoptimizeStyles extends autoptimizeBase {
510
 
511
  // Inject the new stylesheets
512
  $replaceTag = array("<title","before");
513
- $replaceTag = apply_filters( 'autoptimize_filter_css_replacetag', $replaceTag );
514
 
515
  if ($this->inline == true) {
516
  foreach($this->csscode as $media => $code) {
@@ -518,12 +509,12 @@ class autoptimizeStyles extends autoptimizeBase {
518
  }
519
  } else {
520
  if ($this->defer == true) {
521
- $deferredCssBlock = "<script data-cfasync='false'>function lCss(url,media) {var d=document;var l=d.createElement('link');l.rel='stylesheet';l.type='text/css';l.href=url;l.media=media;aoin=d.getElementById('aonoscrcss');aoin.parentNode.insertBefore(l,aoin.nextSibling);}function deferredCSS() {";
522
  $noScriptCssBlock = "<noscript id=\"aonoscrcss\">";
523
  $defer_inline_code=$this->defer_inline;
524
  if(!empty($defer_inline_code)){
525
- if ( apply_filters( 'autoptimize_filter_css_critcss_minify',true ) ) {
526
- $iCssHash=md5($defer_inline_code);
527
  $iCssCache = new autoptimizeCache($iCssHash,'css');
528
  if($iCssCache->check()) {
529
  // we have the optimized inline CSS in cache
@@ -552,7 +543,7 @@ class autoptimizeStyles extends autoptimizeBase {
552
 
553
  //Add the stylesheet either deferred (import at bottom) or normal links in head
554
  if($this->defer == true) {
555
- $deferredCssBlock .= "lCss('".$url."','".$media."');";
556
  $noScriptCssBlock .= '<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />';
557
  } else {
558
  if (strlen($this->csscode[$media]) > $this->cssinlinesize) {
@@ -564,10 +555,13 @@ class autoptimizeStyles extends autoptimizeBase {
564
  }
565
 
566
  if($this->defer == true) {
567
- $deferredCssBlock .= "}if(window.addEventListener){window.addEventListener('DOMContentLoaded',deferredCSS,false);}else{window.onload = deferredCSS;}</script>";
 
 
 
568
  $noScriptCssBlock .= "</noscript>";
569
- $this->inject_in_html($noScriptCssBlock,$replaceTag);
570
- $this->inject_in_html($deferredCssBlock,array('</body>','before'));
571
  }
572
  }
573
 
@@ -575,50 +569,74 @@ class autoptimizeStyles extends autoptimizeBase {
575
  return $this->content;
576
  }
577
 
578
- static function fixurls($file,$code) {
579
- $file = str_replace(WP_ROOT_DIR,'/',$file);
580
- $dir = dirname($file); //Like /wp-content
581
 
582
- // quick fix for import-troubles in e.g. arras theme
583
- $code=preg_replace('#@import ("|\')(.+?)\.css("|\')#','@import url("${2}.css")',$code);
 
 
 
 
 
 
 
584
 
585
- if(preg_match_all('#url\((?!data)(?!\#)(?!"\#)(.*)\)#Usi',$code,$matches)) {
586
  $replace = array();
587
- foreach($matches[1] as $k => $url) {
588
  // Remove quotes
589
- $url = trim($url," \t\n\r\0\x0B\"'");
590
- $noQurl = trim($url,"\"'");
591
- if ($url!==$noQurl) {
592
- $removedQuotes=true;
593
  } else {
594
- $removedQuotes=false;
 
 
 
 
595
  }
596
- $url=$noQurl;
597
- if(substr($url,0,1)=='/' || preg_match('#^(https?://|ftp://|data:)#i',$url)) {
598
- //URL is absolute
 
599
  continue;
600
  } else {
601
- // relative URL
602
- $newurl = preg_replace('/https?:/','',str_replace(" ","%20",AUTOPTIMIZE_WP_ROOT_URL.str_replace('//','/',$dir.'/'.$url)));
603
 
604
- $hash = md5($url);
605
- $code = str_replace($matches[0][$k],$hash,$code);
 
 
 
606
 
607
- if (!empty($removedQuotes)) {
608
- $replace[$hash] = 'url(\''.$newurl.'\')';
609
  } else {
610
- $replace[$hash] = 'url('.$newurl.')';
611
  }
612
  }
613
- }
614
- //Do the replacing here to avoid breaking URLs
615
- $code = str_replace(array_keys($replace),array_values($replace),$code);
616
- }
 
 
 
 
 
 
 
 
617
  return $code;
618
  }
619
 
620
  private function ismovable($tag) {
621
- if (!empty($this->whitelist)) {
 
 
622
  foreach ($this->whitelist as $match) {
623
  if(strpos($tag,$match)!==false) {
624
  return true;
@@ -642,10 +660,14 @@ class autoptimizeStyles extends autoptimizeBase {
642
  }
643
 
644
  private function can_inject_late($cssPath,$css) {
645
- if ((strpos($cssPath,"min.css")===false) || ($this->inject_min_late!==true)) {
646
- // late-inject turned off or file not minified based on filename
 
647
  return false;
648
- } else if (strpos($css,"@import")!==false) {
 
 
 
649
  // can't late-inject files with imports as those need to be aggregated
650
  return false;
651
  } else if ( (strpos($css,"@font-face")!==false ) && ( apply_filters("autoptimize_filter_css_fonts_cdn",false)===true) && (!empty($this->cdn_url)) ) {
@@ -659,4 +681,18 @@ class autoptimizeStyles extends autoptimizeBase {
659
  return true;
660
  }
661
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
  }
2
  if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
 
4
  class autoptimizeStyles extends autoptimizeBase {
5
+
6
+ const ASSETS_REGEX = '/url\s*\(\s*(?!["\']?data:)(?![\'|\"]?[\#|\%|])([^)]+)\s*\)([^;},]*)/i';
7
+
8
  private $css = array();
9
  private $csscode = array();
10
  private $url = array();
11
  private $restofcontent = '';
 
12
  private $datauris = false;
13
  private $hashmap = array();
14
  private $alreadyminified = false;
26
  $noptimizeCSS = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content );
27
  if ($noptimizeCSS) return false;
28
 
29
+ $whitelistCSS = apply_filters( 'autoptimize_filter_css_whitelist', '', $this->content );
30
  if (!empty($whitelistCSS)) {
31
  $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistCSS)));
32
  }
60
 
61
  // what CSS shouldn't be autoptimized
62
  $excludeCSS = $options['css_exclude'];
63
+ $excludeCSS = apply_filters( 'autoptimize_filter_css_exclude', $excludeCSS, $this->content );
64
  if ($excludeCSS!=="") {
65
  $this->dontmove = array_filter(array_map('trim',explode(",",$excludeCSS)));
66
  } else {
67
  $this->dontmove = "";
68
  }
69
+
70
+ // forcefully exclude CSS with data-noptimize attrib
71
+ $this->dontmove[]="data-noptimize";
72
 
73
  // should we defer css?
74
  // value: true/ false
75
  $this->defer = $options['defer'];
76
+ $this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer, $this->content );
77
 
78
  // should we inline while deferring?
79
  // value: inlined CSS
80
  $this->defer_inline = $options['defer_inline'];
81
+ $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $this->defer_inline, $this->content );
82
 
83
  // should we inline?
84
  // value: true/ false
85
  $this->inline = $options['inline'];
86
+ $this->inline = apply_filters( 'autoptimize_filter_css_inline', $this->inline, $this->content );
87
 
88
  // get cdn url
89
  $this->cdn_url = $options['cdn_url'];
135
 
136
  if(preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source)) {
137
  // <link>
138
+ $explUrl = explode('?',$source[2],2);
139
+ $url = $explUrl[0];
140
  $path = $this->getpath($url);
141
 
142
  if($path!==false && preg_match('#\.css$#',$path)) {
164
 
165
  // Remove the original style tag
166
  $this->content = str_replace($tag,'',$this->content);
167
+ } else {
168
+ // excluded CSS, minify if getpath
169
+ if (preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source)) {
170
+ $explUrl = explode('?',$source[2],2);
171
+ $url = $explUrl[0];
172
+ $path = $this->getpath($url);
173
+
174
+ if ($path && apply_filters('autoptimize_filter_css_minify_excluded',false)) {
175
+ $_CachedMinifiedUrl = $this->minify_single($path);
176
+
177
+ if (!empty($_CachedMinifiedUrl)) {
178
+ // replace orig URL with URL to cache
179
+ $newTag = str_replace($url, $_CachedMinifiedUrl, $tag);
180
+ } else {
181
+ $newTag = $tag;
182
+ }
183
+
184
+ // remove querystring from URL
185
+ if ( !empty($explUrl[1]) ) {
186
+ $newTag = str_replace("?".$explUrl[1],"",$newTag);
187
+ }
188
+
189
+ // and replace
190
+ $this->content = str_replace($tag,$newTag,$this->content);
191
+ }
192
+ }
193
+ }
194
  }
195
  return true;
196
  }
215
  //<link>
216
  if($css !== false && file_exists($css) && is_readable($css)) {
217
  $cssPath = $css;
218
+ $cssContents = file_get_contents($cssPath);
219
+ $cssHash = md5($cssContents);
220
+ $css = $this->fixurls($cssPath,$cssContents);
221
  $css = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$css);
222
  $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, $cssPath );
223
  if (has_filter('autoptimize_css_individual_style') && !empty($tmpstyle)) {
224
  $css=$tmpstyle;
225
  $this->alreadyminified=true;
226
  } else if ($this->can_inject_late($cssPath,$css)) {
227
+ $css="/*!%%INJECTLATER%%".base64_encode($cssPath)."|".$cssHash."%%INJECTLATER%%*/";
228
  }
229
  } else {
230
  // Couldn't read CSS. Maybe getpath isn't working?
267
  $external_imports = "";
268
 
269
  // remove comments to avoid importing commented-out imports
270
+ $thiscss_nocomments = preg_replace('#/\*.*\*/#Us','',$thiscss);
271
+
272
  while(preg_match_all('#@import.*(?:;|$)#Um',$thiscss_nocomments,$matches)) {
273
  foreach($matches[0] as $import) {
274
  if ($this->isremovable($import,$this->cssremovables)) {
286
  $code=$tmpstyle;
287
  $this->alreadyminified=true;
288
  } else if ($this->can_inject_late($path,$code)) {
289
+ $code="/*!%%INJECTLATER".AUTOPTIMIZE_HASH."%%".base64_encode($path)."|".md5($code)."%%INJECTLATER%%*/";
290
  }
291
 
292
  if(!empty($code)) {
312
  $thiscss = preg_replace('#/\*FILESTART2\*/#','/*FILESTART*/',$thiscss);
313
 
314
  // and update $thiscss_nocomments before going into next iteration in while loop
315
+ $thiscss_nocomments=preg_replace('#/\*.*\*/#Us','',$thiscss);
316
  }
317
  unset($thiscss_nocomments);
318
 
324
  unset($thiscss);
325
 
326
  // $this->csscode has all the uncompressed code now.
 
327
  foreach($this->csscode as &$code) {
328
  // Check for already-minified code
329
  $hash = md5($code);
337
 
338
  // Do the imaging!
339
  $imgreplace = array();
340
+ preg_match_all( self::ASSETS_REGEX, $code, $matches );
341
+
342
+ if ( ($this->datauris == true) && (function_exists('base64_encode')) && (is_array($matches)) ) {
343
+ foreach($matches[1] as $count => $quotedurl) {
344
  $iurl = trim($quotedurl," \t\n\r\0\x0B\"'");
345
 
346
  // if querystring, remove it from url
404
  unset($icheck);
405
 
406
  // Add it to the list for replacement
407
+ $imgreplace[$matches[0][$count]] = str_replace($quotedurl,$headAndData,$matches[0][$count]);
 
 
 
 
408
  } else {
409
  // just cdn the URL if applicable
410
  if (!empty($this->cdn_url)) {
411
+ $imgreplace[$matches[0][$count]] = str_replace($quotedurl,$this->maybe_cdn_urls($quotedurl),$matches[0][$count]);
412
+ }
 
 
413
  }
414
  }
415
  } else if ((is_array($matches)) && (!empty($this->cdn_url))) {
416
+ // change urls to cdn-url
417
+ foreach($matches[1] as $count => $quotedurl) {
418
+ $imgreplace[$matches[0][$count]] = str_replace($quotedurl,$this->maybe_cdn_urls($quotedurl),$matches[0][$count]);
 
 
419
  }
420
  }
421
 
423
  $code = str_replace(array_keys($imgreplace),array_values($imgreplace),$code);
424
  }
425
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
  // Minify
427
  if (($this->alreadyminified!==true) && (apply_filters( "autoptimize_css_do_minify", true))) {
428
  if (class_exists('Minify_CSS_Compressor')) {
443
 
444
  $code = $this->inject_minified($code);
445
 
446
+ $tmp_code = apply_filters( 'autoptimize_css_after_minify', $code );
447
  if (!empty($tmp_code)) {
448
  $code = $tmp_code;
449
  unset($tmp_code);
457
 
458
  //Caches the CSS in uncompressed, deflated and gzipped form.
459
  public function cache() {
 
 
 
 
 
 
 
 
 
 
 
 
460
  // CSS cache
461
  foreach($this->csscode as $media => $code) {
462
  $md5 = $this->hashmap[md5($code)];
 
 
 
 
 
463
 
464
  $cache = new autoptimizeCache($md5,'css');
465
  if(!$cache->check()) {
501
 
502
  // Inject the new stylesheets
503
  $replaceTag = array("<title","before");
504
+ $replaceTag = apply_filters( 'autoptimize_filter_css_replacetag', $replaceTag, $this->content );
505
 
506
  if ($this->inline == true) {
507
  foreach($this->csscode as $media => $code) {
509
  }
510
  } else {
511
  if ($this->defer == true) {
512
+ $preloadCssBlock = "";
513
  $noScriptCssBlock = "<noscript id=\"aonoscrcss\">";
514
  $defer_inline_code=$this->defer_inline;
515
  if(!empty($defer_inline_code)){
516
+ if ( apply_filters( 'autoptimize_filter_css_critcss_minify', true ) ) {
517
+ $iCssHash = md5($defer_inline_code);
518
  $iCssCache = new autoptimizeCache($iCssHash,'css');
519
  if($iCssCache->check()) {
520
  // we have the optimized inline CSS in cache
543
 
544
  //Add the stylesheet either deferred (import at bottom) or normal links in head
545
  if($this->defer == true) {
546
+ $preloadCssBlock .= '<link rel="preload" as="style" media="'.$media.'" href="'.$url.'" onload="this.rel=\'stylesheet\'" />';
547
  $noScriptCssBlock .= '<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />';
548
  } else {
549
  if (strlen($this->csscode[$media]) > $this->cssinlinesize) {
555
  }
556
 
557
  if($this->defer == true) {
558
+ $preloadPolyfill = '<script data-cfasync=\'false\'>/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
559
+ !function(a){"use strict";var b=function(b,c,d){function e(a){return h.body?a():void setTimeout(function(){e(a)})}function f(){i.addEventListener&&i.removeEventListener("load",f),i.media=d||"all"}var g,h=a.document,i=h.createElement("link");if(c)g=c;else{var j=(h.body||h.getElementsByTagName("head")[0]).childNodes;g=j[j.length-1]}var k=h.styleSheets;i.rel="stylesheet",i.href=b,i.media="only x",e(function(){g.parentNode.insertBefore(i,c?g:g.nextSibling)});var l=function(a){for(var b=i.href,c=k.length;c--;)if(k[c].href===b)return a();setTimeout(function(){l(a)})};return i.addEventListener&&i.addEventListener("load",f),i.onloadcssdefined=l,l(f),i};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b}("undefined"!=typeof global?global:this);
560
+ /*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
561
+ !function(a){if(a.loadCSS){var b=loadCSS.relpreload={};if(b.support=function(){try{return a.document.createElement("link").relList.supports("preload")}catch(b){return!1}},b.poly=function(){for(var b=a.document.getElementsByTagName("link"),c=0;c<b.length;c++){var d=b[c];"preload"===d.rel&&"style"===d.getAttribute("as")&&(a.loadCSS(d.href,d,d.getAttribute("media")),d.rel=null)}},!b.support()){b.poly();var c=a.setInterval(b.poly,300);a.addEventListener&&a.addEventListener("load",function(){b.poly(),a.clearInterval(c)}),a.attachEvent&&a.attachEvent("onload",function(){a.clearInterval(c)})}}}(this);</script>';
562
  $noScriptCssBlock .= "</noscript>";
563
+ $this->inject_in_html($preloadCssBlock.$noScriptCssBlock,$replaceTag);
564
+ $this->inject_in_html($preloadPolyfill,array('</body>','before'));
565
  }
566
  }
567
 
569
  return $this->content;
570
  }
571
 
572
+ static function fixurls($file, $code) {
573
+ // Switch all imports to the url() syntax
574
+ $code = preg_replace( '#@import ("|\')(.+?)\.css.*("|\')#', '@import url("${2}.css")', $code );
575
 
576
+ if ( preg_match_all( self::ASSETS_REGEX, $code, $matches ) ) {
577
+ $file = str_replace( WP_ROOT_DIR, '/', $file );
578
+ $dir = dirname( $file ); // Like /themes/expound/css
579
+
580
+ // $dir should not contain backslashes, since it's used to replace
581
+ // urls, but it can contain them when running on Windows because
582
+ // fixurls() is sometimes called with `ABSPATH . 'index.php'`
583
+ $dir = str_replace( '\\', '/', $dir );
584
+ unset( $file ); // not used below at all
585
 
 
586
  $replace = array();
587
+ foreach ( $matches[1] as $k => $url ) {
588
  // Remove quotes
589
+ $url = trim( $url," \t\n\r\0\x0B\"'" );
590
+ $noQurl = trim( $url, "\"'" );
591
+ if ( $url !== $noQurl ) {
592
+ $removedQuotes = true;
593
  } else {
594
+ $removedQuotes = false;
595
+ }
596
+
597
+ if ( '' === $noQurl ) {
598
+ continue;
599
  }
600
+
601
+ $url = $noQurl;
602
+ if ( '/' === $url{0} || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) {
603
+ // URL is protocol-relative, host-relative or something we don't touch
604
  continue;
605
  } else {
606
+ // Relative URL
607
+ $newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_ROOT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) );
608
 
609
+ // Hash the url + whatever was behind potentially for replacement
610
+ // We must do this, or different css classes referencing the same bg image (but
611
+ // different parts of it, say, in sprites and such) loose their stuff...
612
+ $hash = md5( $url . $matches[2][$k] );
613
+ $code = str_replace( $matches[0][$k], $hash, $code );
614
 
615
+ if ( $removedQuotes ) {
616
+ $replace[$hash] = "url('" . $newurl . "')" . $matches[2][$k];
617
  } else {
618
+ $replace[$hash] = 'url(' . $newurl . ')' . $matches[2][$k];
619
  }
620
  }
621
+ }
622
+
623
+ if ( ! empty( $replace ) ) {
624
+ // Sort the replacements array by key length in desc order (so that the longest strings are replaced first)
625
+ $keys = array_map( 'strlen', array_keys( $replace ) );
626
+ array_multisort( $keys, SORT_DESC, $replace );
627
+
628
+ // Replace URLs found within $code
629
+ $code = str_replace( array_keys( $replace ), array_values( $replace ), $code );
630
+ }
631
+ }
632
+
633
  return $code;
634
  }
635
 
636
  private function ismovable($tag) {
637
+ if ( apply_filters('autoptimize_filter_css_dontaggregate', false) ) {
638
+ return false;
639
+ } else if (!empty($this->whitelist)) {
640
  foreach ($this->whitelist as $match) {
641
  if(strpos($tag,$match)!==false) {
642
  return true;
660
  }
661
 
662
  private function can_inject_late($cssPath,$css) {
663
+ $consider_minified_array = apply_filters('autoptimize_filter_css_consider_minified', false, $cssPath);
664
+ if ( $this->inject_min_late !== true ) {
665
+ // late-inject turned off
666
  return false;
667
+ } else if ( (strpos($cssPath,"min.css") === false) && ( str_replace($consider_minified_array, '', $cssPath) === $cssPath ) ) {
668
+ // file not minified based on filename & filter
669
+ return false;
670
+ } else if ( strpos($css,"@import") !== false ) {
671
  // can't late-inject files with imports as those need to be aggregated
672
  return false;
673
  } else if ( (strpos($css,"@font-face")!==false ) && ( apply_filters("autoptimize_filter_css_fonts_cdn",false)===true) && (!empty($this->cdn_url)) ) {
681
  return true;
682
  }
683
  }
684
+
685
+ private function maybe_cdn_urls($inUrl) {
686
+ $url = trim($inUrl," \t\n\r\0\x0B\"'");
687
+ $urlPath = parse_url($url,PHP_URL_PATH);
688
+
689
+ // exclude fonts from CDN except if filter returns true
690
+ if ( !preg_match('#\.(woff2?|eot|ttf|otf)$#i',$urlPath) || apply_filters('autoptimize_filter_css_fonts_cdn',false) ) {
691
+ $cdn_url = $this->url_replace_cdn($url);
692
+ } else {
693
+ $cdn_url = $url;
694
+ }
695
+
696
+ return $cdn_url;
697
+ }
698
  }
classes/autoptimizeToolbar.php CHANGED
@@ -1,36 +1,36 @@
1
  <?php
2
  if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
-
4
- class autoptimizeToolbar {
5
-
6
  public function __construct()
7
  {
8
  // If Cache is not available we don't add the Autoptimize Toolbar
9
- if( !autoptimizeCache::cacheavail() ) return;
10
-
 
11
  // Load admin toolbar feature once WordPress, all plugins, and the theme are fully loaded and instantiated.
12
  add_action( 'wp_loaded', array( $this, 'load_toolbar' ) );
13
  }
14
 
15
  public function load_toolbar()
16
  {
17
- // We check that the current user has the appropriate permissions
18
- if( current_user_can( 'manage_options' ) && apply_filters( 'autoptimize_filter_toolbar_show', true ) )
19
- {
20
- // Load custom styles and scripts
21
- if( is_admin() ) {
22
- // in the case of back-end
23
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
24
- } else {
25
- // in the case of front-end
26
- add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
27
- }
28
-
29
  // Create a handler for the AJAX toolbar requests
30
  add_action( 'wp_ajax_autoptimize_delete_cache', array( $this, 'delete_cache' ) );
31
 
32
- // Add the Autoptimize Toolbar to the Admin bar
33
- add_action( 'admin_bar_menu', array($this, 'add_toolbar'), 100 );
 
 
 
 
 
 
 
 
 
 
34
  }
35
  }
36
 
@@ -42,7 +42,7 @@ class autoptimizeToolbar {
42
  $stats = autoptimizeCache::stats();
43
 
44
  // Set the Max Size recommended for cache files
45
- $max_size = apply_filters('autoptimize_filter_cachecheck_maxsize', 512 * 1024 * 1024);
46
 
47
  // Retrieve the current Total Files in cache
48
  $files = $stats[0];
@@ -53,10 +53,11 @@ class autoptimizeToolbar {
53
 
54
  // We calculated the percentage of cache used
55
  $percentage = ceil( $bytes / $max_size * 100 );
56
- if( $percentage > 100 ) $percentage = 100;
57
-
 
58
  // We define the type of color indicator for the current state of cache size.
59
- // "green" if the size is less than 80% of the total recommended
60
  // "orange" if over 80%
61
  // "red" if over 100%
62
  $color = ( $percentage == 100 ) ? 'red' : ( ( $percentage > 80 ) ? 'orange' : 'green' );
@@ -65,15 +66,15 @@ class autoptimizeToolbar {
65
  // Main Autoptimize node
66
  $wp_admin_bar->add_node( array(
67
  'id' => 'autoptimize',
68
- 'title' => '<span class="ab-icon"></span><span class="ab-label">' . __("Autoptimize",'autoptimize') . '</span>',
69
  'href' => admin_url( 'options-general.php?page=autoptimize' ),
70
  'meta' => array( 'class' => 'bullet-' . $color )
71
  ));
72
 
73
  // Cache Info node
74
  $wp_admin_bar->add_node( array(
75
- 'id' => 'autoptimize-cache-info',
76
- 'title' => '<p>' . __( "Cache Info", 'autoptimize' ) . '</p>' .
77
  '<div class="autoptimize-radial-bar" percentage="' . $percentage . '">' .
78
  '<div class="circle">'.
79
  '<div class="mask full"><div class="fill bg-' . $color . '"></div></div>'.
@@ -86,42 +87,39 @@ class autoptimizeToolbar {
86
  '<tr><td>' . __( "Size", 'autoptimize' ) . ':</td><td class="size ' . $color . '">' . $size . '</td></tr>' .
87
  '<tr><td>' . __( "Files", 'autoptimize' ) . ':</td><td class="files white">' . $files . '</td></tr>' .
88
  '</table>',
89
- 'parent'=> 'autoptimize'
90
  ));
91
-
92
  // Delete Cache node
93
  $wp_admin_bar->add_node( array(
94
- 'id' => 'autoptimize-delete-cache',
95
- 'title' => __("Delete Cache",'autoptimize'),
96
- 'parent'=> 'autoptimize'
97
  ));
98
  }
99
 
100
  public function delete_cache()
101
  {
102
  check_ajax_referer( 'ao_delcache_nonce', 'nonce' );
103
- if( current_user_can( 'manage_options' ))
 
104
  {
105
  // We call the function for cleaning the Autoptimize cache
106
- autoptimizeCache::clearall();
107
  }
108
-
109
- wp_die();
110
- // NOTE: Remember that any return values of this function must be in JSON format
111
  }
112
 
113
  public function enqueue_scripts()
114
  {
115
  // Autoptimize Toolbar Styles
116
- wp_enqueue_style( 'autoptimize-toolbar', plugins_url('/static/toolbar.css', __FILE__ ), array(), time(), "all" );
117
-
118
  // Autoptimize Toolbar Javascript
119
- wp_enqueue_script( 'autoptimize-toolbar', plugins_url( '/static/toolbar.js', __FILE__ ), array('jquery'), time(), true );
120
-
121
  // Localizes a registered script with data for a JavaScript variable. (We need this for the AJAX work properly in the front-end mode)
122
  wp_localize_script( 'autoptimize-toolbar', 'autoptimize_ajax_object', array(
123
  'ajaxurl' => admin_url( 'admin-ajax.php' ),
124
- 'error_msg' => __( 'Your Autoptimize cache might not have been purged successfully, please check on the <a href=' . admin_url( 'options-general.php?page=autoptimize' ) . ' style="white-space:nowrap;">Autoptimize settings page</a>.', 'autoptimize' ),
125
  'dismiss_msg' => __( 'Dismiss this notice.' ),
126
  'nonce' => wp_create_nonce( 'ao_delcache_nonce' )
127
  ) );
1
  <?php
2
  if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
+ class autoptimizeToolbar
4
+ {
 
5
  public function __construct()
6
  {
7
  // If Cache is not available we don't add the Autoptimize Toolbar
8
+ if( ! autoptimizeCache::cacheavail() ) {
9
+ return;
10
+ }
11
  // Load admin toolbar feature once WordPress, all plugins, and the theme are fully loaded and instantiated.
12
  add_action( 'wp_loaded', array( $this, 'load_toolbar' ) );
13
  }
14
 
15
  public function load_toolbar()
16
  {
17
+ // Check permissions and that toolbar is not hidden via filter
18
+ if ( current_user_can( 'manage_options' ) && apply_filters( 'autoptimize_filter_toolbar_show', true ) ) {
 
 
 
 
 
 
 
 
 
 
19
  // Create a handler for the AJAX toolbar requests
20
  add_action( 'wp_ajax_autoptimize_delete_cache', array( $this, 'delete_cache' ) );
21
 
22
+ // Load custom styles, scripts and menu only when needed
23
+ if ( is_admin_bar_showing() ) {
24
+ if ( is_admin() ) {
25
+ // in the case of back-end
26
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
27
+ } else {
28
+ // in the case of front-end
29
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
30
+ }
31
+ // Add the Autoptimize Toolbar to the Admin bar
32
+ add_action( 'admin_bar_menu', array( $this, 'add_toolbar' ), 100 );
33
+ }
34
  }
35
  }
36
 
42
  $stats = autoptimizeCache::stats();
43
 
44
  // Set the Max Size recommended for cache files
45
+ $max_size = apply_filters( 'autoptimize_filter_cachecheck_maxsize', 512 * 1024 * 1024 );
46
 
47
  // Retrieve the current Total Files in cache
48
  $files = $stats[0];
53
 
54
  // We calculated the percentage of cache used
55
  $percentage = ceil( $bytes / $max_size * 100 );
56
+ if ( $percentage > 100 ) {
57
+ $percentage = 100;
58
+ }
59
  // We define the type of color indicator for the current state of cache size.
60
+ // "green" if the size is less than 80% of the total recommended
61
  // "orange" if over 80%
62
  // "red" if over 100%
63
  $color = ( $percentage == 100 ) ? 'red' : ( ( $percentage > 80 ) ? 'orange' : 'green' );
66
  // Main Autoptimize node
67
  $wp_admin_bar->add_node( array(
68
  'id' => 'autoptimize',
69
+ 'title' => '<span class="ab-icon"></span><span class="ab-label">' . __( "Autoptimize", 'autoptimize' ) . '</span>',
70
  'href' => admin_url( 'options-general.php?page=autoptimize' ),
71
  'meta' => array( 'class' => 'bullet-' . $color )
72
  ));
73
 
74
  // Cache Info node
75
  $wp_admin_bar->add_node( array(
76
+ 'id' => 'autoptimize-cache-info',
77
+ 'title' => '<p>' . __( "Cache Info", 'autoptimize' ) . '</p>' .
78
  '<div class="autoptimize-radial-bar" percentage="' . $percentage . '">' .
79
  '<div class="circle">'.
80
  '<div class="mask full"><div class="fill bg-' . $color . '"></div></div>'.
87
  '<tr><td>' . __( "Size", 'autoptimize' ) . ':</td><td class="size ' . $color . '">' . $size . '</td></tr>' .
88
  '<tr><td>' . __( "Files", 'autoptimize' ) . ':</td><td class="files white">' . $files . '</td></tr>' .
89
  '</table>',
90
+ 'parent' => 'autoptimize'
91
  ));
92
+
93
  // Delete Cache node
94
  $wp_admin_bar->add_node( array(
95
+ 'id' => 'autoptimize-delete-cache',
96
+ 'title' => __( "Delete Cache", 'autoptimize' ),
97
+ 'parent' => 'autoptimize'
98
  ));
99
  }
100
 
101
  public function delete_cache()
102
  {
103
  check_ajax_referer( 'ao_delcache_nonce', 'nonce' );
104
+ $result = false;
105
+ if ( current_user_can( 'manage_options' ) )
106
  {
107
  // We call the function for cleaning the Autoptimize cache
108
+ $result = autoptimizeCache::clearall();
109
  }
110
+ wp_send_json( $result );
 
 
111
  }
112
 
113
  public function enqueue_scripts()
114
  {
115
  // Autoptimize Toolbar Styles
116
+ wp_enqueue_style( 'autoptimize-toolbar', plugins_url( '/static/toolbar.css', __FILE__ ), array(), time(), 'all' );
 
117
  // Autoptimize Toolbar Javascript
118
+ wp_enqueue_script( 'autoptimize-toolbar', plugins_url( '/static/toolbar.js', __FILE__ ), array( 'jquery' ), time(), true );
 
119
  // Localizes a registered script with data for a JavaScript variable. (We need this for the AJAX work properly in the front-end mode)
120
  wp_localize_script( 'autoptimize-toolbar', 'autoptimize_ajax_object', array(
121
  'ajaxurl' => admin_url( 'admin-ajax.php' ),
122
+ '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;"' ),
123
  'dismiss_msg' => __( 'Dismiss this notice.' ),
124
  'nonce' => wp_create_nonce( 'ao_delcache_nonce' )
125
  ) );
classes/external/php/minify-html.php CHANGED
@@ -257,7 +257,7 @@ class Minify_HTML {
257
  protected function _removeCdata($str)
258
  {
259
  return (false !== strpos($str, '<![CDATA['))
260
- ? str_replace(array('/*<![CDATA[*/','/*]]>*/','<![CDATA[', ']]>'), '', $str)
261
  : $str;
262
  }
263
 
257
  protected function _removeCdata($str)
258
  {
259
  return (false !== strpos($str, '<![CDATA['))
260
+ ? str_replace(array('/* <![CDATA[ */','/* ]]> */','/*<![CDATA[*/','/*]]>*/','<![CDATA[', ']]>'), '', $str)
261
  : $str;
262
  }
263
 
classes/external/php/yui-php-cssmin-2.4.8-4.php DELETED
@@ -1,777 +0,0 @@
1
- <?php
2
-
3
- /*!
4
- * cssmin.php v2.4.8-4
5
- * Author: Tubal Martin - http://tubalmartin.me/
6
- * Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
7
- *
8
- * This is a PHP port of the CSS minification tool distributed with YUICompressor,
9
- * itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
10
- * Permission is hereby granted to use the PHP version under the same
11
- * conditions as the YUICompressor.
12
- */
13
-
14
- /*!
15
- * YUI Compressor
16
- * http://developer.yahoo.com/yui/compressor/
17
- * Author: Julien Lecomte - http://www.julienlecomte.net/
18
- * Copyright (c) 2013 Yahoo! Inc. All rights reserved.
19
- * The copyrights embodied in the content of this file are licensed
20
- * by Yahoo! Inc. under the BSD (revised) open source license.
21
- */
22
-
23
- class CSSmin
24
- {
25
- const NL = '___YUICSSMIN_PRESERVED_NL___';
26
- const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
27
- const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
28
- const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
29
- const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
30
-
31
- private $comments;
32
- private $preserved_tokens;
33
- private $memory_limit;
34
- private $max_execution_time;
35
- private $pcre_backtrack_limit;
36
- private $pcre_recursion_limit;
37
- private $raise_php_limits;
38
-
39
- /**
40
- * @param bool|int $raise_php_limits
41
- * If true, PHP settings will be raised if needed
42
- */
43
- public function __construct($raise_php_limits = TRUE)
44
- {
45
- // Set suggested PHP limits
46
- $this->memory_limit = 128 * 1048576; // 128MB in bytes
47
- $this->max_execution_time = 60; // 1 min
48
- $this->pcre_backtrack_limit = 1000 * 1000;
49
- $this->pcre_recursion_limit = 500 * 1000;
50
-
51
- $this->raise_php_limits = (bool) $raise_php_limits;
52
- }
53
-
54
- /**
55
- * Minify a string of CSS
56
- * @param string $css
57
- * @param int|bool $linebreak_pos
58
- * @return string
59
- */
60
- public function run($css = '', $linebreak_pos = FALSE)
61
- {
62
- if (empty($css)) {
63
- return '';
64
- }
65
-
66
- if ($this->raise_php_limits) {
67
- $this->do_raise_php_limits();
68
- }
69
-
70
- $this->comments = array();
71
- $this->preserved_tokens = array();
72
-
73
- $start_index = 0;
74
- $length = strlen($css);
75
-
76
- $css = $this->extract_data_urls($css);
77
-
78
- // collect all comment blocks...
79
- while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
80
- $end_index = $this->index_of($css, '*/', $start_index + 2);
81
- if ($end_index < 0) {
82
- $end_index = $length;
83
- }
84
- $comment_found = $this->str_slice($css, $start_index + 2, $end_index);
85
- $this->comments[] = $comment_found;
86
- $comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
87
- $css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
88
- // Set correct start_index: Fixes issue #2528130
89
- $start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
90
- }
91
-
92
- // preserve strings so their content doesn't get accidentally minified
93
- $css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
94
-
95
- // Let's divide css code in chunks of 5.000 chars aprox.
96
- // Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
97
- // of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
98
- // long strings and a (sub)pattern matches a number of chars greater than
99
- // the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
100
- // returning NULL and $css would be empty.
101
- $charset = '';
102
- $charset_regexp = '/(@charset)( [^;]+;)/i';
103
- $css_chunks = array();
104
- $css_chunk_length = 5000; // aprox size, not exact
105
- $start_index = 0;
106
- $i = $css_chunk_length; // save initial iterations
107
- $l = strlen($css);
108
-
109
-
110
- // if the number of characters is 5000 or less, do not chunk
111
- if ($l <= $css_chunk_length) {
112
- $css_chunks[] = $css;
113
- } else {
114
- // chunk css code securely
115
- while ($i < $l) {
116
- $i += 50; // save iterations
117
- if ($l - $start_index <= $css_chunk_length || $i >= $l) {
118
- $css_chunks[] = $this->str_slice($css, $start_index);
119
- break;
120
- }
121
- if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
122
- // If there are two ending curly braces }} separated or not by spaces,
123
- // join them in the same chunk (i.e. @media blocks)
124
- $next_chunk = substr($css, $i);
125
- if (preg_match('/^\s*\}/', $next_chunk)) {
126
- $i = $i + $this->index_of($next_chunk, '}') + 1;
127
- }
128
-
129
- $css_chunks[] = $this->str_slice($css, $start_index, $i);
130
- $start_index = $i;
131
- }
132
- }
133
- }
134
-
135
- // Minify each chunk
136
- for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
137
- $css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
138
- // Keep the first @charset at-rule found
139
- if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
140
- $charset = strtolower($matches[1]) . $matches[2];
141
- }
142
- // Delete all @charset at-rules
143
- $css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
144
- }
145
-
146
- // Update the first chunk and push the charset to the top of the file.
147
- $css_chunks[0] = $charset . $css_chunks[0];
148
-
149
- return implode('', $css_chunks);
150
- }
151
-
152
- /**
153
- * Sets the memory limit for this script
154
- * @param int|string $limit
155
- */
156
- public function set_memory_limit($limit)
157
- {
158
- $this->memory_limit = $this->normalize_int($limit);
159
- }
160
-
161
- /**
162
- * Sets the maximum execution time for this script
163
- * @param int|string $seconds
164
- */
165
- public function set_max_execution_time($seconds)
166
- {
167
- $this->max_execution_time = (int) $seconds;
168
- }
169
-
170
- /**
171
- * Sets the PCRE backtrack limit for this script
172
- * @param int $limit
173
- */
174
- public function set_pcre_backtrack_limit($limit)
175
- {
176
- $this->pcre_backtrack_limit = (int) $limit;
177
- }
178
-
179
- /**
180
- * Sets the PCRE recursion limit for this script
181
- * @param int $limit
182
- */
183
- public function set_pcre_recursion_limit($limit)
184
- {
185
- $this->pcre_recursion_limit = (int) $limit;
186
- }
187
-
188
- /**
189
- * Try to configure PHP to use at least the suggested minimum settings
190
- */
191
- private function do_raise_php_limits()
192
- {
193
- $php_limits = array(
194
- 'memory_limit' => $this->memory_limit,
195
- 'max_execution_time' => $this->max_execution_time,
196
- 'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
197
- 'pcre.recursion_limit' => $this->pcre_recursion_limit
198
- );
199
-
200
- // If current settings are higher respect them.
201
- foreach ($php_limits as $name => $suggested) {
202
- $current = $this->normalize_int(ini_get($name));
203
- // memory_limit exception: allow -1 for "no memory limit".
204
- if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
205
- ini_set($name, $suggested);
206
- }
207
- }
208
- }
209
-
210
- /**
211
- * Does bulk of the minification
212
- * @param string $css
213
- * @param int|bool $linebreak_pos
214
- * @return string
215
- */
216
- private function minify($css, $linebreak_pos)
217
- {
218
- // strings are safe, now wrestle the comments
219
- for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
220
-
221
- $token = $this->comments[$i];
222
- $placeholder = '/' . self::COMMENT . $i . '___/';
223
-
224
- // ! in the first position of the comment means preserve
225
- // so push to the preserved tokens keeping the !
226
- if (substr($token, 0, 1) === '!') {
227
- $this->preserved_tokens[] = $token;
228
- $token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
229
- $css = preg_replace($placeholder, $token_tring, $css, 1);
230
- // Preserve new lines for /*! important comments
231
- $css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
232
- $css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
233
- continue;
234
- }
235
-
236
- // \ in the last position looks like hack for Mac/IE5
237
- // shorten that to /*\*/ and the next one to /**/
238
- if (substr($token, (strlen($token) - 1), 1) === '\\') {
239
- $this->preserved_tokens[] = '\\';
240
- $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
241
- $i = $i + 1; // attn: advancing the loop
242
- $this->preserved_tokens[] = '';
243
- $css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
244
- continue;
245
- }
246
-
247
- // keep empty comments after child selectors (IE7 hack)
248
- // e.g. html >/**/ body
249
- if (strlen($token) === 0) {
250
- $start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
251
- if ($start_index > 2) {
252
- if (substr($css, $start_index - 3, 1) === '>') {
253
- $this->preserved_tokens[] = '';
254
- $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
255
- }
256
- }
257
- }
258
-
259
- // in all other cases kill the comment
260
- $css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
261
- }
262
-
263
-
264
- // Normalize all whitespace strings to single spaces. Easier to work with that way.
265
- $css = preg_replace('/\s+/', ' ', $css);
266
-
267
- // Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
268
- $css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);
269
-
270
- // Shorten & preserve calculations calc(...) since spaces are important
271
- $css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
272
-
273
- // Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
274
- // +1.2em to 1.2em, +.8px to .8px, +2% to 2%
275
- $css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
276
-
277
- // Remove leading zeros from integer and float numbers preceded by : or a white-space
278
- // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
279
- $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
280
-
281
- // Remove trailing zeros from float numbers preceded by : or a white-space
282
- // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
283
- $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
284
-
285
- // Remove trailing .0 -> -9.0 to -9
286
- $css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
287
-
288
- // Replace 0 length numbers with 0
289
- $css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
290
-
291
- // Remove the spaces before the things that should not have spaces before them.
292
- // But, be careful not to turn "p :link {...}" into "p:link{...}"
293
- // Swap out any pseudo-class colons with the token, and then swap back.
294
- $css = preg_replace_callback('/(?:^|\})[^\{]*\s+\:/', array($this, 'replace_colon'), $css);
295
-
296
- // Remove spaces before the things that should not have spaces before them.
297
- $css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
298
-
299
- // Restore spaces for !important
300
- $css = preg_replace('/\!important/i', ' !important', $css);
301
-
302
- // bring back the colon
303
- $css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
304
-
305
- // retain space for special IE6 cases
306
- $css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
307
-
308
- // no space after the end of a preserved comment
309
- $css = preg_replace('/\*\/ /', '*/', $css);
310
-
311
- // lowercase some popular @directives
312
- $css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
313
-
314
- // lowercase some more common pseudo-elements
315
- $css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css);
316
-
317
- // lowercase some more common functions
318
- $css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css);
319
-
320
- // lower case some common function that can be values
321
- // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
322
- $css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);
323
-
324
- // Put the space back in some cases, to support stuff like
325
- // @media screen and (-webkit-min-device-pixel-ratio:0){
326
- $css = preg_replace('/\band\(/i', 'and (', $css);
327
-
328
- // Remove the spaces after the things that should not have spaces after them.
329
- $css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
330
-
331
- // remove unnecessary semicolons
332
- $css = preg_replace('/;+\}/', '}', $css);
333
-
334
- // Fix for issue: #2528146
335
- // Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
336
- // to avoid issues on Symbian S60 3.x browsers.
337
- $css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
338
-
339
- // Replace 0 <length> and 0 <percentage> values with 0.
340
- // <length> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/length
341
- // <percentage> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
342
- $css = preg_replace('/([^\\\\]\:|\s)0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%)/iS', '${1}0', $css);
343
-
344
- // 0% step in a keyframe? restore the % unit
345
- $css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]+\{)(.*?)(\}\})/iS', array($this, 'replace_keyframe_zero'), $css);
346
-
347
- // Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
348
- $css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
349
-
350
- // Fix for issue: #2528142
351
- // Replace text-shadow:0; with text-shadow:0 0 0;
352
- $css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
353
-
354
- // Replace background-position:0; with background-position:0 0;
355
- // same for transform-origin
356
- // Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
357
- $css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
358
-
359
- // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
360
- // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
361
- // This makes it more likely that it'll get further compressed in the next step.
362
- $css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
363
- $css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
364
-
365
- // Shorten colors from #AABBCC to #ABC or short color name.
366
- $css = $this->compress_hex_colors($css);
367
-
368
- // border: none to border:0, outline: none to outline:0
369
- $css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
370
-
371
- // shorter opacity IE filter
372
- $css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
373
-
374
- // Find a fraction that is used for Opera's -o-device-pixel-ratio query
375
- // Add token to add the "\" back in later
376
- $css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
377
-
378
- // Remove empty rules.
379
- $css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
380
-
381
- // Add "/" back to fix Opera -o-device-pixel-ratio query
382
- $css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
383
-
384
- // Replace multiple semi-colons in a row by a single one
385
- // See SF bug #1980989
386
- $css = preg_replace('/;;+/', ';', $css);
387
-
388
- // Restore new lines for /*! important comments
389
- $css = preg_replace('/'. self::NL .'/', "\n", $css);
390
-
391
- // Lowercase all uppercase properties
392
- $css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
393
-
394
- // Some source control tools don't like it when files containing lines longer
395
- // than, say 8000 characters, are checked in. The linebreak option is used in
396
- // that case to split long lines after a specific column.
397
- if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
398
- $linebreak_pos = (int) $linebreak_pos;
399
- $start_index = $i = 0;
400
- while ($i < strlen($css)) {
401
- $i++;
402
- if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
403
- $css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
404
- $start_index = $i;
405
- }
406
- }
407
- }
408
-
409
- // restore preserved comments and strings in reverse order
410
- for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
411
- $css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
412
- }
413
-
414
- // Trim the final string (for any leading or trailing white spaces)
415
- return trim($css);
416
- }
417
-
418
- /**
419
- * Utility method to replace all data urls with tokens before we start
420
- * compressing, to avoid performance issues running some of the subsequent
421
- * regexes against large strings chunks.
422
- *
423
- * @param string $css
424
- * @return string
425
- */
426
- private function extract_data_urls($css)
427
- {
428
- // Leave data urls alone to increase parse performance.
429
- $max_index = strlen($css) - 1;
430
- $append_index = $index = $last_index = $offset = 0;
431
- $sb = array();
432
- $pattern = '/url\(\s*(["\']?)data\:/i';
433
-
434
- // Since we need to account for non-base64 data urls, we need to handle
435
- // ' and ) being part of the data string. Hence switching to indexOf,
436
- // to determine whether or not we have matching string terminators and
437
- // handling sb appends directly, instead of using matcher.append* methods.
438
-
439
- while (preg_match($pattern, $css, $m, 0, $offset)) {
440
- $index = $this->index_of($css, $m[0], $offset);
441
- $last_index = $index + strlen($m[0]);
442
- $start_index = $index + 4; // "url(".length()
443
- $end_index = $last_index - 1;
444
- $terminator = $m[1]; // ', " or empty (not quoted)
445
- $found_terminator = FALSE;
446
-
447
- if (strlen($terminator) === 0) {
448
- $terminator = ')';
449
- }
450
-
451
- while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
452
- $end_index = $this->index_of($css, $terminator, $end_index + 1);
453
-
454
- // endIndex == 0 doesn't really apply here
455
- if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
456
- $found_terminator = TRUE;
457
- if (')' != $terminator) {
458
- $end_index = $this->index_of($css, ')', $end_index);
459
- }
460
- }
461
- }
462
-
463
- // Enough searching, start moving stuff over to the buffer
464
- $sb[] = $this->str_slice($css, $append_index, $index);
465
-
466
- if ($found_terminator) {
467
- $token = $this->str_slice($css, $start_index, $end_index);
468
- $token = preg_replace('/\s+/', '', $token);
469
- $this->preserved_tokens[] = $token;
470
-
471
- $preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
472
- $sb[] = $preserver;
473
-
474
- $append_index = $end_index + 1;
475
- } else {
476
- // No end terminator found, re-add the whole match. Should we throw/warn here?
477
- $sb[] = $this->str_slice($css, $index, $last_index);
478
- $append_index = $last_index;
479
- }
480
-
481
- $offset = $last_index;
482
- }
483
-
484
- $sb[] = $this->str_slice($css, $append_index);
485
-
486
- return implode('', $sb);
487
- }
488
-
489
- /**
490
- * Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
491
- *
492
- * DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
493
- * e.g. #AddressForm { ... }
494
- *
495
- * DOES NOT compress IE filters, which have hex color values (which would break things).
496
- * e.g. filter: chroma(color="#FFFFFF");
497
- *
498
- * DOES NOT compress invalid hex values.
499
- * e.g. background-color: #aabbccdd
500
- *
501
- * @param string $css
502
- * @return string
503
- */
504
- private function compress_hex_colors($css)
505
- {
506
- // Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
507
- $pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
508
- $_index = $index = $last_index = $offset = 0;
509
- $sb = array();
510
- // See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
511
- $short_safe = array(
512
- '#808080' => 'gray',
513
- '#008000' => 'green',
514
- '#800000' => 'maroon',
515
- '#000080' => 'navy',
516
- '#808000' => 'olive',
517
- '#ffa500' => 'orange',
518
- '#800080' => 'purple',
519
- '#c0c0c0' => 'silver',
520
- '#008080' => 'teal',
521
- '#f00' => 'red'
522
- );
523
-
524
- while (preg_match($pattern, $css, $m, 0, $offset)) {
525
- $index = $this->index_of($css, $m[0], $offset);
526
- $last_index = $index + strlen($m[0]);
527
- $is_filter = $m[1] !== null && $m[1] !== '';
528
-
529
- $sb[] = $this->str_slice($css, $_index, $index);
530
-
531
- if ($is_filter) {
532
- // Restore, maintain case, otherwise filter will break
533
- $sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
534
- } else {
535
- if (strtolower($m[2]) == strtolower($m[3]) &&
536
- strtolower($m[4]) == strtolower($m[5]) &&
537
- strtolower($m[6]) == strtolower($m[7])) {
538
- // Compress.
539
- $hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
540
- } else {
541
- // Non compressible color, restore but lower case.
542
- $hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
543
- }
544
- // replace Hex colors to short safe color names
545
- $sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
546
- }
547
-
548
- $_index = $offset = $last_index - strlen($m[8]);
549
- }
550
-
551
- $sb[] = $this->str_slice($css, $_index);
552
-
553
- return implode('', $sb);
554
- }
555
-
556
- /* CALLBACKS
557
- * ---------------------------------------------------------------------------------------------
558
- */
559
-
560
- private function replace_string($matches)
561
- {
562
- $match = $matches[0];
563
- $quote = substr($match, 0, 1);
564
- // Must use addcslashes in PHP to avoid parsing of backslashes
565
- $match = addcslashes($this->str_slice($match, 1, -1), '\\');
566
-
567
- // maybe the string contains a comment-like substring?
568
- // one, maybe more? put'em back then
569
- if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
570
- for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
571
- $match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
572
- }
573
- }
574
-
575
- // minify alpha opacity in filter strings
576
- $match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
577
-
578
- $this->preserved_tokens[] = $match;
579
- return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
580
- }
581
-
582
- private function replace_colon($matches)
583
- {
584
- return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
585
- }
586
-
587
- private function replace_calc($matches)
588
- {
589
- $this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]));
590
- return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
591
- }
592
-
593
- private function preserve_old_IE_specific_matrix_definition($matches)
594
- {
595
- $this->preserved_tokens[] = $matches[1];
596
- return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
597
- }
598
-
599
- private function replace_keyframe_zero($matches)
600
- {
601
- return $matches[1] . preg_replace('/0(\{|,[^\)\{]+\{)/', '0%$1', $matches[2]) . $matches[3];
602
- }
603
-
604
- private function rgb_to_hex($matches)
605
- {
606
- // Support for percentage values rgb(100%, 0%, 45%);
607
- if ($this->index_of($matches[1], '%') >= 0){
608
- $rgbcolors = explode(',', str_replace('%', '', $matches[1]));
609
- for ($i = 0; $i < count($rgbcolors); $i++) {
610
- $rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
611
- }
612
- } else {
613
- $rgbcolors = explode(',', $matches[1]);
614
- }
615
-
616
- // Values outside the sRGB color space should be clipped (0-255)
617
- for ($i = 0; $i < count($rgbcolors); $i++) {
618
- $rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
619
- $rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
620
- }
621
-
622
- // Fix for issue #2528093
623
- if (!preg_match('/[\s\,\);\}]/', $matches[2])){
624
- $matches[2] = ' ' . $matches[2];
625
- }
626
-
627
- return '#' . implode('', $rgbcolors) . $matches[2];
628
- }
629
-
630
- private function hsl_to_hex($matches)
631
- {
632
- $values = explode(',', str_replace('%', '', $matches[1]));
633
- $h = floatval($values[0]);
634
- $s = floatval($values[1]);
635
- $l = floatval($values[2]);
636
-
637
- // Wrap and clamp, then fraction!
638
- $h = ((($h % 360) + 360) % 360) / 360;
639
- $s = $this->clamp_number($s, 0, 100) / 100;
640
- $l = $this->clamp_number($l, 0, 100) / 100;
641
-
642
- if ($s == 0) {
643
- $r = $g = $b = $this->round_number(255 * $l);
644
- } else {
645
- $v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
646
- $v1 = (2 * $l) - $v2;
647
- $r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
648
- $g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
649
- $b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
650
- }
651
-
652
- return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
653
- }
654
-
655
- private function lowercase_pseudo_first($matches)
656
- {
657
- return ':first-'. strtolower($matches[1]) .' '. $matches[2];
658
- }
659
-
660
- private function lowercase_directives($matches)
661
- {
662
- return '@'. strtolower($matches[1]);
663
- }
664
-
665
- private function lowercase_pseudo_elements($matches)
666
- {
667
- return ':'. strtolower($matches[1]);
668
- }
669
-
670
- private function lowercase_common_functions($matches)
671
- {
672
- return ':'. strtolower($matches[1]) .'(';
673
- }
674
-
675
- private function lowercase_common_functions_values($matches)
676
- {
677
- return $matches[1] . strtolower($matches[2]);
678
- }
679
-
680
- private function lowercase_properties($matches)
681
- {
682
- return $matches[1].strtolower($matches[2]).$matches[3];
683
- }
684
-
685
- /* HELPERS
686
- * ---------------------------------------------------------------------------------------------
687
- */
688
-
689
- private function hue_to_rgb($v1, $v2, $vh)
690
- {
691
- $vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
692
- if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
693
- if ($vh * 2 < 1) return $v2;
694
- if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
695
- return $v1;
696
- }
697
-
698
- private function round_number($n)
699
- {
700
- return intval(floor(floatval($n) + 0.5), 10);
701
- }
702
-
703
- private function clamp_number($n, $min, $max)
704
- {
705
- return min(max($n, $min), $max);
706
- }
707
-
708
- /**
709
- * PHP port of Javascript's "indexOf" function for strings only
710
- * Author: Tubal Martin http://blog.margenn.com
711
- *
712
- * @param string $haystack
713
- * @param string $needle
714
- * @param int $offset index (optional)
715
- * @return int
716
- */
717
- private function index_of($haystack, $needle, $offset = 0)
718
- {
719
- $index = strpos($haystack, $needle, $offset);
720
-
721
- return ($index !== FALSE) ? $index : -1;
722
- }
723
-
724
- /**
725
- * PHP port of Javascript's "slice" function for strings only
726
- * Author: Tubal Martin http://blog.margenn.com
727
- * Tests: http://margenn.com/tubal/str_slice/
728
- *
729
- * @param string $str
730
- * @param int $start index
731
- * @param int|bool $end index (optional)
732
- * @return string
733
- */
734
- private function str_slice($str, $start = 0, $end = FALSE)
735
- {
736
- if ($end !== FALSE && ($start < 0 || $end <= 0)) {
737
- $max = strlen($str);
738
-
739
- if ($start < 0) {
740
- if (($start = $max + $start) < 0) {
741
- return '';
742
- }
743
- }
744
-
745
- if ($end < 0) {
746
- if (($end = $max + $end) < 0) {
747
- return '';
748
- }
749
- }
750
-
751
- if ($end <= $start) {
752
- return '';
753
- }
754
- }
755
-
756
- $slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
757
- return ($slice === FALSE) ? '' : $slice;
758
- }
759
-
760
- /**
761
- * Convert strings like "64M" or "30" to int values
762
- * @param mixed $size
763
- * @return int
764
- */
765
- private function normalize_int($size)
766
- {
767
- if (is_string($size)) {
768
- switch (substr($size, -1)) {
769
- case 'M': case 'm': return $size * 1048576;
770
- case 'K': case 'k': return $size * 1024;
771
- case 'G': case 'g': return $size * 1073741824;
772
- }
773
- }
774
-
775
- return (int) $size;
776
- }
777
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/external/php/yui-php-cssmin-2.4.8-4_fgo.php CHANGED
@@ -592,7 +592,7 @@ class CSSmin
592
 
593
  private function replace_calc($matches)
594
  {
595
- $this->preserved_tokens[] = preg_replace('/([\+\-]{1})\(/','$1 (',trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2])));
596
  return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
597
  }
598
 
@@ -778,9 +778,9 @@ class CSSmin
778
  {
779
  if (is_string($size)) {
780
  switch (substr($size, -1)) {
781
- case 'M': case 'm': return $size * 1048576;
782
- case 'K': case 'k': return $size * 1024;
783
- case 'G': case 'g': return $size * 1073741824;
784
  }
785
  }
786
 
592
 
593
  private function replace_calc($matches)
594
  {
595
+ $this->preserved_tokens[] = preg_replace('/\)([\+\-]{1})/',') $1',preg_replace('/([\+\-]{1})\(/','$1 (',trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]))));
596
  return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
597
  }
598
 
778
  {
779
  if (is_string($size)) {
780
  switch (substr($size, -1)) {
781
+ case 'M': case 'm': return (int) $size * 1048576;
782
+ case 'K': case 'k': return (int) $size * 1024;
783
+ case 'G': case 'g': return (int) $size * 1073741824;
784
  }
785
  }
786
 
classes/external/php/yui-php-cssmin-2.4.8-p10/cssmin.php ADDED
@@ -0,0 +1,1112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*!
4
+ * cssmin.php
5
+ * Author: Tubal Martin - http://tubalmartin.me/
6
+ * Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
7
+ *
8
+ * This is a PHP port of the CSS minification tool distributed with YUICompressor,
9
+ * itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
10
+ * Permission is hereby granted to use the PHP version under the same
11
+ * conditions as the YUICompressor.
12
+ */
13
+
14
+ /*!
15
+ * YUI Compressor
16
+ * http://developer.yahoo.com/yui/compressor/
17
+ * Author: Julien Lecomte - http://www.julienlecomte.net/
18
+ * Copyright (c) 2013 Yahoo! Inc. All rights reserved.
19
+ * The copyrights embodied in the content of this file are licensed
20
+ * by Yahoo! Inc. under the BSD (revised) open source license.
21
+ */
22
+
23
+ class CSSmin
24
+ {
25
+ const NL = '___YUICSSMIN_PRESERVED_NL___';
26
+ const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
27
+ const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
28
+
29
+ const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
30
+ const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
31
+ const AT_RULE_BLOCK = '___YUICSSMIN_PRESERVE_AT_RULE_BLOCK_';
32
+
33
+ private $comments;
34
+ private $atRuleBlocks;
35
+ private $preservedTokens;
36
+ private $chunkLength = 5000;
37
+ private $minChunkLength = 100;
38
+ private $memoryLimit;
39
+ private $maxExecutionTime = 60; // 1 min
40
+ private $pcreBacktrackLimit;
41
+ private $pcreRecursionLimit;
42
+ private $raisePhpLimits;
43
+
44
+ private $unitsGroupRegex = '(?:ch|cm|em|ex|gd|in|mm|px|pt|pc|q|rem|vh|vmax|vmin|vw|%)';
45
+ private $numRegex;
46
+
47
+ /**
48
+ * @param bool|int $raisePhpLimits If true, PHP settings will be raised if needed
49
+ */
50
+ public function __construct($raisePhpLimits = true)
51
+ {
52
+ $this->memoryLimit = 128 * 1048576; // 128MB in bytes
53
+ $this->pcreBacktrackLimit = 1000 * 1000;
54
+ $this->pcreRecursionLimit = 500 * 1000;
55
+
56
+ $this->raisePhpLimits = (bool) $raisePhpLimits;
57
+
58
+ $this->numRegex = '(?:\+|-)?\d*\.?\d+' . $this->unitsGroupRegex .'?';
59
+ }
60
+
61
+ /**
62
+ * Minifies a string of CSS
63
+ * @param string $css
64
+ * @param int|bool $linebreakPos
65
+ * @return string
66
+ */
67
+ public function run($css = '', $linebreakPos = false)
68
+ {
69
+ if (empty($css)) {
70
+ return '';
71
+ }
72
+
73
+ if ($this->raisePhpLimits) {
74
+ $this->doRaisePhpLimits();
75
+ }
76
+
77
+ $this->comments = array();
78
+ $this->atRuleBlocks = array();
79
+ $this->preservedTokens = array();
80
+
81
+ // process data urls
82
+ $css = $this->processDataUrls($css);
83
+
84
+ // process comments
85
+ $css = preg_replace_callback('/(?<!\\\\)\/\*(.*?)\*(?<!\\\\)\//Ss', array($this, 'processComments'), $css);
86
+
87
+ // process strings so their content doesn't get accidentally minified
88
+ $css = preg_replace_callback(
89
+ '/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S",
90
+ array($this, 'processStrings'),
91
+ $css
92
+ );
93
+
94
+ // Safe chunking: process at rule blocks so after chunking nothing gets stripped out
95
+ $css = preg_replace_callback(
96
+ '/@(?:document|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframes|media|supports).+?\}\s*\}/si',
97
+ array($this, 'processAtRuleBlocks'),
98
+ $css
99
+ );
100
+
101
+ // Let's divide css code in chunks of {$this->chunkLength} chars aprox.
102
+ // Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
103
+ // of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
104
+ // long strings and a (sub)pattern matches a number of chars greater than
105
+ // the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
106
+ // returning NULL and $css would be empty.
107
+ $charset = '';
108
+ $charsetRegexp = '/(@charset)( [^;]+;)/i';
109
+ $cssChunks = array();
110
+ $l = strlen($css);
111
+
112
+ // if the number of characters is <= {$this->chunkLength}, do not chunk
113
+ if ($l <= $this->chunkLength) {
114
+ $cssChunks[] = $css;
115
+ } else {
116
+ // chunk css code securely
117
+ for ($startIndex = 0, $i = $this->chunkLength; $i < $l; $i++) {
118
+ if ($css[$i - 1] === '}' && $i - $startIndex >= $this->chunkLength) {
119
+ $cssChunks[] = $this->strSlice($css, $startIndex, $i);
120
+ $startIndex = $i;
121
+ // Move forward saving iterations when possible!
122
+ if ($startIndex + $this->chunkLength < $l) {
123
+ $i += $this->chunkLength;
124
+ }
125
+ }
126
+ }
127
+
128
+ // Final chunk
129
+ $cssChunks[] = $this->strSlice($css, $startIndex);
130
+ }
131
+
132
+ // Minify each chunk
133
+ for ($i = 0, $n = count($cssChunks); $i < $n; $i++) {
134
+ $cssChunks[$i] = $this->minify($cssChunks[$i], $linebreakPos);
135
+ // Keep the first @charset at-rule found
136
+ if (empty($charset) && preg_match($charsetRegexp, $cssChunks[$i], $matches)) {
137
+ $charset = strtolower($matches[1]) . $matches[2];
138
+ }
139
+ // Delete all @charset at-rules
140
+ $cssChunks[$i] = preg_replace($charsetRegexp, '', $cssChunks[$i]);
141
+ }
142
+
143
+ // Update the first chunk and push the charset to the top of the file.
144
+ $cssChunks[0] = $charset . $cssChunks[0];
145
+
146
+ return trim(implode('', $cssChunks));
147
+ }
148
+
149
+ /**
150
+ * Sets the approximate number of characters to use when splitting a string in chunks.
151
+ * @param int $length
152
+ */
153
+ public function set_chunk_length($length)
154
+ {
155
+ $length = (int) $length;
156
+ $this->chunkLength = $length < $this->minChunkLength ? $this->minChunkLength : $length;
157
+ }
158
+
159
+ /**
160
+ * Sets the memory limit for this script
161
+ * @param int|string $limit
162
+ */
163
+ public function set_memory_limit($limit)
164
+ {
165
+ $this->memoryLimit = $this->normalizeInt($limit);
166
+ }
167
+
168
+ /**
169
+ * Sets the maximum execution time for this script
170
+ * @param int|string $seconds
171
+ */
172
+ public function set_max_execution_time($seconds)
173
+ {
174
+ $this->maxExecutionTime = (int) $seconds;
175
+ }
176
+
177
+ /**
178
+ * Sets the PCRE backtrack limit for this script
179
+ * @param int $limit
180
+ */
181
+ public function set_pcre_backtrack_limit($limit)
182
+ {
183
+ $this->pcreBacktrackLimit = (int) $limit;
184
+ }
185
+
186
+ /**
187
+ * Sets the PCRE recursion limit for this script
188
+ * @param int $limit
189
+ */
190
+ public function set_pcre_recursion_limit($limit)
191
+ {
192
+ $this->pcreRecursionLimit = (int) $limit;
193
+ }
194
+
195
+ /**
196
+ * Tries to configure PHP to use at least the suggested minimum settings
197
+ * @return void
198
+ */
199
+ private function doRaisePhpLimits()
200
+ {
201
+ $phpLimits = array(
202
+ 'memory_limit' => $this->memoryLimit,
203
+ 'max_execution_time' => $this->maxExecutionTime,
204
+ 'pcre.backtrack_limit' => $this->pcreBacktrackLimit,
205
+ 'pcre.recursion_limit' => $this->pcreRecursionLimit
206
+ );
207
+
208
+ // If current settings are higher respect them.
209
+ foreach ($phpLimits as $name => $suggested) {
210
+ $current = $this->normalizeInt(ini_get($name));
211
+
212
+ if ($current > $suggested) {
213
+ continue;
214
+ }
215
+
216
+ // memoryLimit exception: allow -1 for "no memory limit".
217
+ if ($name === 'memory_limit' && $current === -1) {
218
+ continue;
219
+ }
220
+
221
+ // maxExecutionTime exception: allow 0 for "no memory limit".
222
+ if ($name === 'max_execution_time' && $current === 0) {
223
+ continue;
224
+ }
225
+
226
+ ini_set($name, $suggested);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Registers a preserved token
232
+ * @param $token
233
+ * @return string The token ID string
234
+ */
235
+ private function registerPreservedToken($token)
236
+ {
237
+ $this->preservedTokens[] = $token;
238
+ return self::TOKEN . (count($this->preservedTokens) - 1) .'___';
239
+ }
240
+
241
+ /**
242
+ * Gets the regular expression to match the specified token ID string
243
+ * @param $id
244
+ * @return string
245
+ */
246
+ private function getPreservedTokenPlaceholderRegexById($id)
247
+ {
248
+ return '/'. self::TOKEN . $id .'___/';
249
+ }
250
+
251
+ /**
252
+ * Registers a candidate comment token
253
+ * @param $comment
254
+ * @return string The comment token ID string
255
+ */
256
+ private function registerComment($comment)
257
+ {
258
+ $this->comments[] = $comment;
259
+ return '/*'. self::COMMENT . (count($this->comments) - 1) .'___*/';
260
+ }
261
+
262
+ /**
263
+ * Gets the candidate comment token ID string for the specified comment token ID
264
+ * @param $id
265
+ * @return string
266
+ */
267
+ private function getCommentPlaceholderById($id)
268
+ {
269
+ return self::COMMENT . $id .'___';
270
+ }
271
+
272
+ /**
273
+ * Gets the regular expression to match the specified comment token ID string
274
+ * @param $id
275
+ * @return string
276
+ */
277
+ private function getCommentPlaceholderRegexById($id)
278
+ {
279
+ return '/'. $this->getCommentPlaceholderById($id) .'/';
280
+ }
281
+
282
+ /**
283
+ * Registers an at rule block token
284
+ * @param $block
285
+ * @return string The comment token ID string
286
+ */
287
+ private function registerAtRuleBlock($block)
288
+ {
289
+ $this->atRuleBlocks[] = $block;
290
+ return self::AT_RULE_BLOCK . (count($this->atRuleBlocks) - 1) .'___';
291
+ }
292
+
293
+ /**
294
+ * Gets the regular expression to match the specified at rule block token ID string
295
+ * @param $id
296
+ * @return string
297
+ */
298
+ private function getAtRuleBlockPlaceholderRegexById($id)
299
+ {
300
+ return '/'. self::AT_RULE_BLOCK . $id .'___/';
301
+ }
302
+
303
+ /**
304
+ * Minifies the given input CSS string
305
+ * @param string $css
306
+ * @param int|bool $linebreakPos
307
+ * @return string
308
+ */
309
+ private function minify($css, $linebreakPos)
310
+ {
311
+ // Restore preserved at rule blocks
312
+ for ($i = 0, $max = count($this->atRuleBlocks); $i < $max; $i++) {
313
+ $css = preg_replace(
314
+ $this->getAtRuleBlockPlaceholderRegexById($i),
315
+ $this->escapeReplacementString($this->atRuleBlocks[$i]),
316
+ $css,
317
+ 1
318
+ );
319
+ }
320
+
321
+ // strings are safe, now wrestle the comments
322
+ for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
323
+ $comment = $this->comments[$i];
324
+ $commentPlaceholder = $this->getCommentPlaceholderById($i);
325
+ $commentPlaceholderRegex = $this->getCommentPlaceholderRegexById($i);
326
+
327
+ // ! in the first position of the comment means preserve
328
+ // so push to the preserved tokens keeping the !
329
+ if (preg_match('/^!/', $comment)) {
330
+ $preservedTokenPlaceholder = $this->registerPreservedToken($comment);
331
+ $css = preg_replace($commentPlaceholderRegex, $preservedTokenPlaceholder, $css, 1);
332
+ // Preserve new lines for /*! important comments
333
+ $css = preg_replace('/\R+\s*(\/\*'. $preservedTokenPlaceholder .')/', self::NL.'$1', $css);
334
+ $css = preg_replace('/('. $preservedTokenPlaceholder .'\*\/)\s*\R+/', '$1'.self::NL, $css);
335
+ continue;
336
+ }
337
+
338
+ // \ in the last position looks like hack for Mac/IE5
339
+ // shorten that to /*\*/ and the next one to /**/
340
+ if (preg_match('/\\\\$/', $comment)) {
341
+ $preservedTokenPlaceholder = $this->registerPreservedToken('\\');
342
+ $css = preg_replace($commentPlaceholderRegex, $preservedTokenPlaceholder, $css, 1);
343
+ $i = $i + 1; // attn: advancing the loop
344
+ $preservedTokenPlaceholder = $this->registerPreservedToken('');
345
+ $css = preg_replace($this->getCommentPlaceholderRegexById($i), $preservedTokenPlaceholder, $css, 1);
346
+ continue;
347
+ }
348
+
349
+ // keep empty comments after child selectors (IE7 hack)
350
+ // e.g. html >/**/ body
351
+ if (strlen($comment) === 0) {
352
+ $startIndex = $this->indexOf($css, $commentPlaceholder);
353
+ if ($startIndex > 2) {
354
+ if (substr($css, $startIndex - 3, 1) === '>') {
355
+ $preservedTokenPlaceholder = $this->registerPreservedToken('');
356
+ $css = preg_replace($commentPlaceholderRegex, $preservedTokenPlaceholder, $css, 1);
357
+ continue;
358
+ }
359
+ }
360
+ }
361
+
362
+ // in all other cases kill the comment
363
+ $css = preg_replace('/\/\*' . $commentPlaceholder . '\*\//', '', $css, 1);
364
+ }
365
+
366
+ // Normalize all whitespace strings to single spaces. Easier to work with that way.
367
+ $css = preg_replace('/\s+/', ' ', $css);
368
+
369
+ // Remove spaces before & after newlines
370
+ $css = preg_replace('/\s*'. self::NL .'\s*/', self::NL, $css);
371
+
372
+ // Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
373
+ $css = preg_replace_callback(
374
+ '/\s*filter:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^)]+)\)/',
375
+ array($this, 'processOldIeSpecificMatrixDefinition'),
376
+ $css
377
+ );
378
+
379
+ // Shorten & preserve calculations calc(...) since spaces are important
380
+ $css = preg_replace_callback('/calc(\(((?:[^()]+|(?1))*)\))/i', array($this, 'processCalc'), $css);
381
+
382
+ // Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
383
+ // +1.2em to 1.2em, +.8px to .8px, +2% to 2%
384
+ $css = preg_replace('/((?<!\\\\):|\s)\+(\.?\d+)/S', '$1$2', $css);
385
+
386
+ // Remove leading zeros from integer and float numbers preceded by : or a white-space
387
+ // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
388
+ $css = preg_replace('/((?<!\\\\):|\s)(-?)0+(\.?\d+)/S', '$1$2$3', $css);
389
+
390
+ // Remove trailing zeros from float numbers preceded by : or a white-space
391
+ // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
392
+ $css = preg_replace('/((?<!\\\\):|\s)(-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
393
+
394
+ // Remove trailing .0 -> -9.0 to -9
395
+ $css = preg_replace('/((?<!\\\\):|\s)(-?\d+)\.0([^\d])/S', '$1$2$3', $css);
396
+
397
+ // Replace 0 length numbers with 0
398
+ $css = preg_replace('/((?<!\\\\):|\s)-?\.?0+([^\d])/S', '${1}0$2', $css);
399
+
400
+ // Remove the spaces before the things that should not have spaces before them.
401
+ // But, be careful not to turn "p :link {...}" into "p:link{...}"
402
+ // Swap out any pseudo-class colons with the token, and then swap back.
403
+ $css = preg_replace_callback('/(?:^|\})[^{]*\s+:/', array($this, 'processColon'), $css);
404
+
405
+ // Remove spaces before the things that should not have spaces before them.
406
+ $css = preg_replace('/\s+([!{};:>+()\]~=,])/', '$1', $css);
407
+
408
+ // Restore spaces for !important
409
+ $css = preg_replace('/!important/i', ' !important', $css);
410
+
411
+ // bring back the colon
412
+ $css = preg_replace('/'. self::CLASSCOLON .'/', ':', $css);
413
+
414
+ // retain space for special IE6 cases
415
+ $css = preg_replace_callback('/:first-(line|letter)(\{|,)/i', array($this, 'lowercasePseudoFirst'), $css);
416
+
417
+ // no space after the end of a preserved comment
418
+ $css = preg_replace('/\*\/ /', '*/', $css);
419
+
420
+ // lowercase some popular @directives
421
+ $css = preg_replace_callback(
422
+ '/@(document|font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframes|media|namespace|page|' .
423
+ 'supports|viewport)/i',
424
+ array($this, 'lowercaseDirectives'),
425
+ $css
426
+ );
427
+
428
+ // lowercase some more common pseudo-elements
429
+ $css = preg_replace_callback(
430
+ '/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|' .
431
+ 'last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i',
432
+ array($this, 'lowercasePseudoElements'),
433
+ $css
434
+ );
435
+
436
+ // lowercase some more common functions
437
+ $css = preg_replace_callback(
438
+ '/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i',
439
+ array($this, 'lowercaseCommonFunctions'),
440
+ $css
441
+ );
442
+
443
+ // lower case some common function that can be values
444
+ // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
445
+ $css = preg_replace_callback(
446
+ '/([:,( ]\s*)(attr|color-stop|from|rgba|to|url|-webkit-gradient|' .
447
+ '(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient))/iS',
448
+ array($this, 'lowercaseCommonFunctionsValues'),
449
+ $css
450
+ );
451
+
452
+ // Put the space back in some cases, to support stuff like
453
+ // @media screen and (-webkit-min-device-pixel-ratio:0){
454
+ $css = preg_replace_callback('/(\s|\)\s)(and|not|or)\(/i', array($this, 'processAtRulesOperators'), $css);
455
+
456
+ // Remove the spaces after the things that should not have spaces after them.
457
+ $css = preg_replace('/([!{}:;>+(\[~=,])\s+/S', '$1', $css);
458
+
459
+ // remove unnecessary semicolons
460
+ $css = preg_replace('/;+\}/', '}', $css);
461
+
462
+ // Fix for issue: #2528146
463
+ // Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
464
+ // to avoid issues on Symbian S60 3.x browsers.
465
+ $css = preg_replace('/(\*[a-z0-9\-]+\s*:[^;}]+)(\})/', '$1;$2', $css);
466
+
467
+ // Shorten zero values for safe properties only
468
+ $css = $this->shortenZeroValues($css);
469
+
470
+ // Shorten font-weight values
471
+ $css = preg_replace('/(font-weight:)bold\b/i', '${1}700', $css);
472
+ $css = preg_replace('/(font-weight:)normal\b/i', '${1}400', $css);
473
+
474
+ // Shorten suitable shorthand properties with repeated non-zero values
475
+ $css = preg_replace(
476
+ '/(margin|padding):('.$this->numRegex.') ('.$this->numRegex.') (?:\2) (?:\3)(;|\}| !)/i',
477
+ '$1:$2 $3$4',
478
+ $css
479
+ );
480
+ $css = preg_replace(
481
+ '/(margin|padding):('.$this->numRegex.') ('.$this->numRegex.') ('.$this->numRegex.') (?:\3)(;|\}| !)/i',
482
+ '$1:$2 $3 $4$5',
483
+ $css
484
+ );
485
+
486
+ // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
487
+ // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
488
+ // This makes it more likely that it'll get further compressed in the next step.
489
+ $css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-.%]+)\s*\)(.{1})/i', array($this, 'rgbToHex'), $css);
490
+ $css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-.%]+)\s*\)(.{1})/i', array($this, 'hslToHex'), $css);
491
+
492
+ // Shorten colors from #AABBCC to #ABC or shorter color name.
493
+ $css = $this->shortenHexColors($css);
494
+
495
+ // Shorten long named colors: white -> #fff.
496
+ $css = $this->shortenNamedColors($css);
497
+
498
+ // shorter opacity IE filter
499
+ $css = preg_replace('/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/i', 'alpha(opacity=', $css);
500
+
501
+ // Find a fraction that is used for Opera's -o-device-pixel-ratio query
502
+ // Add token to add the "\" back in later
503
+ $css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
504
+
505
+ // Patch new lines to avoid being removed when followed by empty rules cases
506
+ $css = preg_replace('/'. self::NL .'/', self::NL .'}', $css);
507
+
508
+ // Remove empty rules.
509
+ $css = preg_replace('/[^{};\/]+\{\}/S', '', $css);
510
+
511
+ // Restore new lines for /*! important comments
512
+ $css = preg_replace('/'. self::NL .'}/', "\n", $css);
513
+
514
+ // Add "/" back to fix Opera -o-device-pixel-ratio query
515
+ $css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
516
+
517
+ // Replace multiple semi-colons in a row by a single one
518
+ // See SF bug #1980989
519
+ $css = preg_replace('/;;+/', ';', $css);
520
+
521
+ // Lowercase all uppercase properties
522
+ $css = preg_replace_callback('/(\{|;)([A-Z\-]+)(:)/', array($this, 'lowercaseProperties'), $css);
523
+
524
+ // Some source control tools don't like it when files containing lines longer
525
+ // than, say 8000 characters, are checked in. The linebreak option is used in
526
+ // that case to split long lines after a specific column.
527
+ if ($linebreakPos !== false && (int) $linebreakPos >= 0) {
528
+ $linebreakPos = (int) $linebreakPos;
529
+ for ($startIndex = $i = 1, $l = strlen($css); $i < $l; $i++) {
530
+ if ($css[$i - 1] === '}' && $i - $startIndex > $linebreakPos) {
531
+ $css = $this->strSlice($css, 0, $i) . "\n" . $this->strSlice($css, $i);
532
+ $l = strlen($css);
533
+ $startIndex = $i;
534
+ }
535
+ }
536
+ }
537
+
538
+ // restore preserved comments and strings in reverse order
539
+ for ($i = count($this->preservedTokens) - 1; $i >= 0; $i--) {
540
+ $css = preg_replace(
541
+ $this->getPreservedTokenPlaceholderRegexById($i),
542
+ $this->escapeReplacementString($this->preservedTokens[$i]),
543
+ $css,
544
+ 1
545
+ );
546
+ }
547
+
548
+ // Trim the final string for any leading or trailing white space but respect newlines!
549
+ $css = preg_replace('/(^ | $)/', '', $css);
550
+
551
+ return $css;
552
+ }
553
+
554
+ /**
555
+ * Searches & replaces all data urls with tokens before we start compressing,
556
+ * to avoid performance issues running some of the subsequent regexes against large string chunks.
557
+ * @param string $css
558
+ * @return string
559
+ */
560
+ private function processDataUrls($css)
561
+ {
562
+ // Leave data urls alone to increase parse performance.
563
+ $maxIndex = strlen($css) - 1;
564
+ $appenIndex = $index = $lastIndex = $offset = 0;
565
+ $sb = array();
566
+ $pattern = '/url\(\s*(["\']?)data:/i';
567
+
568
+ // Since we need to account for non-base64 data urls, we need to handle
569
+ // ' and ) being part of the data string. Hence switching to indexOf,
570
+ // to determine whether or not we have matching string terminators and
571
+ // handling sb appends directly, instead of using matcher.append* methods.
572
+ while (preg_match($pattern, $css, $m, 0, $offset)) {
573
+ $index = $this->indexOf($css, $m[0], $offset);
574
+ $lastIndex = $index + strlen($m[0]);
575
+ $startIndex = $index + 4; // "url(".length()
576
+ $endIndex = $lastIndex - 1;
577
+ $terminator = $m[1]; // ', " or empty (not quoted)
578
+ $terminatorFound = false;
579
+
580
+ if (strlen($terminator) === 0) {
581
+ $terminator = ')';
582
+ }
583
+
584
+ while ($terminatorFound === false && $endIndex+1 <= $maxIndex) {
585
+ $endIndex = $this->indexOf($css, $terminator, $endIndex + 1);
586
+ // endIndex == 0 doesn't really apply here
587
+ if ($endIndex > 0 && substr($css, $endIndex - 1, 1) !== '\\') {
588
+ $terminatorFound = true;
589
+ if (')' !== $terminator) {
590
+ $endIndex = $this->indexOf($css, ')', $endIndex);
591
+ }
592
+ }
593
+ }
594
+
595
+ // Enough searching, start moving stuff over to the buffer
596
+ $sb[] = $this->strSlice($css, $appenIndex, $index);
597
+
598
+ if ($terminatorFound) {
599
+ $token = $this->strSlice($css, $startIndex, $endIndex);
600
+ // Remove all spaces only for base64 encoded URLs.
601
+ $token = preg_replace_callback(
602
+ '/.+base64,.+/s',
603
+ array($this, 'removeSpacesFromDataUrls'),
604
+ trim($token)
605
+ );
606
+ $preservedTokenPlaceholder = $this->registerPreservedToken($token);
607
+ $sb[] = 'url('. $preservedTokenPlaceholder .')';
608
+ $appenIndex = $endIndex + 1;
609
+ } else {
610
+ // No end terminator found, re-add the whole match. Should we throw/warn here?
611
+ $sb[] = $this->strSlice($css, $index, $lastIndex);
612
+ $appenIndex = $lastIndex;
613
+ }
614
+
615
+ $offset = $lastIndex;
616
+ }
617
+
618
+ $sb[] = $this->strSlice($css, $appenIndex);
619
+
620
+ return implode('', $sb);
621
+ }
622
+
623
+ /**
624
+ * Shortens all zero values for a set of safe properties
625
+ * e.g. padding: 0px 1px; -> padding:0 1px
626
+ * e.g. padding: 0px 0rem 0em 0.0pc; -> padding:0
627
+ * @param string $css
628
+ * @return string
629
+ */
630
+ private function shortenZeroValues($css)
631
+ {
632
+ $unitsGroupReg = $this->unitsGroupRegex;
633
+ $numOrPosReg = '('. $this->numRegex .'|top|left|bottom|right|center)';
634
+ $oneZeroSafeProperties = array(
635
+ '(?:line-)?height',
636
+ '(?:(?:min|max)-)?width',
637
+ 'top',
638
+ 'left',
639
+ 'background-position',
640
+ 'bottom',
641
+ 'right',
642
+ 'border(?:-(?:top|left|bottom|right))?(?:-width)?',
643
+ 'border-(?:(?:top|bottom)-(?:left|right)-)?radius',
644
+ 'column-(?:gap|width)',
645
+ 'margin(?:-(?:top|left|bottom|right))?',
646
+ 'outline-width',
647
+ 'padding(?:-(?:top|left|bottom|right))?'
648
+ );
649
+ $nZeroSafeProperties = array(
650
+ 'margin',
651
+ 'padding',
652
+ 'background-position'
653
+ );
654
+
655
+ $regStart = '/(;|\{)';
656
+ $regEnd = '/i';
657
+
658
+ // First zero regex start
659
+ $oneZeroRegStart = $regStart .'('. implode('|', $oneZeroSafeProperties) .'):';
660
+
661
+ // Multiple zeros regex start
662
+ $nZerosRegStart = $regStart .'('. implode('|', $nZeroSafeProperties) .'):';
663
+
664
+ $css = preg_replace(
665
+ array(
666
+ $oneZeroRegStart .'0'. $unitsGroupReg . $regEnd,
667
+ $nZerosRegStart . $numOrPosReg .' 0'. $unitsGroupReg . $regEnd,
668
+ $nZerosRegStart . $numOrPosReg .' '. $numOrPosReg .' 0'. $unitsGroupReg . $regEnd,
669
+ $nZerosRegStart . $numOrPosReg .' '. $numOrPosReg .' '. $numOrPosReg .' 0'. $unitsGroupReg . $regEnd
670
+ ),
671
+ array(
672
+ '$1$2:0',
673
+ '$1$2:$3 0',
674
+ '$1$2:$3 $4 0',
675
+ '$1$2:$3 $4 $5 0'
676
+ ),
677
+ $css
678
+ );
679
+
680
+ // Remove background-position
681
+ array_pop($nZeroSafeProperties);
682
+
683
+ // Replace 0 0; or 0 0 0; or 0 0 0 0; with 0 for safe properties only.
684
+ $css = preg_replace(
685
+ '/('. implode('|', $nZeroSafeProperties) .'):0(?: 0){1,3}(;|\}| !)'. $regEnd,
686
+ '$1:0$2',
687
+ $css
688
+ );
689
+
690
+ // Replace 0 0 0; or 0 0 0 0; with 0 0 for background-position property.
691
+ $css = preg_replace('/(background-position):0(?: 0){2,3}(;|\}| !)'. $regEnd, '$1:0 0$2', $css);
692
+
693
+ return $css;
694
+ }
695
+
696
+ /**
697
+ * Shortens all named colors with a shorter HEX counterpart for a set of safe properties
698
+ * e.g. white -> #fff
699
+ * @param string $css
700
+ * @return string
701
+ */
702
+ private function shortenNamedColors($css)
703
+ {
704
+ $patterns = array();
705
+ $replacements = array();
706
+ $longNamedColors = include 'data/named-to-hex-color-map.php';
707
+ $propertiesWithColors = array(
708
+ 'color',
709
+ 'background(?:-color)?',
710
+ 'border(?:-(?:top|right|bottom|left|color)(?:-color)?)?',
711
+ 'outline(?:-color)?',
712
+ '(?:text|box)-shadow'
713
+ );
714
+
715
+ $regStart = '/(;|\{)('. implode('|', $propertiesWithColors) .'):([^;}]*)\b';
716
+ $regEnd = '\b/iS';
717
+
718
+ foreach ($longNamedColors as $colorName => $colorCode) {
719
+ $patterns[] = $regStart . $colorName . $regEnd;
720
+ $replacements[] = '$1$2:$3'. $colorCode;
721
+ }
722
+
723
+ // Run at least 4 times to cover most cases (same color used several times for the same property)
724
+ for ($i = 0; $i < 4; $i++) {
725
+ $css = preg_replace($patterns, $replacements, $css);
726
+ }
727
+
728
+ return $css;
729
+ }
730
+
731
+ /**
732
+ * Compresses HEX color values of the form #AABBCC to #ABC or short color name.
733
+ *
734
+ * DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
735
+ * e.g. #AddressForm { ... }
736
+ *
737
+ * DOES NOT compress IE filters, which have hex color values (which would break things).
738
+ * e.g. filter: chroma(color="#FFFFFF");
739
+ *
740
+ * DOES NOT compress invalid hex values.
741
+ * e.g. background-color: #aabbccdd
742
+ *
743
+ * @param string $css
744
+ * @return string
745
+ */
746
+ private function shortenHexColors($css)
747
+ {
748
+ // Look for hex colors inside { ... } (to avoid IDs) and
749
+ // which don't have a =, or a " in front of them (to avoid filters)
750
+ $pattern =
751
+ '/(=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
752
+ $_index = $index = $lastIndex = $offset = 0;
753
+ $longHexColors = include 'data/hex-to-named-color-map.php';
754
+ $sb = array();
755
+
756
+ while (preg_match($pattern, $css, $m, 0, $offset)) {
757
+ $index = $this->indexOf($css, $m[0], $offset);
758
+ $lastIndex = $index + strlen($m[0]);
759
+ $isFilter = $m[1] !== null && $m[1] !== '';
760
+
761
+ $sb[] = $this->strSlice($css, $_index, $index);
762
+
763
+ if ($isFilter) {
764
+ // Restore, maintain case, otherwise filter will break
765
+ $sb[] = $m[1] .'#'. $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
766
+ } else {
767
+ if (strtolower($m[2]) == strtolower($m[3]) &&
768
+ strtolower($m[4]) == strtolower($m[5]) &&
769
+ strtolower($m[6]) == strtolower($m[7])) {
770
+ // Compress.
771
+ $hex = '#'. strtolower($m[3] . $m[5] . $m[7]);
772
+ } else {
773
+ // Non compressible color, restore but lower case.
774
+ $hex = '#'. strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
775
+ }
776
+ // replace Hex colors with shorter color names
777
+ $sb[] = array_key_exists($hex, $longHexColors) ? $longHexColors[$hex] : $hex;
778
+ }
779
+
780
+ $_index = $offset = $lastIndex - strlen($m[8]);
781
+ }
782
+
783
+ $sb[] = $this->strSlice($css, $_index);
784
+
785
+ return implode('', $sb);
786
+ }
787
+
788
+ // ---------------------------------------------------------------------------------------------
789
+ // CALLBACKS
790
+ // ---------------------------------------------------------------------------------------------
791
+
792
+ private function processComments($matches)
793
+ {
794
+ $match = !empty($matches[1]) ? $matches[1] : '';
795
+ return $this->registerComment($match);
796
+ }
797
+
798
+ private function processStrings($matches)
799
+ {
800
+ $match = $matches[0];
801
+ $quote = substr($match, 0, 1);
802
+ $match = $this->strSlice($match, 1, -1);
803
+
804
+ // maybe the string contains a comment-like substring?
805
+ // one, maybe more? put'em back then
806
+ if (($pos = strpos($match, self::COMMENT)) !== false) {
807
+ for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
808
+ $match = preg_replace(
809
+ $this->getCommentPlaceholderRegexById($i),
810
+ $this->escapeReplacementString($this->comments[$i]),
811
+ $match,
812
+ 1
813
+ );
814
+ }
815
+ }
816
+
817
+ // minify alpha opacity in filter strings
818
+ $match = preg_replace('/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/i', 'alpha(opacity=', $match);
819
+
820
+ $preservedTokenPlaceholder = $this->registerPreservedToken($match);
821
+ return $quote . $preservedTokenPlaceholder . $quote;
822
+ }
823
+
824
+ private function processAtRuleBlocks($matches)
825
+ {
826
+ return $this->registerAtRuleBlock($matches[0]);
827
+ }
828
+
829
+ private function processCalc($matches)
830
+ {
831
+ $token = preg_replace(
832
+ '/\)([+\-]{1})/',
833
+ ') $1',
834
+ preg_replace(
835
+ '/([+\-]{1})\(/',
836
+ '$1 (',
837
+ trim(preg_replace('/\s*([*\/(),])\s*/', '$1', $matches[2]))
838
+ )
839
+ );
840
+ $preservedTokenPlaceholder = $this->registerPreservedToken($token);
841
+ return 'calc('. $preservedTokenPlaceholder .')';
842
+ }
843
+
844
+ private function processOldIeSpecificMatrixDefinition($matches)
845
+ {
846
+ $preservedTokenPlaceholder = $this->registerPreservedToken($matches[1]);
847
+ return 'filter:progid:DXImageTransform.Microsoft.Matrix('. $preservedTokenPlaceholder .')';
848
+ }
849
+
850
+ private function processColon($matches)
851
+ {
852
+ return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
853
+ }
854
+
855
+ private function removeSpacesFromDataUrls($matches)
856
+ {
857
+ return preg_replace('/\s+/', '', $matches[0]);
858
+ }
859
+
860
+ private function rgbToHex($matches)
861
+ {
862
+ $hexColors = array();
863
+ $rgbColors = explode(',', $matches[1]);
864
+
865
+ // Values outside the sRGB color space should be clipped (0-255)
866
+ for ($i = 0, $l = count($rgbColors); $i < $l; $i++) {
867
+ $hexColors[$i] = sprintf("%02x", $this->clampNumberSrgb($this->rgbPercentageToRgbInteger($rgbColors[$i])));
868
+ }
869
+
870
+ // Fix for issue #2528093
871
+ if (!preg_match('/[\s,);}]/', $matches[2])) {
872
+ $matches[2] = ' '. $matches[2];
873
+ }
874
+
875
+ return '#'. implode('', $hexColors) . $matches[2];
876
+ }
877
+
878
+ private function hslToHex($matches)
879
+ {
880
+ $hslValues = explode(',', $matches[1]);
881
+
882
+ $rgbColors = $this->hslToRgb($hslValues);
883
+
884
+ return $this->rgbToHex(array('', implode(',', $rgbColors), $matches[2]));
885
+ }
886
+
887
+ private function processAtRulesOperators($matches)
888
+ {
889
+ return $matches[1] . strtolower($matches[2]) .' (';
890
+ }
891
+
892
+ private function lowercasePseudoFirst($matches)
893
+ {
894
+ return ':first-'. strtolower($matches[1]) .' '. $matches[2];
895
+ }
896
+
897
+ private function lowercaseDirectives($matches)
898
+ {
899
+ return '@'. strtolower($matches[1]);
900
+ }
901
+
902
+ private function lowercasePseudoElements($matches)
903
+ {
904
+ return ':'. strtolower($matches[1]);
905
+ }
906
+
907
+ private function lowercaseCommonFunctions($matches)
908
+ {
909
+ return ':'. strtolower($matches[1]) .'(';
910
+ }
911
+
912
+ private function lowercaseCommonFunctionsValues($matches)
913
+ {
914
+ return $matches[1] . strtolower($matches[2]);
915
+ }
916
+
917
+ private function lowercaseProperties($matches)
918
+ {
919
+ return $matches[1] . strtolower($matches[2]) . $matches[3];
920
+ }
921
+
922
+ // ---------------------------------------------------------------------------------------------
923
+ // HELPERS
924
+ // ---------------------------------------------------------------------------------------------
925
+
926
+ /**
927
+ * Clamps a number between a minimum and a maximum value.
928
+ * @param int|float $n the number to clamp
929
+ * @param int|float $min the lower end number allowed
930
+ * @param int|float $max the higher end number allowed
931
+ * @return int|float
932
+ */
933
+ private function clampNumber($n, $min, $max)
934
+ {
935
+ return min(max($n, $min), $max);
936
+ }
937
+
938
+ /**
939
+ * Clamps a RGB color number outside the sRGB color space
940
+ * @param int|float $n the number to clamp
941
+ * @return int|float
942
+ */
943
+ private function clampNumberSrgb($n)
944
+ {
945
+ return $this->clampNumber($n, 0, 255);
946
+ }
947
+
948
+ /**
949
+ * Escapes backreferences such as \1 and $1 in a regular expression replacement string
950
+ * @param $string
951
+ * @return string
952
+ */
953
+ private function escapeReplacementString($string)
954
+ {
955
+ return addcslashes($string, '\\$');
956
+ }
957
+
958
+ /**
959
+ * Converts a HSL color into a RGB color
960
+ * @param array $hslValues
961
+ * @return array
962
+ */
963
+ private function hslToRgb($hslValues)
964
+ {
965
+ $h = floatval($hslValues[0]);
966
+ $s = floatval(str_replace('%', '', $hslValues[1]));
967
+ $l = floatval(str_replace('%', '', $hslValues[2]));
968
+
969
+ // Wrap and clamp, then fraction!
970
+ $h = ((($h % 360) + 360) % 360) / 360;
971
+ $s = $this->clampNumber($s, 0, 100) / 100;
972
+ $l = $this->clampNumber($l, 0, 100) / 100;
973
+
974
+ if ($s == 0) {
975
+ $r = $g = $b = $this->roundNumber(255 * $l);
976
+ } else {
977
+ $v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
978
+ $v1 = (2 * $l) - $v2;
979
+ $r = $this->roundNumber(255 * $this->hueToRgb($v1, $v2, $h + (1/3)));
980
+ $g = $this->roundNumber(255 * $this->hueToRgb($v1, $v2, $h));
981
+ $b = $this->roundNumber(255 * $this->hueToRgb($v1, $v2, $h - (1/3)));
982
+ }
983
+
984
+ return array($r, $g, $b);
985
+ }
986
+
987
+ /**
988
+ * Tests and selects the correct formula for each RGB color channel
989
+ * @param $v1
990
+ * @param $v2
991
+ * @param $vh
992
+ * @return mixed
993
+ */
994
+ private function hueToRgb($v1, $v2, $vh)
995
+ {
996
+ $vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
997
+
998
+ if ($vh * 6 < 1) {
999
+ return $v1 + ($v2 - $v1) * 6 * $vh;
1000
+ }
1001
+
1002
+ if ($vh * 2 < 1) {
1003
+ return $v2;
1004
+ }
1005
+
1006
+ if ($vh * 3 < 2) {
1007
+ return $v1 + ($v2 - $v1) * ((2 / 3) - $vh) * 6;
1008
+ }
1009
+
1010
+ return $v1;
1011
+ }
1012
+
1013
+ /**
1014
+ * PHP port of Javascript's "indexOf" function for strings only
1015
+ * Author: Tubal Martin
1016
+ *
1017
+ * @param string $haystack
1018
+ * @param string $needle
1019
+ * @param int $offset index (optional)
1020
+ * @return int
1021
+ */
1022
+ private function indexOf($haystack, $needle, $offset = 0)
1023
+ {
1024
+ $index = strpos($haystack, $needle, $offset);
1025
+
1026
+ return ($index !== false) ? $index : -1;
1027
+ }
1028
+
1029
+ /**
1030
+ * Convert strings like "64M" or "30" to int values
1031
+ * @param mixed $size
1032
+ * @return int
1033
+ */
1034
+ private function normalizeInt($size)
1035
+ {
1036
+ if (is_string($size)) {
1037
+ $letter = substr($size, -1);
1038
+ $size = intval($size);
1039
+ switch ($letter) {
1040
+ case 'M':
1041
+ case 'm':
1042
+ return (int) $size * 1048576;
1043
+ case 'K':
1044
+ case 'k':
1045
+ return (int) $size * 1024;
1046
+ case 'G':
1047
+ case 'g':
1048
+ return (int) $size * 1073741824;
1049
+ }
1050
+ }
1051
+ return (int) $size;
1052
+ }
1053
+
1054
+ /**
1055
+ * Converts a string containing and RGB percentage value into a RGB integer value i.e. '90%' -> 229.5
1056
+ * @param $rgbPercentage
1057
+ * @return int
1058
+ */
1059
+ private function rgbPercentageToRgbInteger($rgbPercentage)
1060
+ {
1061
+ if (strpos($rgbPercentage, '%') !== false) {
1062
+ $rgbPercentage = $this->roundNumber(floatval(str_replace('%', '', $rgbPercentage)) * 2.55);
1063
+ }
1064
+
1065
+ return intval($rgbPercentage, 10);
1066
+ }
1067
+
1068
+ /**
1069
+ * Rounds a number to its closest integer
1070
+ * @param $n
1071
+ * @return int
1072
+ */
1073
+ private function roundNumber($n)
1074
+ {
1075
+ return intval(round(floatval($n)), 10);
1076
+ }
1077
+
1078
+ /**
1079
+ * PHP port of Javascript's "slice" function for strings only
1080
+ * Author: Tubal Martin
1081
+ *
1082
+ * @param string $str
1083
+ * @param int $start index
1084
+ * @param int|bool $end index (optional)
1085
+ * @return string
1086
+ */
1087
+ private function strSlice($str, $start = 0, $end = false)
1088
+ {
1089
+ if ($end !== false && ($start < 0 || $end <= 0)) {
1090
+ $max = strlen($str);
1091
+
1092
+ if ($start < 0) {
1093
+ if (($start = $max + $start) < 0) {
1094
+ return '';
1095
+ }
1096
+ }
1097
+
1098
+ if ($end < 0) {
1099
+ if (($end = $max + $end) < 0) {
1100
+ return '';
1101
+ }
1102
+ }
1103
+
1104
+ if ($end <= $start) {
1105
+ return '';
1106
+ }
1107
+ }
1108
+
1109
+ $slice = ($end === false) ? substr($str, $start) : substr($str, $start, $end - $start);
1110
+ return ($slice === false) ? '' : $slice;
1111
+ }
1112
+ }
classes/external/php/yui-php-cssmin-2.4.8-p10/data/hex-to-named-color-map.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Hex colors longer than named counterpart
4
+ return array(
5
+ '#f0ffff' => 'azure',
6
+ '#f5f5dc' => 'beige',
7
+ '#ffe4c4' => 'bisque',
8
+ '#a52a2a' => 'brown',
9
+ '#ff7f50' => 'coral',
10
+ '#ffd700' => 'gold',
11
+ '#808080' => 'gray',
12
+ '#008000' => 'green',
13
+ '#4b0082' => 'indigo',
14
+ '#fffff0' => 'ivory',
15
+ '#f0e68c' => 'khaki',
16
+ '#faf0e6' => 'linen',
17
+ '#800000' => 'maroon',
18
+ '#000080' => 'navy',
19
+ '#fdf5e6' => 'oldlace',
20
+ '#808000' => 'olive',
21
+ '#ffa500' => 'orange',
22
+ '#da70d6' => 'orchid',
23
+ '#cd853f' => 'peru',
24
+ '#ffc0cb' => 'pink',
25
+ '#dda0dd' => 'plum',
26
+ '#800080' => 'purple',
27
+ '#f00' => 'red',
28
+ '#fa8072' => 'salmon',
29
+ '#a0522d' => 'sienna',
30
+ '#c0c0c0' => 'silver',
31
+ '#fffafa' => 'snow',
32
+ '#d2b48c' => 'tan',
33
+ '#008080' => 'teal',
34
+ '#ff6347' => 'tomato',
35
+ '#ee82ee' => 'violet',
36
+ '#f5deb3' => 'wheat'
37
+ );
classes/external/php/yui-php-cssmin-2.4.8-p10/data/named-to-hex-color-map.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Named colors longer than hex counterpart
4
+ return array(
5
+ 'aliceblue' => '#f0f8ff',
6
+ 'antiquewhite' => '#faebd7',
7
+ 'aquamarine' => '#7fffd4',
8
+ 'black' => '#000',
9
+ 'blanchedalmond' => '#ffebcd',
10
+ 'blueviolet' => '#8a2be2',
11
+ 'burlywood' => '#deb887',
12
+ 'cadetblue' => '#5f9ea0',
13
+ 'chartreuse' => '#7fff00',
14
+ 'chocolate' => '#d2691e',
15
+ 'cornflowerblue' => '#6495ed',
16
+ 'cornsilk' => '#fff8dc',
17
+ 'darkblue' => '#00008b',
18
+ 'darkcyan' => '#008b8b',
19
+ 'darkgoldenrod' => '#b8860b',
20
+ 'darkgray' => '#a9a9a9',
21
+ 'darkgreen' => '#006400',
22
+ 'darkgrey' => '#a9a9a9',
23
+ 'darkkhaki' => '#bdb76b',
24
+ 'darkmagenta' => '#8b008b',
25
+ 'darkolivegreen' => '#556b2f',
26
+ 'darkorange' => '#ff8c00',
27
+ 'darkorchid' => '#9932cc',
28
+ 'darksalmon' => '#e9967a',
29
+ 'darkseagreen' => '#8fbc8f',
30
+ 'darkslateblue' => '#483d8b',
31
+ 'darkslategray' => '#2f4f4f',
32
+ 'darkslategrey' => '#2f4f4f',
33
+ 'darkturquoise' => '#00ced1',
34
+ 'darkviolet' => '#9400d3',
35
+ 'deeppink' => '#ff1493',
36
+ 'deepskyblue' => '#00bfff',
37
+ 'dodgerblue' => '#1e90ff',
38
+ 'firebrick' => '#b22222',
39
+ 'floralwhite' => '#fffaf0',
40
+ 'forestgreen' => '#228b22',
41
+ 'fuchsia' => '#f0f',
42
+ 'gainsboro' => '#dcdcdc',
43
+ 'ghostwhite' => '#f8f8ff',
44
+ 'goldenrod' => '#daa520',
45
+ 'greenyellow' => '#adff2f',
46
+ 'honeydew' => '#f0fff0',
47
+ 'indianred' => '#cd5c5c',
48
+ 'lavender' => '#e6e6fa',
49
+ 'lavenderblush' => '#fff0f5',
50
+ 'lawngreen' => '#7cfc00',
51
+ 'lemonchiffon' => '#fffacd',
52
+ 'lightblue' => '#add8e6',
53
+ 'lightcoral' => '#f08080',
54
+ 'lightcyan' => '#e0ffff',
55
+ 'lightgoldenrodyellow' => '#fafad2',
56
+ 'lightgray' => '#d3d3d3',
57
+ 'lightgreen' => '#90ee90',
58
+ 'lightgrey' => '#d3d3d3',
59
+ 'lightpink' => '#ffb6c1',
60
+ 'lightsalmon' => '#ffa07a',
61
+ 'lightseagreen' => '#20b2aa',
62
+ 'lightskyblue' => '#87cefa',
63
+ 'lightslategray' => '#778899',
64
+ 'lightslategrey' => '#778899',
65
+ 'lightsteelblue' => '#b0c4de',
66
+ 'lightyellow' => '#ffffe0',
67
+ 'limegreen' => '#32cd32',
68
+ 'mediumaquamarine' => '#66cdaa',
69
+ 'mediumblue' => '#0000cd',
70
+ 'mediumorchid' => '#ba55d3',
71
+ 'mediumpurple' => '#9370db',
72
+ 'mediumseagreen' => '#3cb371',
73
+ 'mediumslateblue' => '#7b68ee',
74
+ 'mediumspringgreen' => '#00fa9a',
75
+ 'mediumturquoise' => '#48d1cc',
76
+ 'mediumvioletred' => '#c71585',
77
+ 'midnightblue' => '#191970',
78
+ 'mintcream' => '#f5fffa',
79
+ 'mistyrose' => '#ffe4e1',
80
+ 'moccasin' => '#ffe4b5',
81
+ 'navajowhite' => '#ffdead',
82
+ 'olivedrab' => '#6b8e23',
83
+ 'orangered' => '#ff4500',
84
+ 'palegoldenrod' => '#eee8aa',
85
+ 'palegreen' => '#98fb98',
86
+ 'paleturquoise' => '#afeeee',
87
+ 'palevioletred' => '#db7093',
88
+ 'papayawhip' => '#ffefd5',
89
+ 'peachpuff' => '#ffdab9',
90
+ 'powderblue' => '#b0e0e6',
91
+ 'rebeccapurple' => '#663399',
92
+ 'rosybrown' => '#bc8f8f',
93
+ 'royalblue' => '#4169e1',
94
+ 'saddlebrown' => '#8b4513',
95
+ 'sandybrown' => '#f4a460',
96
+ 'seagreen' => '#2e8b57',
97
+ 'seashell' => '#fff5ee',
98
+ 'slateblue' => '#6a5acd',
99
+ 'slategray' => '#708090',
100
+ 'slategrey' => '#708090',
101
+ 'springgreen' => '#00ff7f',
102
+ 'steelblue' => '#4682b4',
103
+ 'turquoise' => '#40e0d0',
104
+ 'white' => '#fff',
105
+ 'whitesmoke' => '#f5f5f5',
106
+ 'yellow' => '#ff0',
107
+ 'yellowgreen' => '#9acd32'
108
+ );
classes/static/toolbar.js CHANGED
@@ -1,13 +1,12 @@
1
  jQuery( document ).ready(function()
2
  {
3
-
4
  var percentage = jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar' ).attr('percentage');
5
  var rotate = percentage * 1.8;
6
 
7
  jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .mask.full, #wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill' ).css({
8
- '-webkit-transform' : 'rotate(' + rotate + 'deg)',
9
- '-ms-transform' : 'rotate(' + rotate + 'deg)',
10
- 'transform' : 'rotate(' + rotate + 'deg)'
11
  });
12
 
13
  // Fix Background color of circle percentage & delete cache to fit with the current color theme
@@ -30,34 +29,45 @@ jQuery( document ).ready(function()
30
 
31
  // Create and Show the Autoptimize Loading Modal
32
  var modal_loading = jQuery( '<div class="autoptimize-loading"></div>' ).appendTo( 'body' ).show();
33
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  jQuery.ajax({
35
- type : 'GET',
36
- url : autoptimize_ajax_object.ajaxurl,
37
- data : {'action':action, 'nonce':autoptimize_ajax_object.nonce},
38
- dataType: 'json',
39
- cache : false,
40
- timeout : 5000,
41
- success : function( data )
42
  {
43
  // Remove the Autoptimize Loading Modal
44
  modal_loading.remove();
45
-
46
- // Reset output values & class names of cache info
47
- jQuery( '#wp-admin-bar-autoptimize-cache-info .size' ).attr( 'class', 'size green' ).html( '0.00 B' );
48
- jQuery( '#wp-admin-bar-autoptimize-cache-info .files' ).html( '0' );
49
- jQuery( '#wp-admin-bar-autoptimize-cache-info .percentage .numbers' ).attr( 'class', 'numbers green' ).html( '0%' );
50
- jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill' ).attr( 'class', 'fill bg-green' );
51
-
52
- // Reset the class names of bullet icon
53
- jQuery( '#wp-admin-bar-autoptimize' ).attr( 'class', 'menupop bullet-green' );
54
-
55
- // Reset the Radial Bar progress
56
- jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .mask.full, #wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill' ).css({
57
- '-webkit-transform' : 'rotate(0deg)',
58
- '-ms-transform' : 'rotate(0deg)',
59
- 'transform' : 'rotate(0deg)'
60
- });
61
  },
62
  error: function( jqXHR, textStatus )
63
  {
@@ -65,8 +75,7 @@ jQuery( document ).ready(function()
65
  modal_loading.remove();
66
 
67
  // WordPress Admin Notice
68
- jQuery( '<div id="ao-delete-cache-timeout" class="notice notice-error is-dismissible"><p><strong><span style="display:block;clear:both;">' + autoptimize_ajax_object.error_msg + '</span></strong></p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' + autoptimize_ajax_object.dismiss_msg + '</span></button></div><br>' ).insertAfter( '#wpbody .wrap h1:first-of-type' ).show();
69
-
70
  }
71
  });
72
  });
1
  jQuery( document ).ready(function()
2
  {
 
3
  var percentage = jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar' ).attr('percentage');
4
  var rotate = percentage * 1.8;
5
 
6
  jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .mask.full, #wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill' ).css({
7
+ '-webkit-transform' : 'rotate(' + rotate + 'deg)',
8
+ '-ms-transform' : 'rotate(' + rotate + 'deg)',
9
+ 'transform' : 'rotate(' + rotate + 'deg)'
10
  });
11
 
12
  // Fix Background color of circle percentage & delete cache to fit with the current color theme
29
 
30
  // Create and Show the Autoptimize Loading Modal
31
  var modal_loading = jQuery( '<div class="autoptimize-loading"></div>' ).appendTo( 'body' ).show();
32
+
33
+ var success = function() {
34
+ // Reset output values & class names of cache info
35
+ jQuery( '#wp-admin-bar-autoptimize-cache-info .size' ).attr( 'class', 'size green' ).html( '0.00 B' );
36
+ jQuery( '#wp-admin-bar-autoptimize-cache-info .files' ).html( '0' );
37
+ jQuery( '#wp-admin-bar-autoptimize-cache-info .percentage .numbers' ).attr( 'class', 'numbers green' ).html( '0%' );
38
+ jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill' ).attr( 'class', 'fill bg-green' );
39
+
40
+ // Reset the class names of bullet icon
41
+ jQuery( '#wp-admin-bar-autoptimize' ).attr( 'class', 'menupop bullet-green' );
42
+
43
+ // Reset the Radial Bar progress
44
+ jQuery( '#wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .mask.full, #wp-admin-bar-autoptimize-cache-info .autoptimize-radial-bar .fill' ).css({
45
+ '-webkit-transform' : 'rotate(0deg)',
46
+ '-ms-transform' : 'rotate(0deg)',
47
+ 'transform' : 'rotate(0deg)'
48
+ });
49
+ };
50
+
51
+ var notice = function() {
52
+ jQuery( '<div id="ao-delete-cache-timeout" class="notice notice-error is-dismissible"><p><strong><span style="display:block;clear:both;">' + autoptimize_ajax_object.error_msg + '</span></strong></p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' + autoptimize_ajax_object.dismiss_msg + '</span></button></div><br>' ).insertAfter( '#wpbody .wrap h1:first-of-type' ).show();
53
+ };
54
+
55
  jQuery.ajax({
56
+ type : 'GET',
57
+ url : autoptimize_ajax_object.ajaxurl,
58
+ data : {'action':action, 'nonce':autoptimize_ajax_object.nonce},
59
+ dataType : 'json',
60
+ cache : false,
61
+ timeout : 5000,
62
+ success : function( cleared )
63
  {
64
  // Remove the Autoptimize Loading Modal
65
  modal_loading.remove();
66
+ if ( cleared ) {
67
+ success();
68
+ } else {
69
+ notice();
70
+ }
 
 
 
 
 
 
 
 
 
 
 
71
  },
72
  error: function( jqXHR, textStatus )
73
  {
75
  modal_loading.remove();
76
 
77
  // WordPress Admin Notice
78
+ notice();
 
79
  }
80
  });
81
  });
classlesses/autoptimizeCacheChecker.php CHANGED
@@ -39,10 +39,11 @@ function ao_cachechecker_cronjob() {
39
  if (($cacheSize>$maxSize) && ($doCacheCheck)) {
40
  update_option("autoptimize_cachesize_notice",true);
41
  if (apply_filters('autoptimize_filter_cachecheck_sendmail',true)) {
 
42
  $ao_mailto=apply_filters('autoptimize_filter_cachecheck_mailto',get_option('admin_email',''));
43
- $ao_mailsubject=__('Autoptimize cache size warning','autoptimize');
44
- $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');
45
-
46
  if (!empty($ao_mailto)) {
47
  $ao_mailresult=wp_mail($ao_mailto,$ao_mailsubject,$ao_mailbody);
48
  if (!$ao_mailresult) {
39
  if (($cacheSize>$maxSize) && ($doCacheCheck)) {
40
  update_option("autoptimize_cachesize_notice",true);
41
  if (apply_filters('autoptimize_filter_cachecheck_sendmail',true)) {
42
+ $saniSiteUrl=esc_url(site_url());
43
  $ao_mailto=apply_filters('autoptimize_filter_cachecheck_mailto',get_option('admin_email',''));
44
+ $ao_mailsubject=__('Autoptimize cache size warning','autoptimize')." (".$saniSiteUrl.")";
45
+ $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: ".$saniSiteUrl.")";
46
+
47
  if (!empty($ao_mailto)) {
48
  $ao_mailresult=wp_mail($ao_mailto,$ao_mailsubject,$ao_mailbody);
49
  if (!$ao_mailresult) {
classlesses/autoptimizeFontRegex.php DELETED
@@ -1,7 +0,0 @@
1
- <?php
2
- // regex to find fonts, externalised to avoid nasty errors for php<5.3
3
-
4
- $fonturl_regex = <<<'LOD'
5
- ~(?(DEFINE)(?<quoted_content>(["']) (?>[^"'\\]++ | \\{2} | \\. | (?!\g{-1})["'] )*+ \g{-1})(?<comment> /\* .*? \*/ ) (?<url_skip>(?: data: ) [^"'\s)}]*+ ) (?<other_content>(?> [^u}/"']++ | \g<quoted_content> | \g<comment> | \Bu | u(?!rl\s*+\() | /(?!\*) | \g<url_start> \g<url_skip> ["']?+ )++ ) (?<anchor> \G(?<!^) ["']?+ | @font-face \s*+ { ) (?<url_start> url\( \s*+ ["']?+ ) ) \g<comment> (*SKIP)(*FAIL) | \g<anchor> \g<other_content>?+ \g<url_start> \K ((?:(?:https?:)?(?://[[:alnum:]\-\.]+)(?::[0-9]+)?)?\/[^"'\s)}]*+) ~xs
6
- LOD;
7
- ?>
 
 
 
 
 
 
 
classlesses/autoptimizePageCacheFlush.php CHANGED
@@ -2,6 +2,8 @@
2
  // flush as many page cache plugin's caches as possible
3
  // hyper cache and gator cache hook into AO, so we don't need to :-)
4
 
 
 
5
  function autoptimize_flush_pagecache() {
6
  if(function_exists('wp_cache_clear_cache')) {
7
  if (is_multisite()) {
@@ -37,6 +39,8 @@ function autoptimize_flush_pagecache() {
37
  if ( method_exists( "WpeCommon", "purge_varnish_cache" ) ) {
38
  WpeCommon::purge_varnish_cache();
39
  }
 
 
40
  } else if(file_exists(WP_CONTENT_DIR.'/wp-cache-config.php') && function_exists('prune_super_cache')){
41
  // fallback for WP-Super-Cache
42
  global $cache_path;
2
  // flush as many page cache plugin's caches as possible
3
  // hyper cache and gator cache hook into AO, so we don't need to :-)
4
 
5
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
6
+
7
  function autoptimize_flush_pagecache() {
8
  if(function_exists('wp_cache_clear_cache')) {
9
  if (is_multisite()) {
39
  if ( method_exists( "WpeCommon", "purge_varnish_cache" ) ) {
40
  WpeCommon::purge_varnish_cache();
41
  }
42
+ } else if ( function_exists('sg_cachepress_purge_cache') ) {
43
+ sg_cachepress_purge_cache();
44
  } else if(file_exists(WP_CONTENT_DIR.'/wp-cache-config.php') && function_exists('prune_super_cache')){
45
  // fallback for WP-Super-Cache
46
  global $cache_path;
classlesses/autoptimizePartners.php CHANGED
@@ -3,6 +3,8 @@
3
  Classlessly add a "more tools" tab to promote (future) AO addons and/ or affiliate services
4
  */
5
 
 
 
6
  add_action('admin_init', 'ao_partner_tabs_preinit');
7
  function ao_partner_tabs_preinit() {
8
  if (apply_filters('autoptimize_filter_show_partner_tabs',true)) {
@@ -29,7 +31,7 @@ function ao_partners() {
29
  .itemDetail {
30
  background: #fff;
31
  width: 250px;
32
- min-height: 270px;
33
  border: 1px solid #ccc;
34
  float: left;
35
  padding: 15px;
@@ -72,7 +74,7 @@ function ao_partners() {
72
  <h1><?php _e('Autoptimize Settings','autoptimize'); ?></h1>
73
  <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
74
  <?php
75
- _e("<h2>These Autoptimize power-ups and related services will improve your site's performance even more!</h2>","autoptimize");
76
  ?>
77
  <div>
78
  <?php getAOPartnerFeed(); ?>
@@ -117,4 +119,4 @@ function getAOPartnerFeed() {
117
  } else {
118
  echo $noFeedText;
119
  }
120
- }
3
  Classlessly add a "more tools" tab to promote (future) AO addons and/ or affiliate services
4
  */
5
 
6
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
7
+
8
  add_action('admin_init', 'ao_partner_tabs_preinit');
9
  function ao_partner_tabs_preinit() {
10
  if (apply_filters('autoptimize_filter_show_partner_tabs',true)) {
31
  .itemDetail {
32
  background: #fff;
33
  width: 250px;
34
+ min-height: 290px;
35
  border: 1px solid #ccc;
36
  float: left;
37
  padding: 15px;
74
  <h1><?php _e('Autoptimize Settings','autoptimize'); ?></h1>
75
  <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
76
  <?php
77
+ echo '<h2>'. __("These Autoptimize power-ups and related services will improve your site's performance even more!",'autoptimize') . '</h2>';
78
  ?>
79
  <div>
80
  <?php getAOPartnerFeed(); ?>
119
  } else {
120
  echo $noFeedText;
121
  }
122
+ }
classlesses/autoptimizeSpeedupper.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Autoptimize SpeedUp; minify & cache each JS/ CSS separately
4
+ * new in Autoptimize 2.2
5
+ */
6
+
7
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
8
+
9
+ function ao_js_snippetcache($jsin,$jsfilename) {
10
+ $md5hash = "snippet_".md5($jsin);
11
+ $ccheck = new autoptimizeCache($md5hash,'js');
12
+ if($ccheck->check()) {
13
+ $scriptsrc = $ccheck->retrieve();
14
+ } else {
15
+ if ( (strpos($jsfilename,"min.js") === false) && ( strpos($jsfilename,"js/jquery/jquery.js") === false ) && ( str_replace(apply_filters('autoptimize_filter_js_consider_minified',false), '', $jsfilename) === $jsfilename ) ) {
16
+ if(class_exists('JSMin')) {
17
+ $tmp_jscode = trim(JSMin::minify($jsin));
18
+ if (!empty($tmp_jscode)) {
19
+ $scriptsrc = $tmp_jscode;
20
+ unset($tmp_jscode);
21
+ } else {
22
+ $scriptsrc=$jsin;
23
+ }
24
+ } else {
25
+ $scriptsrc=$jsin;
26
+ }
27
+ } else {
28
+ // do some housekeeping here to remove comments & linebreaks and stuff
29
+ $scriptsrc=preg_replace("#^\s*\/\/.*$#Um","",$jsin);
30
+ $scriptsrc=preg_replace("#^\s*\/\*[^!].*\*\/\s?#Us","",$scriptsrc);
31
+ $scriptsrc=preg_replace("#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $scriptsrc);
32
+
33
+ if ((substr($scriptsrc,-1,1)!==";")&&(substr($scriptsrc,-1,1)!=="}")) {
34
+ $scriptsrc.=";";
35
+ }
36
+ }
37
+ if ( !empty($jsfilename) && str_replace( apply_filters('autoptimize_filter_js_speedup_cache',false), '', $jsfilename ) === $jsfilename ) {
38
+ // don't cache inline CSS or if filter says no
39
+ $ccheck->cache($scriptsrc,'text/javascript');
40
+ }
41
+ }
42
+ unset($ccheck);
43
+
44
+ if (get_option("autoptimize_js_trycatch")==="on") {
45
+ $scriptsrc="try{".$scriptsrc."}catch(e){}";
46
+ }
47
+
48
+ return $scriptsrc;
49
+ }
50
+
51
+ function ao_css_snippetcache($cssin,$cssfilename) {
52
+ $md5hash = "snippet_".md5($cssin);
53
+ $ccheck = new autoptimizeCache($md5hash,'css');
54
+ if($ccheck->check()) {
55
+ $stylesrc = $ccheck->retrieve();
56
+ } else {
57
+ if ( ( strpos($cssfilename,"min.css") === false ) && ( str_replace( apply_filters('autoptimize_filter_css_consider_minified',false), '', $cssfilename ) === $cssfilename ) ) {
58
+ if (class_exists('Minify_CSS_Compressor')) {
59
+ $tmp_code = trim(Minify_CSS_Compressor::process($cssin));
60
+ } else if(class_exists('CSSmin')) {
61
+ $cssmin = new CSSmin();
62
+ if (method_exists($cssmin,"run")) {
63
+ $tmp_code = trim($cssmin->run($cssin));
64
+ } elseif (@is_callable(array($cssmin,"minify"))) {
65
+ $tmp_code = trim(CssMin::minify($cssin));
66
+ }
67
+ }
68
+
69
+ if (!empty($tmp_code)) {
70
+ $stylesrc = $tmp_code;
71
+ unset($tmp_code);
72
+ } else {
73
+ $stylesrc = $cssin;
74
+ }
75
+ } else {
76
+ // .min.css -> no heavy-lifting, just some cleanup
77
+ $stylesrc=preg_replace("#^\s*\/\*[^!].*\*\/\s?#Us","",$cssin);
78
+ $stylesrc=preg_replace("#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $stylesrc);
79
+ $stylesrc=autoptimizeStyles::fixurls($cssfilename,$stylesrc);
80
+ }
81
+ if ( !empty($cssfilename) && ( str_replace( apply_filters('autoptimize_filter_css_speedup_cache',false), '', $cssfilename ) === $cssfilename ) ) {
82
+ // only cache CSS if not inline and allowed by filter
83
+ $ccheck->cache($stylesrc,'text/css');
84
+ }
85
+ }
86
+ unset($ccheck);
87
+ return $stylesrc;
88
+ }
89
+
90
+ function ao_css_speedup_cleanup($cssin) {
91
+ // speedupper results in aggregated CSS not being minified, so the filestart-marker AO adds when aggregating need to be removed
92
+ return trim(str_replace(array('/*FILESTART*/','/*FILESTART2*/'),'',$cssin));
93
+ }
94
+
95
+ function ao_js_speedup_cleanup($jsin) {
96
+ // cleanup
97
+ return trim($jsin);
98
+ }
99
+
100
+ add_filter('autoptimize_css_individual_style','ao_css_snippetcache',10,2);
101
+ add_filter('autoptimize_js_individual_script','ao_js_snippetcache',10,2);
102
+ add_filter('autoptimize_css_after_minify','ao_css_speedup_cleanup',10,1);
103
+ add_filter('autoptimize_js_after_minify','ao_js_speedup_cleanup',10,1);
classlesses/autoptimizeUpdateCode.php CHANGED
@@ -3,6 +3,8 @@
3
  * below code handles updates and is only included by autoptimize.php if/ when needed
4
  */
5
 
 
 
6
  $majorUp = false;
7
  $autoptimize_major_version=substr($autoptimize_db_version,0,3);
8
 
3
  * below code handles updates and is only included by autoptimize.php if/ when needed
4
  */
5
 
6
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
7
+
8
  $majorUp = false;
9
  $autoptimize_major_version=substr($autoptimize_db_version,0,3);
10
 
config/default.php CHANGED
@@ -54,7 +54,7 @@ if (($modTimeMatch)||($eTagMatch)) {
54
  header('Vary: Accept-Encoding');
55
  header('Content-Length: '.strlen($contents));
56
  header('Content-type: %%CONTENT%%; charset=utf-8');
57
- header('Cache-Control: max-age='.$expireTime.', public, must-revalidate');
58
  header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expireTime).' GMT'); //10 years
59
  header('ETag: ' . $eTag);
60
  header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modTime).' GMT');
54
  header('Vary: Accept-Encoding');
55
  header('Content-Length: '.strlen($contents));
56
  header('Content-type: %%CONTENT%%; charset=utf-8');
57
+ header('Cache-Control: max-age='.$expireTime.', public, immutable');
58
  header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expireTime).' GMT'); //10 years
59
  header('ETag: ' . $eTag);
60
  header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modTime).' GMT');
config/delayed.php CHANGED
@@ -62,7 +62,7 @@ if (($modTimeMatch)||($eTagMatch)) {
62
  header('Vary: Accept-Encoding');
63
  header('Content-Length: '.strlen($contents));
64
  header('Content-type: %%CONTENT%%; charset=utf-8');
65
- header('Cache-Control: max-age='.$expireTime.', public, must-revalidate');
66
  header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expireTime).' GMT');
67
  header('ETag: ' . $eTag);
68
  header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modTime).' GMT');
62
  header('Vary: Accept-Encoding');
63
  header('Content-Length: '.strlen($contents));
64
  header('Content-type: %%CONTENT%%; charset=utf-8');
65
+ header('Cache-Control: max-age='.$expireTime.', public, immutable');
66
  header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expireTime).' GMT');
67
  header('ETag: ' . $eTag);
68
  header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modTime).' GMT');
readme.txt CHANGED
@@ -1,18 +1,21 @@
1
  === Autoptimize ===
2
- Contributors: futtta, turl, optimizingmatters
3
- Tags: minify, css, html, javascript, js, optimize, speed, cache, aggregate, minification, performance, pagespeed
4
  Donate link: http://blog.futtta.be/2013/10/21/do-not-donate-to-me/
5
  Requires at least: 4.0
6
  Tested up to: 4.8
7
- Stable tag: 2.1.0
8
 
9
  Autoptimize speeds up your website and helps you save bandwidth by aggregating and minimizing JS, CSS and HTML.
10
 
11
  == Description ==
12
 
13
- Autoptimize makes optimizing your site really easy. It concatenates all scripts and styles, minifies and compresses them, adds expires headers, caches them, and moves styles to the page head and can move scripts to the footer. It also minifies the HTML code itself, making your page really lightweight. There are advanced options and an extensive API available to enable you to tailor Autoptimize to each and every site's specific needs.
14
 
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/extend/plugins/wp-super-cache/), [HyperCache](http://wordpress.org/extend/plugins/hyper-cache/), [Comet Cache](https://wordpress.org/plugins/comet-cache/) or [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler).
 
 
 
16
 
17
  (Speed-surfing image under creative commons [by LL Twistiti](https://www.flickr.com/photos/twistiti/818552808/))
18
 
@@ -20,7 +23,7 @@ If you consider performance important, you really should use one of the many cac
20
 
21
  Just install from your WordPress "Plugins > Add New" screen and all will be well. Manual installation is very straightforward as well:
22
 
23
- 1. Upload the zip file and unzip it in the `/wp-content/plugins/` directory.
24
  1. Activate the plugin through the 'Plugins' menu in WordPress
25
  1. Go to `Settings > Autoptimize` and enable the options you want. Generally this means "Optimize HTML/ CSS/ JavaScript".
26
 
@@ -30,10 +33,18 @@ Just install from your WordPress "Plugins > Add New" screen and all will be well
30
 
31
  It concatenates all scripts and styles, minifies and compresses them, adds expires headers, caches them, and moves styles to the page head, and scripts (optionally) to the footer. It also minifies the HTML code itself, making your page really lightweight.
32
 
 
 
 
 
33
  = Will this work with my blog? =
34
 
35
  Although Autoptimize comes without any warranties, it will in general work flawlessly if you configure it correctly. See "Troubleshooting" below for info on how to configure in case of problems.
36
 
 
 
 
 
37
  = What is the use of "inline and defer CSS"? =
38
 
39
  CSS in general should go in the head of the document. Recently a.o. Google started promoting deferring non-essential CSS, while inlining those styles needed to build the page above the fold. This is especially important to render pages as quickly as possible on mobile devices. As from Autoptimize 1.9.0 this is easy; select "inline and defer CSS", paste the block of "above the fold CSS" in the input field (text area) and you're good to go!
@@ -44,7 +55,7 @@ There's no easy solution for that as "above the fold" depends on where the fold
44
 
45
  = Or should you inline all CSS? =
46
 
47
- The short answer: probably not (but I do).
48
 
49
  Back in the days CSS optimization was easy; put all CSS in your head, aggregating everything in one CSS-file per media-type and you were good to go. But ever since Google included mobile in PageSpeed Insights and started complaining about render blocking CSS, things got messy (see "deferring CSS" elsewhere in this FAQ). One of the solutions is inlining all your CSS, which as of Autoptimize 1.8.0 is supported.
50
 
@@ -56,20 +67,18 @@ You can find more information on this topic [in this blog post](http://blog.futt
56
 
57
  = My cache is getting huge, doesn't Autoptimize purge the cache? =
58
 
59
- Autoptimize does not have its proper cache purging mechanism, as this could remove optimized CSS/JS which is still referred to in other caches, which would break your site. As from version 2.0.0 Autoptimize will display a notice on the administration pages if the cache size surpasses the half a Gigabyte mark.
60
 
61
- You can keep the cache size at an acceptable level by either:
62
 
63
  * disactivating the "aggregate inline JS" and/ or "aggregate inline CSS" options
64
  * excluding JS-variables (or sometimes CSS-selectors) that change on a per page (or per pageload) basis. You can read how you can do that [in this blogpost](http://blog.futtta.be/2014/03/19/how-to-keep-autoptimizes-cache-size-under-control-and-improve-visitor-experience/).
65
 
66
- = Where is the "look only in head" option? =
67
-
68
- While "look only in head" still works, it is now (since Autoptimize 2.0.0) no longer visible on the settings-page if it is not active. As long as the option is active (for JS or CSS), it will however remain visible until you deactivate it. If you're comfortable with PHP, there still are filters available to keep on using "look only in head".
69
 
70
  = So should I aggregate inline CSS/ JS? =
71
 
72
- Before Autoptimize 2.0.0, inline code was always optimized with all CSS pushed in the head-section and all JS at the end with a defer-flag. This often caused 2 problems; the priority of inline CSS got lost and inline JS could contain page- or request-specific code which broke Autoptimize's caching mechanism leading to too many cached files and the minification running over and over. This is why as from 2.0.0 by default inline code is not optimized (except for those upgrading from previous versions). Additionally, to avoid inline JS breaking because the optimized JS is not available, JS is forced in head by default. If you want to squeeze out as much performance as possible, you should indeed tick the "aggregate inline"-options and disable "force JS into head", while off course keeping an eye out for the disadvantages listed above.
73
 
74
  = What can I do with the API? =
75
 
@@ -95,6 +104,10 @@ Autoptimize supports this, but it is not enabled by default because [non-local f
95
 
96
  `add_filter('autoptimize_filter_css_fonts_cdn',__return_true);`
97
 
 
 
 
 
98
  = How can I force the aggregated files to be static CSS or JS instead of PHP? =
99
 
100
  If your webserver is properly configured to handle compression (gzip or deflate) and cache expiry (expires and cache-control with sufficient cacheability), you don't need Autoptimize to handle that for you. In that case you can check the "Save aggregated script/css as static files?"-option, which will force Autoptimize to save the aggregated files as .css and .js-files (meaning no PHP is needed to serve these files). This setting is default as of Autoptimize 1.8.
@@ -137,6 +150,7 @@ If you are running Apache, the htaccess file written by Autoptimize can in some
137
  = I get no error, but my pages are not optimized at all? =
138
 
139
  Autoptimize does a number of checks before actually optimizing. When one of the following is true, your pages won't be optimized:
 
140
  * when in the customizer
141
  * if there is no opening `<html` tag
142
  * if there is `<xsl:stylesheet` in the response (indicating the output is not HTML but XML)
@@ -149,47 +163,36 @@ Autoptimize does a number of checks before actually optimizing. When one of the
149
 
150
  = Visual Composer, Beaver Builder and similar page builder solutions are broken!! =
151
 
152
- These page builder plugins run on the frontend for logged in users and are very JavaScript intensive and should not be optimized. You can tell Autoptimize not to act on these page-builder powered pages for logged in users with this code:
153
 
154
- `
155
- add_filter('autoptimize_filter_noptimize','pagebuilder_noptimize',10,0);
156
- function pagebuilder_noptimize() {
157
- if (is_user_logged_in()) {
158
- return true;
159
- } else {
160
- return false;
161
- }
162
- }
163
- `
164
 
165
  = Revolution Slider is broken! =
166
 
167
- You can fix this by adding `js/jquery/jquery.js` to the comma-separated list of JS optimization exclusion.
168
 
169
  = I'm getting "jQuery is not defined" errors =
170
 
171
- In that case you have un-aggregated JavaScript that requires jQuery to be loaded, so you'll have to either aggregate that JavaScript (ticking the "also aggregate inline JS"-option) or add `js/jquery/jquery.js` to the comma-separated list of JS optimization exclusions.
172
 
173
- = My Autoptimized CSS/ JS is broken after upgrading from 1.9.4 to 2.0! =
174
 
175
- One of the bigger changes in Autoptimize 2.0 is that files that have "min.js" or "min.css" in their name are considered already minified and are only injected into the aggregated code after the actual minification, because this has an important performance-benefit. Although this has been tested rather thoroughly, it is possible that this approach does not always work. You can turn this behavior off by hooking into Autoptimize's API, like this;
176
 
177
  `
178
- add_filter('autoptimize_filter_js_inject_min_late',__return_false);
179
- add_filter('autoptimize_filter_css_inject_min_late',__return_false);
180
  `
181
 
182
- Obviously you can choose to do this for only CSS, JS or both (as in example).
183
-
184
  = I use NextGen Galleries and a lot of JS is not aggregated/ minified? =
185
 
186
- NextGen Galleries does some nifty stuff to add JavaScript. In order for Autoptimize to be able to aggregate that, you'll need to tell it to initialize earlier, by adding this to your wp-config.php:
187
- `define("AUTOPTIMIZE_INIT_EARLIER","true");`
188
 
189
  = What is noptimize? =
190
 
191
  Starting with version 1.6.6 Autoptimize excludes everything inside noptimize tags, e.g.:
192
- `<!--noptimize--><script>alert('this will not get autoptimized');</script><!--/noptimize-->`
193
 
194
  You can do this in your page/ post content, in widgets and in your theme files (consider creating [a child theme](http://codex.wordpress.org/Child_Themes) to avoid your work being overwritten by theme updates).
195
 
@@ -205,17 +208,9 @@ define('AUTOPTIMIZE_CACHEFILE_PREFIX','aggregated_');
205
 
206
  Yes, but this is off by default. You can enable this by passing ´true´ to ´autoptimize_filter_cache_create_static_gzip´. You'll obviously still have to configure your webserver to use these files instead of the non-gzipped ones to avoid the overhead of on-the-fly compression.
207
 
208
- = Where can I report an error? =
209
-
210
- You can report problems 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).
211
 
212
- = What information should I include when requesting support =
213
-
214
- * A description of the problem, including screenshots and information from your browser's Error/ debug console
215
- * URL of your blog (you can turn Autoptimize off, but should be willing to turn it briefly on to have the error visible)
216
- * your Autoptimize settings (including a description of changes you made to the configuration to try to troubleshoot yourself)
217
- * the Theme used (including the Theme's download link)
218
- * optionally plugins used (if you suspect one or more plugins are raising havoc)
219
 
220
  = I want out, how should I remove Autoptimize? =
221
 
@@ -229,9 +224,20 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
229
 
230
  == Changelog ==
231
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  = 2.1.1 =
233
- * Fix for problem reported by Matthew Berry
234
- * confirmed working with WordPress 4.8
235
 
236
  = 2.1.0 =
237
  * new: Autoptimize now appears in admin-toolbar with an easy view on cache size and the possibility to purge the cache (pass `false` to `autoptimize_filter_toolbar_show` filter to disable), a big thanks to [Pablo Custo](https://github.com/pablocusto) for his hard work on this nice feature!
1
  === Autoptimize ===
2
+ Contributors: futtta, optimizingmatters, turl
3
+ Tags: css, html, javascript, js, optimize, speed, cache, aggregate, minimize, minification, performance, pagespeed
4
  Donate link: http://blog.futtta.be/2013/10/21/do-not-donate-to-me/
5
  Requires at least: 4.0
6
  Tested up to: 4.8
7
+ Stable tag: 2.2.0
8
 
9
  Autoptimize speeds up your website and helps you save bandwidth by aggregating and minimizing JS, CSS and HTML.
10
 
11
  == Description ==
12
 
13
+ Autoptimize makes optimizing your site really easy. It can aggregate, minify and cache scripts and styles, injects CSS in the page head by default and can move and defer scripts to the footer. It also minifies the HTML code itself, making your page really lightweight. There are advanced options and an extensive API available to enable you to tailor Autoptimize to each and every site's specific needs. And it can even improve your site's performance when on HTTP/2!
14
 
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>
18
+ > We provide great [Autoptimize Pro Support and Web Performance Optimization services](http://autoptimize.com/), check out our offering on (http://autoptimize.com/)!
19
 
20
  (Speed-surfing image under creative commons [by LL Twistiti](https://www.flickr.com/photos/twistiti/818552808/))
21
 
23
 
24
  Just install from your WordPress "Plugins > Add New" screen and all will be well. Manual installation is very straightforward as well:
25
 
26
+ 1. Upload the zip file and unzip it in the `/wp-content/plugins/` directory
27
  1. Activate the plugin through the 'Plugins' menu in WordPress
28
  1. Go to `Settings > Autoptimize` and enable the options you want. Generally this means "Optimize HTML/ CSS/ JavaScript".
29
 
33
 
34
  It concatenates all scripts and styles, minifies and compresses them, adds expires headers, caches them, and moves styles to the page head, and scripts (optionally) to the footer. It also minifies the HTML code itself, making your page really lightweight.
35
 
36
+ = But I'm on HTTP/2, so I don't need Autoptimize? =
37
+
38
+ HTTP/2 is a great step forward for sure, reducing the impact of multiple requests from the same server significantly by using the same connection to perform several concurrent requests. That being said, [concatenation of CSS/ JS can still make a lot of sense](http://engineering.khanacademy.org/posts/js-packaging-http2.htm), as described in [this css-tricks.com article](https://css-tricks.com/http2-real-world-performance-test-analysis/) and this [blogpost from one of the Ebay engineers](http://calendar.perfplanet.com/2015/packaging-for-performance/). The conclusion; configure, test, reconfigure, retest, tweak and look what works best in your context. Maybe it's just HTTP/2, maybe it's HTTP/2 + aggregation and minification, maybe it's HTTP/2 + minification (which AO can do as well).
39
+
40
  = Will this work with my blog? =
41
 
42
  Although Autoptimize comes without any warranties, it will in general work flawlessly if you configure it correctly. See "Troubleshooting" below for info on how to configure in case of problems.
43
 
44
+ = Why are jquery.js and the autoptimized CSS still called out as render blocking? =
45
+
46
+ With the default Autoptimize configuration jquery.js is excluded from optimization and the CSS is linked in the head, which are safe defaults but have Google PageSpeed Insights complaining. You can try removing `js/jquery/jquery.js` from the JS optimization exclusions and check if your site still works. For the render blocking CSS you can look into "inline all CSS" (easy) or "inline and defer CSS" (better) which are explained in this FAQ as well.
47
+
48
  = What is the use of "inline and defer CSS"? =
49
 
50
  CSS in general should go in the head of the document. Recently a.o. Google started promoting deferring non-essential CSS, while inlining those styles needed to build the page above the fold. This is especially important to render pages as quickly as possible on mobile devices. As from Autoptimize 1.9.0 this is easy; select "inline and defer CSS", paste the block of "above the fold CSS" in the input field (text area) and you're good to go!
55
 
56
  = Or should you inline all CSS? =
57
 
58
+ The short answer: probably not.
59
 
60
  Back in the days CSS optimization was easy; put all CSS in your head, aggregating everything in one CSS-file per media-type and you were good to go. But ever since Google included mobile in PageSpeed Insights and started complaining about render blocking CSS, things got messy (see "deferring CSS" elsewhere in this FAQ). One of the solutions is inlining all your CSS, which as of Autoptimize 1.8.0 is supported.
61
 
67
 
68
  = My cache is getting huge, doesn't Autoptimize purge the cache? =
69
 
70
+ Autoptimize does not have its proper cache purging mechanism, as this could remove optimized CSS/JS which is still referred to in other caches, which would break your site. Moreover a fast growing cache is an indication of [other problems you should avoid](http://blog.futtta.be/2016/09/15/autoptimize-cache-size-the-canary-in-the-coal-mine/).
71
 
72
+ Instead you can keep the cache size at an acceptable level by either:
73
 
74
  * disactivating the "aggregate inline JS" and/ or "aggregate inline CSS" options
75
  * excluding JS-variables (or sometimes CSS-selectors) that change on a per page (or per pageload) basis. You can read how you can do that [in this blogpost](http://blog.futtta.be/2014/03/19/how-to-keep-autoptimizes-cache-size-under-control-and-improve-visitor-experience/).
76
 
77
+ Despite above objections, there are 3rd party solutions to automatically purge the AO cache, e.g. using [this code](https://wordpress.org/support/topic/contribution-autoptimize-cache-size-under-control-by-schedule-auto-cache-purge/) or [this plugin](https://wordpress.org/plugins/bi-clean-cache/), but for reasons above these are to be used only if you really know what you're doing.
 
 
78
 
79
  = So should I aggregate inline CSS/ JS? =
80
 
81
+ Before Autoptimize 2.0.0, inline code was always optimized with all CSS pushed in the head-section and all JS at the end with a defer-flag. This often caused 2 problems; the priority of inline CSS got lost and inline JS could contain page- or request-specific code which broke Autoptimize's caching mechanism leading to too many cached files and the minification running over and over. This is why as from AQ 2.0 by default inline code is not optimized (except for those upgrading from previous versions). Additionally, to avoid inline JS breaking because jquery is not available, `js/jquery/jquery.js` is excluded by default.
82
 
83
  = What can I do with the API? =
84
 
104
 
105
  `add_filter('autoptimize_filter_css_fonts_cdn',__return_true);`
106
 
107
+ = I'm using Cloudflare, what should I enter as CDN root directory =
108
+
109
+ Nothing, when on Cloudflare your autoptimized CSS/ JS is on the Cloudflare's CDN automatically.
110
+
111
  = How can I force the aggregated files to be static CSS or JS instead of PHP? =
112
 
113
  If your webserver is properly configured to handle compression (gzip or deflate) and cache expiry (expires and cache-control with sufficient cacheability), you don't need Autoptimize to handle that for you. In that case you can check the "Save aggregated script/css as static files?"-option, which will force Autoptimize to save the aggregated files as .css and .js-files (meaning no PHP is needed to serve these files). This setting is default as of Autoptimize 1.8.
150
  = I get no error, but my pages are not optimized at all? =
151
 
152
  Autoptimize does a number of checks before actually optimizing. When one of the following is true, your pages won't be optimized:
153
+
154
  * when in the customizer
155
  * if there is no opening `<html` tag
156
  * if there is `<xsl:stylesheet` in the response (indicating the output is not HTML but XML)
163
 
164
  = Visual Composer, Beaver Builder and similar page builder solutions are broken!! =
165
 
166
+ Disable the option to have Autoptimize active for logged on users and go crazy dragging and dropping ;-)
167
 
168
+ = Help, my shop checkout/ payment don't work!! =
169
+
170
+ Disable the option to optimize cart/ checkout pages (works for WooCommerce, Easy Digital Downloads and WP eCommerce).
 
 
 
 
 
 
 
171
 
172
  = Revolution Slider is broken! =
173
 
174
+ Make sure `js/jquery/jquery.js` is in the comma-separated list of JS optimization exclusions (this is excluded in the default configuration).
175
 
176
  = I'm getting "jQuery is not defined" errors =
177
 
178
+ In that case you have un-aggregated JavaScript that requires jQuery to be loaded, so you'll have to add `js/jquery/jquery.js` to the comma-separated list of JS optimization exclusions.
179
 
180
+ = My Autoptimized CSS/ JS is broken after upgrading from 2.1 to 2.2! =
181
 
182
+ One of the bigger changes in Autoptimize 2.2 it that it minifies first and aggregates second, which has important performance-benefits. Although this has been tested rather thoroughly, it is possible that this approach does not always work. You can turn this behavior off by hooking into Autoptimize's API, like this;
183
 
184
  `
185
+ add_filter('autoptimize_filter_speedupper','__return_false');
 
186
  `
187
 
 
 
188
  = I use NextGen Galleries and a lot of JS is not aggregated/ minified? =
189
 
190
+ NextGen Galleries does some nifty stuff to add JavaScript. In order for Autoptimize to be able to aggregate that, you can either disable Nextgen Gallery's resourced manage with this code snippet `add_filter( 'run_ngg_resource_manager', '__return_false' );` or you can tell Autoptimize to initialize earlier, by adding this to your wp-config.php: `define("AUTOPTIMIZE_INIT_EARLIER","true");`
 
191
 
192
  = What is noptimize? =
193
 
194
  Starting with version 1.6.6 Autoptimize excludes everything inside noptimize tags, e.g.:
195
+ `&lt;!--noptimize-->&lt;script>alert('this will not get autoptimized');&lt;/script>&lt;!--/noptimize-->`
196
 
197
  You can do this in your page/ post content, in widgets and in your theme files (consider creating [a child theme](http://codex.wordpress.org/Child_Themes) to avoid your work being overwritten by theme updates).
198
 
208
 
209
  Yes, but this is off by default. You can enable this by passing ´true´ to ´autoptimize_filter_cache_create_static_gzip´. You'll obviously still have to configure your webserver to use these files instead of the non-gzipped ones to avoid the overhead of on-the-fly compression.
210
 
211
+ = Where can I get help? =
 
 
212
 
213
+ 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/).
 
 
 
 
 
 
214
 
215
  = I want out, how should I remove Autoptimize? =
216
 
224
 
225
  == Changelog ==
226
 
227
+ = 2.2.0 =
228
+ * new: Autoptimize minifies first (caching the individual snippets) and aggregrates the minified snippets, resulting in huge performance improvements for uncached JS/ CSS.
229
+ * new: option to enable/ disable AO for logged in users (on by default)
230
+ * new: option to enable/ disable AO on WooCommerce, Easy Digital Downloads or WP eCommerce cart/ checkout page (on by default)
231
+ * improvement: switched to [rel=preload + Filamentgroup’s loadCSS for CSS deferring](http://blog.futtta.be/2017/02/24/autoptimize-css-defer-switching-to-loadcss-soon/)
232
+ * improvement: switched to YUI CSS minifier PHP-port 2.8.4-p10 (so not to the 3.x branch yet)
233
+ * improvements to the logic of which JS/ CSS can be optimized (getPath function) increasing reliability of the aggregation process
234
+ * security: made placeholder replacement less naive to protect against XSS and LFI vulnerability as reported by Matthew Barry and fixed with great help from Matthew and Tomas Trkulja. Thanks guys!!
235
+ * API: Lots of extra filters, making AO (even) more flexible.
236
+ * Lots of bugfixes and smaller improvements (see [GitHub commit log](https://github.com/futtta/autoptimize/commits/master))
237
+ * tested and confirmed working in WordPress 4.8
238
+
239
  = 2.1.1 =
240
+ * identical to 2.1.0 except for the security fix backported from 2.2.0
 
241
 
242
  = 2.1.0 =
243
  * new: Autoptimize now appears in admin-toolbar with an easy view on cache size and the possibility to purge the cache (pass `false` to `autoptimize_filter_toolbar_show` filter to disable), a big thanks to [Pablo Custo](https://github.com/pablocusto) for his hard work on this nice feature!