Version Description
- refactored significantly (no more "classlesses", all is OO), classes are autoloaded, tests added (travis-ci) by zytzagoo who deserves praise for his hard work!
- new: image optimization (see "Extra"-tab) using Shortpixel's smart image optimization proxy
- new: you can now disable JS/ CSS-files being aggregated, having them minified individually instead
- new: Google Fonts can now be "aggregated & preloaded", this uses CSS which is loaded non render-blocking
- new: Autoptimize "listens" to page caches being cleared, upon which it purges it's own cache as well. Support depends on known action hooks firing by the page cache, supported by Hyper Cache, WP Rocket, W3 Total Cache, KeyCDN Cache Enabler, Swift, WP Super Cache, WP Fastest Cache and Comet Cache.
- new: local JS/ CSS-files that are excluded from optimization are minified by default (can be overridden by filter)
- improvement: updated minifiers (with very significant improvements for YUI CSS compressor PHP port)
- improvement: "remove all Google Fonts" is now more careful (avoiding removing entire CSS blocks)
- misc. under the hood improvements (e.g. more robust cache clearing, better support for multibyte character sets, improved CDN rewrite logic, avoid PHP warnings when writing files to cache, ...)
Download this release
Release Info
Developer | futtta |
Plugin | Autoptimize |
Version | 2.4.0 |
Comparing to | |
See all releases |
Code changes from version 2.3.4 to 2.4.0
- autoptimize.php +61 -300
- autoptimize_helper.php_example +5 -5
- classes/autoptimizeBase.php +620 -336
- classes/autoptimizeCLI.php +26 -14
- classes/autoptimizeCSSmin.php +57 -0
- classes/autoptimizeCache.php +542 -151
- classes/autoptimizeCacheChecker.php +112 -0
- classes/autoptimizeConfig.php +372 -227
- classes/autoptimizeExtra.php +921 -0
- classes/autoptimizeHTML.php +98 -67
- classes/autoptimizeMain.php +528 -0
- classes/autoptimizePartners.php +145 -0
- classes/autoptimizeScripts.php +339 -242
- classes/autoptimizeSpeedupper.php +110 -0
- classes/autoptimizeStyles.php +1070 -701
- classes/autoptimizeToolbar.php +72 -56
- classes/autoptimizeUtils.php +424 -0
- classes/autoptimizeVersionUpdatesHandler.php +206 -0
- classes/external/index.html +1 -1
- classes/external/js/index.html +1 -1
- classes/external/php/index.html +1 -1
- classes/external/php/jsmin-1.1.1.php +0 -291
- classes/external/php/{minify-2.3.1-jsmin.php → jsmin.php} +7 -6
- classes/external/php/minify-2.1.7-html.php +0 -257
- classes/external/php/minify-2.1.7-jsmin.php +0 -447
- classes/external/php/minify-css-compressor.php +0 -250
- classes/external/php/minify-html.php +59 -57
- classes/external/php/persist-admin-notices-dismissal/README.md +108 -0
- classes/external/php/persist-admin-notices-dismissal/dismiss-notice.js +29 -0
- classes/external/php/persist-admin-notices-dismissal/persist-admin-notices-dismissal.php +119 -0
- classes/external/php/yui-php-cssmin-2.4.8-4_fgo.php +0 -790
- classes/external/php/yui-php-cssmin-2.4.8-p10/cssmin.php +0 -1112
- classes/external/php/yui-php-cssmin-2.4.8-p10/data/hex-to-named-color-map.php +0 -37
- classes/external/php/yui-php-cssmin-2.4.8-p10/data/named-to-hex-color-map.php +0 -108
- classes/external/php/yui-php-cssmin-bundled/Colors.php +155 -0
- classes/external/php/yui-php-cssmin-bundled/Minifier.php +895 -0
- classes/external/php/yui-php-cssmin-bundled/Utils.php +149 -0
- {classlesses → classes/external/php/yui-php-cssmin-bundled}/index.html +1 -1
- classes/static/toolbar.css +8 -8
- classlesses/autoptimizeCacheChecker.php +0 -65
- classlesses/autoptimizeExtra.php +0 -386
- classlesses/autoptimizePageCacheFlush.php +0 -56
- classlesses/autoptimizePartners.php +0 -122
- classlesses/autoptimizeSpeedupper.php +0 -105
- classlesses/autoptimizeUpdateCode.php +0 -106
- config/default.php +35 -13
- config/delayed.php +0 -86
- localization/index.html +0 -1
- readme.txt +60 -282
autoptimize.php
CHANGED
@@ -2,334 +2,95 @@
|
|
2 |
/*
|
3 |
Plugin Name: Autoptimize
|
4 |
Plugin URI: https://autoptimize.com/
|
5 |
-
Description:
|
6 |
-
Version: 2.
|
7 |
Author: Frank Goossens (futtta)
|
8 |
Author URI: https://autoptimize.com/
|
9 |
-
Domain Path: localization/
|
10 |
Text Domain: autoptimize
|
11 |
Released under the GNU General Public License (GPL)
|
12 |
http://www.gnu.org/licenses/gpl.txt
|
13 |
*/
|
14 |
|
15 |
-
|
|
|
|
|
16 |
|
17 |
-
define('AUTOPTIMIZE_PLUGIN_DIR',plugin_dir_path(__FILE__));
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
// Load toolbar class
|
23 |
-
include( AUTOPTIMIZE_PLUGIN_DIR.'classes/autoptimizeToolbar.php' );
|
24 |
-
|
25 |
-
// Load partners tab if admin
|
26 |
-
if (is_admin()) {
|
27 |
-
include AUTOPTIMIZE_PLUGIN_DIR.'classlesses/autoptimizePartners.php';
|
28 |
-
}
|
29 |
-
|
30 |
-
// Do we gzip when caching (needed early to load autoptimizeCache.php)
|
31 |
-
define('AUTOPTIMIZE_CACHE_NOGZIP',(bool) get_option('autoptimize_cache_nogzip'));
|
32 |
-
|
33 |
-
// Load cache class
|
34 |
-
include(AUTOPTIMIZE_PLUGIN_DIR.'classes/autoptimizeCache.php');
|
35 |
-
|
36 |
-
// wp-content dir name (automagically set, should not be needed), dirname of AO cache dir and AO-prefix can be overridden in wp-config.php
|
37 |
-
if (!defined('AUTOPTIMIZE_WP_CONTENT_NAME')) { define('AUTOPTIMIZE_WP_CONTENT_NAME','/'.wp_basename( WP_CONTENT_DIR )); }
|
38 |
-
if (!defined('AUTOPTIMIZE_CACHE_CHILD_DIR')) { define('AUTOPTIMIZE_CACHE_CHILD_DIR','/cache/autoptimize/'); }
|
39 |
-
if (!defined('AUTOPTIMIZE_CACHEFILE_PREFIX')) { define('AUTOPTIMIZE_CACHEFILE_PREFIX', 'autoptimize_'); }
|
40 |
-
|
41 |
-
// Plugin dir constants (plugin url's defined later to accomodate domain mapped sites)
|
42 |
-
if (!defined('AUTOPTIMIZE_CACHE_DIR')) {
|
43 |
-
if (is_multisite() && apply_filters( 'autoptimize_separate_blog_caches' , true )) {
|
44 |
-
$blog_id = get_current_blog_id();
|
45 |
-
define('AUTOPTIMIZE_CACHE_DIR', WP_CONTENT_DIR.AUTOPTIMIZE_CACHE_CHILD_DIR.$blog_id.'/' );
|
46 |
-
} else {
|
47 |
-
define('AUTOPTIMIZE_CACHE_DIR', WP_CONTENT_DIR.AUTOPTIMIZE_CACHE_CHILD_DIR );
|
48 |
-
}
|
49 |
-
}
|
50 |
-
define('AUTOPTIMIZE_CACHE_DELAY',true);
|
51 |
-
define('WP_ROOT_DIR',substr(WP_CONTENT_DIR, 0, strlen(WP_CONTENT_DIR)-strlen(AUTOPTIMIZE_WP_CONTENT_NAME)));
|
52 |
-
|
53 |
-
// WP CLI
|
54 |
-
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
55 |
-
require_once AUTOPTIMIZE_PLUGIN_DIR . 'classes/autoptimizeCLI.php';
|
56 |
-
}
|
57 |
-
|
58 |
-
// Initialize the cache at least once
|
59 |
-
$conf = autoptimizeConfig::instance();
|
60 |
-
|
61 |
-
/* Check if we're updating, in which case we might need to do stuff and flush the cache
|
62 |
-
to avoid old versions of aggregated files lingering around */
|
63 |
-
|
64 |
-
$autoptimize_version="2.3.0";
|
65 |
-
$autoptimize_db_version=get_option('autoptimize_version','none');
|
66 |
-
|
67 |
-
if ($autoptimize_db_version !== $autoptimize_version) {
|
68 |
-
if ($autoptimize_db_version==="none") {
|
69 |
-
add_action('admin_notices', 'autoptimize_install_config_notice');
|
70 |
-
} else {
|
71 |
-
// updating, include the update-code
|
72 |
-
include(AUTOPTIMIZE_PLUGIN_DIR.'classlesses/autoptimizeUpdateCode.php');
|
73 |
-
}
|
74 |
-
|
75 |
-
update_option('autoptimize_version',$autoptimize_version);
|
76 |
-
$autoptimize_db_version=$autoptimize_version;
|
77 |
-
}
|
78 |
-
|
79 |
-
// Load translations
|
80 |
-
function autoptimize_load_plugin_textdomain() {
|
81 |
-
load_plugin_textdomain('autoptimize',false,plugin_basename(dirname( __FILE__ )).'/localization');
|
82 |
-
}
|
83 |
-
add_action( 'init', 'autoptimize_load_plugin_textdomain' );
|
84 |
-
|
85 |
-
function autoptimize_uninstall(){
|
86 |
-
autoptimizeCache::clearall();
|
87 |
-
|
88 |
-
$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_optimize_logged","autoptimize_optimize_checkout","autoptimize_extra_settings");
|
89 |
-
|
90 |
-
if ( !is_multisite() ) {
|
91 |
-
foreach ($delete_options as $del_opt) { delete_option( $del_opt ); }
|
92 |
-
} else {
|
93 |
-
global $wpdb;
|
94 |
-
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
95 |
-
$original_blog_id = get_current_blog_id();
|
96 |
-
foreach ( $blog_ids as $blog_id ) {
|
97 |
-
switch_to_blog( $blog_id );
|
98 |
-
foreach ($delete_options as $del_opt) { delete_option( $del_opt ); }
|
99 |
-
}
|
100 |
-
switch_to_blog( $original_blog_id );
|
101 |
-
}
|
102 |
-
|
103 |
-
if ( wp_get_schedule( 'ao_cachechecker' ) ) {
|
104 |
-
wp_clear_scheduled_hook( 'ao_cachechecker' );
|
105 |
-
}
|
106 |
-
}
|
107 |
-
|
108 |
-
function autoptimize_install_config_notice() {
|
109 |
-
echo '<div class="updated"><p>';
|
110 |
-
_e('Thank you for installing and activating Autoptimize. Please configure it under "Settings" -> "Autoptimize" to start improving your site\'s performance.', 'autoptimize' );
|
111 |
-
echo '</p></div>';
|
112 |
-
}
|
113 |
-
|
114 |
-
function autoptimize_update_config_notice() {
|
115 |
-
echo '<div class="updated"><p>';
|
116 |
-
_e('Autoptimize has just been updated. Please <strong>test your site now</strong> and adapt Autoptimize config if needed.', 'autoptimize' );
|
117 |
-
echo '</p></div>';
|
118 |
}
|
119 |
|
120 |
-
|
121 |
-
echo '<div class="error"><p>';
|
122 |
-
printf( __( 'Autoptimize cannot write to the cache directory (%s), please fix to enable CSS/ JS optimization!', 'autoptimize' ), AUTOPTIMIZE_CACHE_DIR );
|
123 |
-
echo '</p></div>';
|
124 |
-
}
|
125 |
|
126 |
-
//
|
127 |
-
|
128 |
-
|
129 |
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
|
|
|
|
134 |
}
|
135 |
}
|
136 |
-
|
137 |
-
|
138 |
-
if (defined('DONOTMINIFY') && (constant('DONOTMINIFY')===true || constant('DONOTMINIFY')==="true")) {
|
139 |
-
$ao_noptimize = true;
|
140 |
-
}
|
141 |
-
|
142 |
-
// if setting says not to optimize logged in user and user is logged in
|
143 |
-
if ( get_option('autoptimize_optimize_logged','on') !== 'on' && is_user_logged_in() && current_user_can('edit_posts') ) {
|
144 |
-
$ao_noptimize = true;
|
145 |
-
}
|
146 |
-
|
147 |
-
// if setting says not to optimize cart/ checkout
|
148 |
-
if ( get_option('autoptimize_optimize_checkout','on') !== 'on' ) {
|
149 |
-
// checking for woocommerce, easy digital downloads and wp ecommerce
|
150 |
-
foreach ( array("is_checkout","is_cart","edd_is_checkout","wpsc_is_cart","wpsc_is_checkout") as $shopCond ) {
|
151 |
-
if ( function_exists($shopCond) && $shopCond() ) {
|
152 |
-
$ao_noptimize = true;
|
153 |
-
break;
|
154 |
-
}
|
155 |
-
}
|
156 |
-
}
|
157 |
-
|
158 |
-
// filter you can use to block autoptimization on your own terms
|
159 |
-
$ao_noptimize = (bool) apply_filters( 'autoptimize_filter_noptimize', $ao_noptimize );
|
160 |
-
|
161 |
-
if (!is_feed() && !$ao_noptimize && !is_admin() && ( !function_exists('is_customize_preview') || !is_customize_preview() ) ) {
|
162 |
-
// load speedupper conditionally (true by default?)
|
163 |
-
if ( apply_filters('autoptimize_filter_speedupper', true) ) {
|
164 |
-
include(AUTOPTIMIZE_PLUGIN_DIR.'classlesses/autoptimizeSpeedupper.php');
|
165 |
-
}
|
166 |
-
|
167 |
-
// Config element
|
168 |
-
$conf = autoptimizeConfig::instance();
|
169 |
-
|
170 |
-
// Load our base class
|
171 |
-
include(AUTOPTIMIZE_PLUGIN_DIR.'classes/autoptimizeBase.php');
|
172 |
-
|
173 |
-
// Load extra classes and set some vars
|
174 |
-
if($conf->get('autoptimize_html')) {
|
175 |
-
include(AUTOPTIMIZE_PLUGIN_DIR.'classes/autoptimizeHTML.php');
|
176 |
-
// BUG: new minify-html does not support keeping HTML comments, skipping for now
|
177 |
-
// if (defined('AUTOPTIMIZE_LEGACY_MINIFIERS')) {
|
178 |
-
@include(AUTOPTIMIZE_PLUGIN_DIR.'classes/external/php/minify-html.php');
|
179 |
-
// } else {
|
180 |
-
// @include(AUTOPTIMIZE_PLUGIN_DIR.'classes/external/php/minify-2.1.7-html.php');
|
181 |
-
// }
|
182 |
-
}
|
183 |
-
|
184 |
-
if($conf->get('autoptimize_js')) {
|
185 |
-
include(AUTOPTIMIZE_PLUGIN_DIR.'classes/autoptimizeScripts.php');
|
186 |
-
if (!class_exists('JSMin')) {
|
187 |
-
if (defined('AUTOPTIMIZE_LEGACY_MINIFIERS')) {
|
188 |
-
@include(AUTOPTIMIZE_PLUGIN_DIR.'classes/external/php/jsmin-1.1.1.php');
|
189 |
-
} else {
|
190 |
-
@include(AUTOPTIMIZE_PLUGIN_DIR.'classes/external/php/minify-2.3.1-jsmin.php');
|
191 |
-
}
|
192 |
-
}
|
193 |
-
if ( ! defined( 'CONCATENATE_SCRIPTS' )) {
|
194 |
-
define('CONCATENATE_SCRIPTS',false);
|
195 |
-
}
|
196 |
-
if ( ! defined( 'COMPRESS_SCRIPTS' )) {
|
197 |
-
define('COMPRESS_SCRIPTS',false);
|
198 |
-
}
|
199 |
-
}
|
200 |
-
|
201 |
-
if($conf->get('autoptimize_css')) {
|
202 |
-
include(AUTOPTIMIZE_PLUGIN_DIR.'classes/autoptimizeStyles.php');
|
203 |
-
if (defined('AUTOPTIMIZE_LEGACY_MINIFIERS')) {
|
204 |
-
if (!class_exists('Minify_CSS_Compressor')) {
|
205 |
-
@include(AUTOPTIMIZE_PLUGIN_DIR.'classes/external/php/minify-css-compressor.php');
|
206 |
-
}
|
207 |
-
} else {
|
208 |
-
if (!class_exists('CSSmin')) {
|
209 |
-
@include(AUTOPTIMIZE_PLUGIN_DIR.'classes/external/php/yui-php-cssmin-2.4.8-4_fgo.php');
|
210 |
-
}
|
211 |
-
}
|
212 |
-
if ( ! defined( 'COMPRESS_CSS' )) {
|
213 |
-
define('COMPRESS_CSS',false);
|
214 |
-
}
|
215 |
-
}
|
216 |
-
|
217 |
-
// filter to be used with care, kills all output buffers when true. use with extreme caution. you have been warned!
|
218 |
-
if (apply_filters('autoptimize_filter_obkiller',false)) {
|
219 |
-
while (ob_get_level() > 0) {
|
220 |
-
ob_end_clean();
|
221 |
-
}
|
222 |
-
}
|
223 |
-
// Now, start the real thing!
|
224 |
-
ob_start('autoptimize_end_buffering');
|
225 |
}
|
|
|
|
|
|
|
226 |
}
|
227 |
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
}
|
240 |
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
} else {
|
245 |
-
define('AUTOPTIMIZE_CACHE_URL',AUTOPTIMIZE_WP_CONTENT_URL.AUTOPTIMIZE_CACHE_CHILD_DIR);
|
246 |
}
|
247 |
-
define('AUTOPTIMIZE_WP_ROOT_URL',str_replace(AUTOPTIMIZE_WP_CONTENT_NAME,'',AUTOPTIMIZE_WP_CONTENT_URL));
|
248 |
-
define('AUTOPTIMIZE_HASH',wp_hash(AUTOPTIMIZE_CACHE_DIR));
|
249 |
-
|
250 |
-
// Config element
|
251 |
-
$conf = autoptimizeConfig::instance();
|
252 |
-
|
253 |
-
// Choose the classes
|
254 |
-
$classes = array();
|
255 |
-
if($conf->get('autoptimize_js'))
|
256 |
-
$classes[] = 'autoptimizeScripts';
|
257 |
-
if($conf->get('autoptimize_css'))
|
258 |
-
$classes[] = 'autoptimizeStyles';
|
259 |
-
if($conf->get('autoptimize_html'))
|
260 |
-
$classes[] = 'autoptimizeHTML';
|
261 |
-
|
262 |
-
// Set some options
|
263 |
-
$classoptions = array(
|
264 |
-
'autoptimizeScripts' => array(
|
265 |
-
'justhead' => $conf->get('autoptimize_js_justhead'),
|
266 |
-
'forcehead' => $conf->get('autoptimize_js_forcehead'),
|
267 |
-
'trycatch' => $conf->get('autoptimize_js_trycatch'),
|
268 |
-
'js_exclude' => $conf->get('autoptimize_js_exclude'),
|
269 |
-
'cdn_url' => $conf->get('autoptimize_cdn_url'),
|
270 |
-
'include_inline' => $conf->get('autoptimize_js_include_inline')
|
271 |
-
),
|
272 |
-
'autoptimizeStyles' => array(
|
273 |
-
'justhead' => $conf->get('autoptimize_css_justhead'),
|
274 |
-
'datauris' => $conf->get('autoptimize_css_datauris'),
|
275 |
-
'defer' => $conf->get('autoptimize_css_defer'),
|
276 |
-
'defer_inline' => $conf->get('autoptimize_css_defer_inline'),
|
277 |
-
'inline' => $conf->get('autoptimize_css_inline'),
|
278 |
-
'css_exclude' => $conf->get('autoptimize_css_exclude'),
|
279 |
-
'cdn_url' => $conf->get('autoptimize_cdn_url'),
|
280 |
-
'include_inline' => $conf->get('autoptimize_css_include_inline'),
|
281 |
-
'nogooglefont' => $conf->get('autoptimize_css_nogooglefont')
|
282 |
-
),
|
283 |
-
'autoptimizeHTML' => array(
|
284 |
-
'keepcomments' => $conf->get('autoptimize_html_keepcomments')
|
285 |
-
)
|
286 |
-
);
|
287 |
-
|
288 |
-
$content = apply_filters( 'autoptimize_filter_html_before_minify', $content );
|
289 |
|
290 |
-
|
291 |
-
foreach($classes as $name) {
|
292 |
-
$instance = new $name($content);
|
293 |
-
if($instance->read($classoptions[$name])) {
|
294 |
-
$instance->minify();
|
295 |
-
$instance->cache();
|
296 |
-
$content = $instance->getcontent();
|
297 |
-
}
|
298 |
-
unset($instance);
|
299 |
-
}
|
300 |
-
|
301 |
-
$content = apply_filters( 'autoptimize_html_after_minify', $content );
|
302 |
-
return $content;
|
303 |
}
|
304 |
|
305 |
-
|
306 |
-
$conf = autoptimizeConfig::instance();
|
307 |
-
if( $conf->get('autoptimize_html') || $conf->get('autoptimize_js') || $conf->get('autoptimize_css') ) {
|
308 |
-
// Hook to wordpress
|
309 |
-
if (defined('AUTOPTIMIZE_INIT_EARLIER')) {
|
310 |
-
add_action('init','autoptimize_start_buffering',-1);
|
311 |
-
} else {
|
312 |
-
if (!defined('AUTOPTIMIZE_HOOK_INTO')) { define('AUTOPTIMIZE_HOOK_INTO', 'template_redirect'); }
|
313 |
-
add_action(constant("AUTOPTIMIZE_HOOK_INTO"),'autoptimize_start_buffering',2);
|
314 |
-
}
|
315 |
-
}
|
316 |
-
} else {
|
317 |
-
add_action('admin_notices', 'autoptimize_cache_unavailable_notice');
|
318 |
-
}
|
319 |
|
320 |
-
|
321 |
-
|
|
|
322 |
}
|
323 |
-
register_activation_hook( __FILE__, 'autoptimize_activate' );
|
324 |
|
325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
|
327 |
-
|
328 |
-
|
329 |
-
if ( apply_filters('autoptimize_filter_extra_activate',true) ) {
|
330 |
-
include_once('classlesses/autoptimizeExtra.php');
|
331 |
}
|
|
|
|
|
332 |
}
|
333 |
|
334 |
-
|
335 |
-
unset($conf);
|
2 |
/*
|
3 |
Plugin Name: Autoptimize
|
4 |
Plugin URI: https://autoptimize.com/
|
5 |
+
Description: Optimize your website's performance: JS, CSS, HTML, images, Google Fonts and more!
|
6 |
+
Version: 2.4.0
|
7 |
Author: Frank Goossens (futtta)
|
8 |
Author URI: https://autoptimize.com/
|
|
|
9 |
Text Domain: autoptimize
|
10 |
Released under the GNU General Public License (GPL)
|
11 |
http://www.gnu.org/licenses/gpl.txt
|
12 |
*/
|
13 |
|
14 |
+
/**
|
15 |
+
* Autoptimize main plugin file.
|
16 |
+
*/
|
17 |
|
|
|
18 |
|
19 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
20 |
+
exit;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
}
|
22 |
|
23 |
+
define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.4.0' );
|
|
|
|
|
|
|
|
|
24 |
|
25 |
+
// plugin_dir_path() returns the trailing slash!
|
26 |
+
define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
27 |
+
define( 'AUTOPTIMIZE_PLUGIN_FILE', __FILE__ );
|
28 |
|
29 |
+
// Bail early if attempting to run on non-supported php versions.
|
30 |
+
if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
|
31 |
+
function autoptimize_incompatible_admin_notice() {
|
32 |
+
echo '<div class="error"><p>' . __( 'Autoptimize requires PHP 5.3 (or higher) to function properly. Please upgrade PHP. The Plugin has been auto-deactivated.', 'autoptimize' ) . '</p></div>';
|
33 |
+
if ( isset( $_GET['activate'] ) ) {
|
34 |
+
unset( $_GET['activate'] );
|
35 |
}
|
36 |
}
|
37 |
+
function autoptimize_deactivate_self() {
|
38 |
+
deactivate_plugins( plugin_basename( AUTOPTIMIZE_PLUGIN_FILE ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
}
|
40 |
+
add_action( 'admin_notices', 'autoptimize_incompatible_admin_notice' );
|
41 |
+
add_action( 'admin_init', 'autoptimize_deactivate_self' );
|
42 |
+
return;
|
43 |
}
|
44 |
|
45 |
+
function autoptimize_autoload( $class_name ) {
|
46 |
+
if ( in_array( $class_name, array( 'Minify_HTML', 'JSMin' ) ) ) {
|
47 |
+
$file = strtolower( $class_name );
|
48 |
+
$file = str_replace( '_', '-', $file );
|
49 |
+
$path = dirname( __FILE__ ) . '/classes/external/php/';
|
50 |
+
$filepath = $path . $file . '.php';
|
51 |
+
} elseif ( false !== strpos( $class_name, 'Autoptimize\\tubalmartin\\CssMin' ) ) {
|
52 |
+
$file = str_replace( 'Autoptimize\\tubalmartin\\CssMin\\', '', $class_name );
|
53 |
+
$path = dirname( __FILE__ ) . '/classes/external/php/yui-php-cssmin-bundled/';
|
54 |
+
$filepath = $path . $file . '.php';
|
55 |
+
} elseif ( 'autoptimize' === substr( $class_name, 0, 11 ) ) {
|
56 |
+
// One of our "old" classes.
|
57 |
+
$file = $class_name;
|
58 |
+
$path = dirname( __FILE__ ) . '/classes/';
|
59 |
+
$filepath = $path . $file . '.php';
|
60 |
+
} elseif ( 'PAnD' === $class_name ) {
|
61 |
+
$file = 'persist-admin-notices-dismissal';
|
62 |
+
$path = dirname( __FILE__ ) . '/classes/external/php/persist-admin-notices-dismissal/';
|
63 |
+
$filepath = $path . $file . '.php';
|
64 |
}
|
65 |
|
66 |
+
// If we didn't match one of our rules, bail!
|
67 |
+
if ( ! isset( $filepath ) ) {
|
68 |
+
return;
|
|
|
|
|
69 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
|
71 |
+
require $filepath;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
}
|
73 |
|
74 |
+
spl_autoload_register( 'autoptimize_autoload' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
+
// Load WP CLI command(s) on demand.
|
77 |
+
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
78 |
+
require AUTOPTIMIZE_PLUGIN_DIR . 'classes/autoptimizeCLI.php';
|
79 |
}
|
|
|
80 |
|
81 |
+
/**
|
82 |
+
* Retrieve the instance of the main plugin class.
|
83 |
+
*
|
84 |
+
* @return autoptimizeMain
|
85 |
+
*/
|
86 |
+
function autoptimize() {
|
87 |
+
static $plugin = null;
|
88 |
|
89 |
+
if ( null === $plugin ) {
|
90 |
+
$plugin = new autoptimizeMain( AUTOPTIMIZE_PLUGIN_VERSION, AUTOPTIMIZE_PLUGIN_FILE );
|
|
|
|
|
91 |
}
|
92 |
+
|
93 |
+
return $plugin;
|
94 |
}
|
95 |
|
96 |
+
autoptimize()->run();
|
|
autoptimize_helper.php_example
CHANGED
@@ -5,7 +5,7 @@ Plugin URI: http://blog.futtta.be/autoptimize
|
|
5 |
Description: Autoptimize Helper contains some helper functions to make Autoptimize even more flexible
|
6 |
Author: Frank Goossens (futtta)
|
7 |
Version: 0.2
|
8 |
-
Author URI: http://blog.futtta.be/
|
9 |
*/
|
10 |
|
11 |
/* autoptimize_filter_css_datauri_maxsize: change the threshold at which background images are turned into data uri's;
|
@@ -33,7 +33,7 @@ function my_ao_exclude_image($imageexcl) {
|
|
33 |
// add_filter('autoptimize_filter_js_defer','my_ao_override_defer',10,1);
|
34 |
function my_ao_override_defer($defer) {
|
35 |
return $defer."async ";
|
36 |
-
}
|
37 |
|
38 |
/* autoptimize_filter_noptimize: stop autoptimize from optimizing, e.g. based on URL as in example
|
39 |
|
@@ -133,11 +133,11 @@ function my_ao_css_include_inline() {
|
|
133 |
function my_ao_css_defer_inline($inlined) {
|
134 |
return $inlined."h2,h1{color:red !important;}";
|
135 |
}
|
136 |
-
|
137 |
/* autoptimize_filter_css_fonts_cdn: do we want to move fonts to the CDN-url as well
|
138 |
|
139 |
-
@return: false (default) or true */
|
140 |
// add_filter('autoptimize_filter_css_fonts_cdn','my_css_cdnfont',10,0);
|
141 |
function my_css_cdnfont(){
|
142 |
-
|
143 |
}
|
5 |
Description: Autoptimize Helper contains some helper functions to make Autoptimize even more flexible
|
6 |
Author: Frank Goossens (futtta)
|
7 |
Version: 0.2
|
8 |
+
Author URI: http://blog.futtta.be/
|
9 |
*/
|
10 |
|
11 |
/* autoptimize_filter_css_datauri_maxsize: change the threshold at which background images are turned into data uri's;
|
33 |
// add_filter('autoptimize_filter_js_defer','my_ao_override_defer',10,1);
|
34 |
function my_ao_override_defer($defer) {
|
35 |
return $defer."async ";
|
36 |
+
}
|
37 |
|
38 |
/* autoptimize_filter_noptimize: stop autoptimize from optimizing, e.g. based on URL as in example
|
39 |
|
133 |
function my_ao_css_defer_inline($inlined) {
|
134 |
return $inlined."h2,h1{color:red !important;}";
|
135 |
}
|
136 |
+
|
137 |
/* autoptimize_filter_css_fonts_cdn: do we want to move fonts to the CDN-url as well
|
138 |
|
139 |
+
@return: false (default) or true */
|
140 |
// add_filter('autoptimize_filter_css_fonts_cdn','my_css_cdnfont',10,0);
|
141 |
function my_css_cdnfont(){
|
142 |
+
return true;
|
143 |
}
|
classes/autoptimizeBase.php
CHANGED
@@ -1,82 +1,127 @@
|
|
1 |
<?php
|
2 |
-
|
|
|
|
|
3 |
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
protected $content = '';
|
6 |
-
|
7 |
-
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
$this->content = $content;
|
10 |
}
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
abstract public function minify();
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
19 |
abstract public function cache();
|
20 |
-
|
21 |
-
|
|
|
|
|
|
|
|
|
22 |
abstract public function getcontent();
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
}
|
31 |
|
32 |
-
$
|
33 |
-
$
|
34 |
-
|
35 |
-
//
|
36 |
-
|
37 |
-
|
38 |
-
|
|
|
39 |
} else {
|
40 |
-
$url =
|
41 |
}
|
42 |
-
}
|
43 |
-
if (AUTOPTIMIZE_WP_SITE_URL === $
|
44 |
-
$url = AUTOPTIMIZE_WP_SITE_URL
|
45 |
} else {
|
46 |
-
$
|
47 |
-
$url = AUTOPTIMIZE_WP_SITE_URL.str_repeat("/..",$subdir_levels).$url;
|
48 |
}
|
49 |
}
|
50 |
-
|
51 |
-
if ($
|
52 |
-
$url=str_replace(AUTOPTIMIZE_WP_CONTENT_URL,AUTOPTIMIZE_WP_SITE_URL.AUTOPTIMIZE_WP_CONTENT_NAME
|
53 |
}
|
54 |
|
55 |
-
//
|
56 |
-
$
|
57 |
-
if ($
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
$multidomains = array();
|
65 |
-
|
66 |
-
$
|
67 |
-
if (!empty($
|
68 |
-
$multidomains = array_map(array($this,
|
69 |
}
|
70 |
-
|
71 |
-
if (!empty($this->cdn_url)) {
|
72 |
-
$multidomains[]=parse_url($this->cdn_url,PHP_URL_HOST);
|
73 |
}
|
74 |
-
|
75 |
-
$multidomains = apply_filters('autoptimize_filter_cssjs_multidomain', $multidomains);
|
76 |
-
|
77 |
-
if (!empty($multidomains)) {
|
78 |
-
if (in_array($
|
79 |
-
$url=str_replace($
|
80 |
} else {
|
81 |
return false;
|
82 |
}
|
@@ -84,337 +129,576 @@ abstract class autoptimizeBase {
|
|
84 |
return false;
|
85 |
}
|
86 |
}
|
87 |
-
|
88 |
-
//
|
89 |
-
$tmp_ao_root = preg_replace('/https?:/','',AUTOPTIMIZE_WP_ROOT_URL);
|
90 |
-
|
91 |
-
|
92 |
-
|
|
|
93 |
}
|
94 |
-
|
95 |
-
$
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
|
|
|
|
100 |
return false;
|
101 |
}
|
102 |
|
103 |
-
//
|
104 |
-
$path = str_replace('//','/',WP_ROOT_DIR
|
105 |
-
|
106 |
-
//
|
107 |
-
if (file_exists($path) && is_file($path) && is_readable($path)) {
|
108 |
return $path;
|
109 |
} else {
|
110 |
return false;
|
111 |
}
|
112 |
}
|
113 |
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
}
|
120 |
-
|
121 |
-
// do the actual parse_url
|
122 |
-
$out = parse_url($in,PHP_URL_HOST);
|
123 |
-
|
124 |
-
// fallback if parse_url does not understand the url is in fact a url
|
125 |
-
if (empty($out)) $out=$in;
|
126 |
-
|
127 |
-
return $out;
|
128 |
-
}
|
129 |
|
|
|
|
|
130 |
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
$logmsg="<!--noptimize--><!-- ".$logmsg." --><!--/noptimize-->";
|
135 |
-
$this->content.=$logmsg;
|
136 |
-
} else {
|
137 |
-
error_log("Autoptimize: ".$logmsg);
|
138 |
}
|
|
|
|
|
139 |
}
|
140 |
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
}
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
);
|
169 |
-
} else {
|
170 |
-
$noptimize_out = $noptimize_in;
|
171 |
-
}
|
172 |
-
return $noptimize_out;
|
173 |
}
|
174 |
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
|
|
189 |
}
|
190 |
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
} else {
|
202 |
-
$iehacks_out=$iehacks_in;
|
203 |
-
}
|
204 |
-
return $iehacks_out;
|
205 |
}
|
206 |
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
|
|
|
|
|
|
221 |
}
|
222 |
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
$comments_out=$comments_in;
|
235 |
-
}
|
236 |
-
return $comments_out;
|
237 |
}
|
238 |
|
239 |
-
|
240 |
-
|
241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
242 |
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
|
|
|
|
|
|
|
|
247 |
} else {
|
248 |
-
//
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
$
|
253 |
-
}
|
254 |
-
// replace full url's with scheme
|
255 |
-
$tmp_url = str_replace( $WPBaseUrl, rtrim( $cdn_url, '/' ), $url );
|
256 |
-
if ( $tmp_url === $url ) {
|
257 |
-
// last attempt; replace scheme-less URL's
|
258 |
-
$url = str_replace( preg_replace( '/https?:/', '', $WPBaseUrl ), rtrim( $cdn_url, '/' ), $url );
|
259 |
} else {
|
260 |
-
$
|
261 |
}
|
|
|
|
|
262 |
}
|
|
|
|
|
263 |
}
|
264 |
|
265 |
-
//
|
266 |
$url = apply_filters( 'autoptimize_filter_base_replace_cdn', $url );
|
|
|
267 |
return $url;
|
268 |
}
|
269 |
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
} else {
|
277 |
-
$
|
278 |
}
|
279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
} else {
|
|
|
281 |
$this->content .= $payload;
|
282 |
-
if (
|
283 |
-
$
|
284 |
-
$this->
|
|
|
285 |
}
|
286 |
}
|
287 |
}
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
292 |
return true;
|
293 |
}
|
294 |
}
|
|
|
295 |
return false;
|
296 |
}
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
301 |
$out = preg_replace_callback(
|
302 |
-
'#\/\*\!%%INJECTLATER'.AUTOPTIMIZE_HASH.'%%(.*?)%%INJECTLATER%%\*\/#is',
|
303 |
-
|
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 |
-
|
311 |
-
// remove comments and blank lines
|
312 |
-
if (substr($filepath,-3,3)===".js") {
|
313 |
-
$filecontent=preg_replace("#^\s*\/\/.*$#Um","",$filecontent);
|
314 |
-
}
|
315 |
-
|
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.=";";
|
323 |
-
}
|
324 |
-
|
325 |
-
if (get_option("autoptimize_js_trycatch")==="on") {
|
326 |
-
$filecontent="try{".$filecontent."}catch(e){}";
|
327 |
-
}
|
328 |
-
} else if ((substr($filepath,-4,4)===".css")) {
|
329 |
-
$filecontent=autoptimizeStyles::fixurls($filepath,$filecontent);
|
330 |
-
} else {
|
331 |
-
$filecontent="";
|
332 |
-
}
|
333 |
-
|
334 |
-
// return
|
335 |
-
return "\n".$filecontent;'
|
336 |
-
),
|
337 |
$in
|
338 |
);
|
339 |
-
} else {
|
340 |
-
$out = $in;
|
341 |
}
|
|
|
342 |
return $out;
|
343 |
}
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
420 |
}
|
1 |
<?php
|
2 |
+
/**
|
3 |
+
* Base class other (more-specific) classes inherit from.
|
4 |
+
*/
|
5 |
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
+
abstract class autoptimizeBase
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Holds content being processed (html, scripts, styles)
|
14 |
+
*
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
protected $content = '';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Controls debug logging.
|
21 |
+
*
|
22 |
+
* @var bool
|
23 |
+
*/
|
24 |
+
public $debug_log = false;
|
25 |
+
|
26 |
+
public function __construct( $content )
|
27 |
+
{
|
28 |
$this->content = $content;
|
29 |
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Reads the page and collects tags.
|
33 |
+
*
|
34 |
+
* @param array $options Options.
|
35 |
+
*
|
36 |
+
* @return bool
|
37 |
+
*/
|
38 |
+
abstract public function read( $options );
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Joins and optimizes collected things.
|
42 |
+
*
|
43 |
+
* @return bool
|
44 |
+
*/
|
45 |
abstract public function minify();
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Caches the things.
|
49 |
+
*
|
50 |
+
* @return void
|
51 |
+
*/
|
52 |
abstract public function cache();
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Returns the content
|
56 |
+
*
|
57 |
+
* @return string
|
58 |
+
*/
|
59 |
abstract public function getcontent();
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Tranfsorms a given URL to a full local filepath if possible.
|
63 |
+
* Returns local filepath or false.
|
64 |
+
*
|
65 |
+
* @param string $url URL to transform.
|
66 |
+
*
|
67 |
+
* @return bool|string
|
68 |
+
*/
|
69 |
+
public function getpath( $url )
|
70 |
+
{
|
71 |
+
$url = apply_filters( 'autoptimize_filter_cssjs_alter_url', $url );
|
72 |
+
|
73 |
+
if ( false !== strpos( $url, '%' ) ) {
|
74 |
+
$url = urldecode( $url );
|
75 |
}
|
76 |
|
77 |
+
$site_host = parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST );
|
78 |
+
$content_host = parse_url( AUTOPTIMIZE_WP_ROOT_URL, PHP_URL_HOST );
|
79 |
+
|
80 |
+
// Normalizing attempts...
|
81 |
+
$double_slash_position = strpos( $url, '//' );
|
82 |
+
if ( 0 === $double_slash_position ) {
|
83 |
+
if ( is_ssl() ) {
|
84 |
+
$url = 'https:' . $url;
|
85 |
} else {
|
86 |
+
$url = 'http:' . $url;
|
87 |
}
|
88 |
+
} elseif ( ( false === $double_slash_position ) && ( false === strpos( $url, $site_host ) ) ) {
|
89 |
+
if ( AUTOPTIMIZE_WP_SITE_URL === $site_host ) {
|
90 |
+
$url = AUTOPTIMIZE_WP_SITE_URL . $url;
|
91 |
} else {
|
92 |
+
$url = AUTOPTIMIZE_WP_SITE_URL . autoptimizeUtils::path_canonicalize( $url );
|
|
|
93 |
}
|
94 |
}
|
95 |
+
|
96 |
+
if ( $site_host !== $content_host ) {
|
97 |
+
$url = str_replace( AUTOPTIMIZE_WP_CONTENT_URL, AUTOPTIMIZE_WP_SITE_URL . AUTOPTIMIZE_WP_CONTENT_NAME, $url );
|
98 |
}
|
99 |
|
100 |
+
// First check; hostname wp site should be hostname of url!
|
101 |
+
$url_host = @parse_url( $url, PHP_URL_HOST ); // @codingStandardsIgnoreLine
|
102 |
+
if ( $url_host !== $site_host ) {
|
103 |
+
/**
|
104 |
+
* First try to get all domains from WPML (if available)
|
105 |
+
* then explicitely declare $this->cdn_url as OK as well
|
106 |
+
* then apply own filter autoptimize_filter_cssjs_multidomain takes an array of hostnames
|
107 |
+
* each item in that array will be considered part of the same WP multisite installation
|
108 |
+
*/
|
109 |
$multidomains = array();
|
110 |
+
|
111 |
+
$multidomains_wpml = apply_filters( 'wpml_setting', array(), 'language_domains' );
|
112 |
+
if ( ! empty( $multidomains_wpml ) ) {
|
113 |
+
$multidomains = array_map( array( $this, 'get_url_hostname' ), $multidomains_wpml );
|
114 |
}
|
115 |
+
|
116 |
+
if ( ! empty( $this->cdn_url ) ) {
|
117 |
+
$multidomains[] = parse_url( $this->cdn_url, PHP_URL_HOST );
|
118 |
}
|
119 |
+
|
120 |
+
$multidomains = apply_filters( 'autoptimize_filter_cssjs_multidomain', $multidomains );
|
121 |
+
|
122 |
+
if ( ! empty( $multidomains ) ) {
|
123 |
+
if ( in_array( $url_host, $multidomains ) ) {
|
124 |
+
$url = str_replace( $url_host, $site_host, $url );
|
125 |
} else {
|
126 |
return false;
|
127 |
}
|
129 |
return false;
|
130 |
}
|
131 |
}
|
132 |
+
|
133 |
+
// Try to remove "wp root url" from url while not minding http<>https.
|
134 |
+
$tmp_ao_root = preg_replace( '/https?:/', '', AUTOPTIMIZE_WP_ROOT_URL );
|
135 |
+
|
136 |
+
if ( $site_host !== $content_host ) {
|
137 |
+
// As we replaced the content-domain with the site-domain, we should match against that.
|
138 |
+
$tmp_ao_root = preg_replace( '/https?:/', '', AUTOPTIMIZE_WP_SITE_URL );
|
139 |
}
|
140 |
+
|
141 |
+
$tmp_url = preg_replace( '/https?:/', '', $url );
|
142 |
+
$path = str_replace( $tmp_ao_root, '', $tmp_url );
|
143 |
+
|
144 |
+
// If path starts with :// or //, this is not a URL in the WP context and
|
145 |
+
// we have to assume we can't aggregate.
|
146 |
+
if ( preg_match( '#^:?//#', $path ) ) {
|
147 |
+
// External script/css (adsense, etc).
|
148 |
return false;
|
149 |
}
|
150 |
|
151 |
+
// Prepend with WP_ROOT_DIR to have full path to file.
|
152 |
+
$path = str_replace( '//', '/', WP_ROOT_DIR . $path );
|
153 |
+
|
154 |
+
// Final check: does file exist and is it readable?
|
155 |
+
if ( file_exists( $path ) && is_file( $path ) && is_readable( $path ) ) {
|
156 |
return $path;
|
157 |
} else {
|
158 |
return false;
|
159 |
}
|
160 |
}
|
161 |
|
162 |
+
/**
|
163 |
+
* Returns the hostname part of a given $url if we're able to parse it.
|
164 |
+
* If not, it returns the original url (prefixed with http:// scheme in case
|
165 |
+
* it was missing).
|
166 |
+
* Used as callback for WPML multidomains filter.
|
167 |
+
*
|
168 |
+
* @param string $url URL.
|
169 |
+
*
|
170 |
+
* @return string
|
171 |
+
*/
|
172 |
+
protected function get_url_hostname( $url )
|
173 |
+
{
|
174 |
+
// Checking that the url starts with something vaguely resembling a protocol.
|
175 |
+
if ( ( 0 !== strpos( $url, 'http' ) ) && ( 0 !== strpos( $url, '//' ) ) ) {
|
176 |
+
$url = 'http://' . $url;
|
177 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
|
179 |
+
// Grab the hostname.
|
180 |
+
$hostname = parse_url( $url, PHP_URL_HOST );
|
181 |
|
182 |
+
// Fallback when parse_url() fails.
|
183 |
+
if ( empty( $hostname ) ) {
|
184 |
+
$hostname = $url;
|
|
|
|
|
|
|
|
|
185 |
}
|
186 |
+
|
187 |
+
return $hostname;
|
188 |
}
|
189 |
|
190 |
+
/**
|
191 |
+
* Hides everything between noptimize-comment tags.
|
192 |
+
*
|
193 |
+
* @param string $markup Markup to process.
|
194 |
+
*
|
195 |
+
* @return string
|
196 |
+
*/
|
197 |
+
protected function hide_noptimize( $markup )
|
198 |
+
{
|
199 |
+
return $this->replace_contents_with_marker_if_exists(
|
200 |
+
'NOPTIMIZE',
|
201 |
+
'/<!--\s?noptimize\s?-->/',
|
202 |
+
'#<!--\s?noptimize\s?-->.*?<!--\s?/\s?noptimize\s?-->#is',
|
203 |
+
$markup
|
204 |
+
);
|
205 |
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Unhide noptimize-tags.
|
209 |
+
*
|
210 |
+
* @param string $markup Markup to process.
|
211 |
+
*
|
212 |
+
* @return string
|
213 |
+
*/
|
214 |
+
protected function restore_noptimize( $markup )
|
215 |
+
{
|
216 |
+
return $this->restore_marked_content( 'NOPTIMIZE', $markup );
|
|
|
|
|
|
|
|
|
|
|
217 |
}
|
218 |
|
219 |
+
/**
|
220 |
+
* Hides "iehacks" content.
|
221 |
+
*
|
222 |
+
* @param string $markup Markup to process.
|
223 |
+
*
|
224 |
+
* @return string
|
225 |
+
*/
|
226 |
+
protected function hide_iehacks( $markup )
|
227 |
+
{
|
228 |
+
return $this->replace_contents_with_marker_if_exists(
|
229 |
+
'IEHACK', // Marker name...
|
230 |
+
'<!--[if', // Invalid regex, will fallback to search using strpos()...
|
231 |
+
'#<!--\[if.*?\[endif\]-->#is', // Replacement regex...
|
232 |
+
$markup
|
233 |
+
);
|
234 |
}
|
235 |
|
236 |
+
/**
|
237 |
+
* Restores "hidden" iehacks content.
|
238 |
+
*
|
239 |
+
* @param string $markup Markup to process.
|
240 |
+
*
|
241 |
+
* @return string
|
242 |
+
*/
|
243 |
+
protected function restore_iehacks( $markup )
|
244 |
+
{
|
245 |
+
return $this->restore_marked_content( 'IEHACK', $markup );
|
|
|
|
|
|
|
|
|
246 |
}
|
247 |
|
248 |
+
/**
|
249 |
+
* "Hides" content within HTML comments using a regex-based replacement
|
250 |
+
* if HTML comment markers are found.
|
251 |
+
* `<!--example-->` becomes `%%COMMENTS%%ZXhhbXBsZQ==%%COMMENTS%%`
|
252 |
+
*
|
253 |
+
* @param string $markup Markup to process.
|
254 |
+
*
|
255 |
+
* @return string
|
256 |
+
*/
|
257 |
+
protected function hide_comments( $markup )
|
258 |
+
{
|
259 |
+
return $this->replace_contents_with_marker_if_exists(
|
260 |
+
'COMMENTS',
|
261 |
+
'<!--',
|
262 |
+
'#<!--.*?-->#is',
|
263 |
+
$markup
|
264 |
+
);
|
265 |
}
|
266 |
|
267 |
+
/**
|
268 |
+
* Restores original HTML comment markers inside a string whose HTML
|
269 |
+
* comments have been "hidden" by using `hide_comments()`.
|
270 |
+
*
|
271 |
+
* @param string $markup Markup to process.
|
272 |
+
*
|
273 |
+
* @return string
|
274 |
+
*/
|
275 |
+
protected function restore_comments( $markup )
|
276 |
+
{
|
277 |
+
return $this->restore_marked_content( 'COMMENTS', $markup );
|
|
|
|
|
|
|
278 |
}
|
279 |
|
280 |
+
/**
|
281 |
+
* Replaces the given URL with the CDN-version of it when CDN replacement
|
282 |
+
* is supposed to be done.
|
283 |
+
*
|
284 |
+
* @param string $url URL to process.
|
285 |
+
*
|
286 |
+
* @return string
|
287 |
+
*/
|
288 |
+
public function url_replace_cdn( $url )
|
289 |
+
{
|
290 |
+
// For 2.3 back-compat in which cdn-ing appeared to be automatically
|
291 |
+
// including WP subfolder/subdirectory into account as part of cdn-ing,
|
292 |
+
// even though it might've caused serious troubles in certain edge-cases.
|
293 |
+
$cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $this->cdn_url );
|
294 |
+
|
295 |
+
// Allows API/filter to further tweak the cdn url...
|
296 |
+
$cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url );
|
297 |
+
if ( ! empty( $cdn_url ) ) {
|
298 |
+
$this->debug_log( 'before=' . $url );
|
299 |
|
300 |
+
// Simple str_replace-based approach fails when $url is protocol-or-host-relative.
|
301 |
+
$is_protocol_relative = autoptimizeUtils::is_protocol_relative( $url );
|
302 |
+
$is_host_relative = ( ! $is_protocol_relative && ( '/' === $url{0} ) );
|
303 |
+
$cdn_url = rtrim( $cdn_url, '/' );
|
304 |
+
|
305 |
+
if ( $is_host_relative ) {
|
306 |
+
// Prepending host-relative urls with the cdn url.
|
307 |
+
$url = $cdn_url . $url;
|
308 |
} else {
|
309 |
+
// Either a protocol-relative or "regular" url, replacing it either way.
|
310 |
+
if ( $is_protocol_relative ) {
|
311 |
+
// Massage $site_url so that simple str_replace() still "works" by
|
312 |
+
// searching for the protocol-relative version of AUTOPTIMIZE_WP_SITE_URL.
|
313 |
+
$site_url = str_replace( array( 'http:', 'https:' ), '', AUTOPTIMIZE_WP_SITE_URL );
|
|
|
|
|
|
|
|
|
|
|
|
|
314 |
} else {
|
315 |
+
$site_url = AUTOPTIMIZE_WP_SITE_URL;
|
316 |
}
|
317 |
+
$this->debug_log( '`' . $site_url . '` -> `' . $cdn_url . '` in `' . $url . '`' );
|
318 |
+
$url = str_replace( $site_url, $cdn_url, $url );
|
319 |
}
|
320 |
+
|
321 |
+
$this->debug_log( 'after=' . $url );
|
322 |
}
|
323 |
|
324 |
+
// Allow API filter to take further care of CDN replacement.
|
325 |
$url = apply_filters( 'autoptimize_filter_base_replace_cdn', $url );
|
326 |
+
|
327 |
return $url;
|
328 |
}
|
329 |
|
330 |
+
/**
|
331 |
+
* Injects/replaces the given payload markup into `$this->content`
|
332 |
+
* at the specified location.
|
333 |
+
* If the specified tag cannot be found, the payload is appended into
|
334 |
+
* $this->content along with a warning wrapped inside <!--noptimize--> tags.
|
335 |
+
*
|
336 |
+
* @param string $payload Markup to inject.
|
337 |
+
* @param array $where Array specifying the tag name and method of injection.
|
338 |
+
* Index 0 is the tag name (i.e., `</body>`).
|
339 |
+
* Index 1 specifies ˛'before', 'after' or 'replace'. Defaults to 'before'.
|
340 |
+
*
|
341 |
+
* @return void
|
342 |
+
*/
|
343 |
+
protected function inject_in_html( $payload, $where )
|
344 |
+
{
|
345 |
+
$warned = false;
|
346 |
+
$position = autoptimizeUtils::strpos( $this->content, $where[0] );
|
347 |
+
if ( false !== $position ) {
|
348 |
+
// Found the tag, setup content/injection as specified.
|
349 |
+
if ( 'after' === $where[1] ) {
|
350 |
+
$content = $where[0] . $payload;
|
351 |
+
} elseif ( 'replace' === $where[1] ) {
|
352 |
+
$content = $payload;
|
353 |
} else {
|
354 |
+
$content = $payload . $where[0];
|
355 |
}
|
356 |
+
// Place where specified.
|
357 |
+
$this->content = autoptimizeUtils::substr_replace(
|
358 |
+
$this->content,
|
359 |
+
$content,
|
360 |
+
$position,
|
361 |
+
// Using plain strlen() should be safe here for now, since
|
362 |
+
// we're not searching for multibyte chars here still...
|
363 |
+
strlen( $where[0] )
|
364 |
+
);
|
365 |
} else {
|
366 |
+
// Couldn't find what was specified, just append and add a warning.
|
367 |
$this->content .= $payload;
|
368 |
+
if ( ! $warned ) {
|
369 |
+
$tag_display = str_replace( array( '<', '>' ), '', $where[0] );
|
370 |
+
$this->content .= '<!--noptimize--><!-- Autoptimize found a problem with the HTML in your Theme, tag `' . $tag_display . '` missing --><!--/noptimize-->';
|
371 |
+
$warned = true;
|
372 |
}
|
373 |
}
|
374 |
}
|
375 |
+
|
376 |
+
/**
|
377 |
+
* Returns true if given `$tag` is found in the list of `$removables`.
|
378 |
+
*
|
379 |
+
* @param string $tag Tag to search for.
|
380 |
+
* @param array $removables List of things considered completely removable.
|
381 |
+
*
|
382 |
+
* @return bool
|
383 |
+
*/
|
384 |
+
protected function isremovable( $tag, $removables )
|
385 |
+
{
|
386 |
+
foreach ( $removables as $match ) {
|
387 |
+
if ( false !== strpos( $tag, $match ) ) {
|
388 |
return true;
|
389 |
}
|
390 |
}
|
391 |
+
|
392 |
return false;
|
393 |
}
|
394 |
+
|
395 |
+
/**
|
396 |
+
* Callback used in `self::inject_minified()`.
|
397 |
+
*
|
398 |
+
* @param array $matches Regex matches.
|
399 |
+
*
|
400 |
+
* @return string
|
401 |
+
*/
|
402 |
+
public function inject_minified_callback( $matches )
|
403 |
+
{
|
404 |
+
static $conf = null;
|
405 |
+
if ( null === $conf ) {
|
406 |
+
$conf = autoptimizeConfig::instance();
|
407 |
+
}
|
408 |
+
|
409 |
+
/**
|
410 |
+
* $matches[1] holds the whole match caught by regex in self::inject_minified(),
|
411 |
+
* so we take that and split the string on `|`.
|
412 |
+
* First element is the filepath, second is the md5 hash of contents
|
413 |
+
* the filepath had when it was being processed.
|
414 |
+
* If we don't have those, we'll bail out early.
|
415 |
+
*/
|
416 |
+
$filepath = null;
|
417 |
+
$filehash = null;
|
418 |
+
|
419 |
+
// Grab the parts we need.
|
420 |
+
$parts = explode( $matches[1], '|' );
|
421 |
+
if ( ! empty( $parts ) ) {
|
422 |
+
$filepath = isset( $parts[0] ) ? $parts[0] : null;
|
423 |
+
$filehash = isset( $parts[1] ) ? $parts[1] : null;
|
424 |
+
}
|
425 |
+
|
426 |
+
// Bail early if something's not right...
|
427 |
+
if ( ! $filepath || ! $filehash ) {
|
428 |
+
return "\n";
|
429 |
+
}
|
430 |
+
|
431 |
+
$filecontent = file_get_contents( $filepath );
|
432 |
+
|
433 |
+
// Some things are differently handled for css/js...
|
434 |
+
$is_js_file = ( '.js' === substr( $filepath, -3, 3 ) );
|
435 |
+
|
436 |
+
$is_css_file = false;
|
437 |
+
if ( ! $is_js_file ) {
|
438 |
+
$is_css_file = ( '.css' === substr( $filepath, -4, 4 ) );
|
439 |
+
}
|
440 |
+
|
441 |
+
// BOMs being nuked here unconditionally (regardless of where they are)!
|
442 |
+
$filecontent = preg_replace( "#\x{EF}\x{BB}\x{BF}#", '', $filecontent );
|
443 |
+
|
444 |
+
// Remove comments and blank lines.
|
445 |
+
if ( $is_js_file ) {
|
446 |
+
$filecontent = preg_replace( '#^\s*\/\/.*$#Um', '', $filecontent );
|
447 |
+
}
|
448 |
+
|
449 |
+
// Nuke un-important comments.
|
450 |
+
$filecontent = preg_replace( '#^\s*\/\*[^!].*\*\/\s?#Um', '', $filecontent );
|
451 |
+
|
452 |
+
// Normalize newlines.
|
453 |
+
$filecontent = preg_replace( '#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#', "\n", $filecontent );
|
454 |
+
|
455 |
+
// JS specifics.
|
456 |
+
if ( $is_js_file ) {
|
457 |
+
// Append a semicolon at the end of js files if it's missing.
|
458 |
+
$last_char = substr( $filecontent, -1, 1 );
|
459 |
+
if ( ';' !== $last_char && '}' !== $last_char ) {
|
460 |
+
$filecontent .= ';';
|
461 |
+
}
|
462 |
+
// Check if try/catch should be used.
|
463 |
+
$opt_js_try_catch = $conf->get( 'autoptimize_js_trycatch' );
|
464 |
+
if ( 'on' === $opt_js_try_catch ) {
|
465 |
+
// It should, wrap in try/catch.
|
466 |
+
$filecontent = 'try{' . $filecontent . '}catch(e){}';
|
467 |
+
}
|
468 |
+
} elseif ( $is_css_file ) {
|
469 |
+
$filecontent = autoptimizeStyles::fixurls( $filepath, $filecontent );
|
470 |
+
} else {
|
471 |
+
$filecontent = '';
|
472 |
+
}
|
473 |
+
|
474 |
+
// Return modified (or empty!) code/content.
|
475 |
+
return "\n" . $filecontent;
|
476 |
+
}
|
477 |
+
|
478 |
+
/**
|
479 |
+
* Inject already minified code in optimized JS/CSS.
|
480 |
+
*
|
481 |
+
* @param string $in Markup.
|
482 |
+
*
|
483 |
+
* @return string
|
484 |
+
*/
|
485 |
+
protected function inject_minified( $in )
|
486 |
+
{
|
487 |
+
$out = $in;
|
488 |
+
|
489 |
+
if ( false !== strpos( $in, '%%INJECTLATER%%' ) ) {
|
490 |
$out = preg_replace_callback(
|
491 |
+
'#\/\*\!%%INJECTLATER' . AUTOPTIMIZE_HASH . '%%(.*?)%%INJECTLATER%%\*\/#is',
|
492 |
+
array( $this, 'inject_minified_callback' ),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
493 |
$in
|
494 |
);
|
|
|
|
|
495 |
}
|
496 |
+
|
497 |
return $out;
|
498 |
}
|
499 |
+
|
500 |
+
/**
|
501 |
+
* Specialized method to create the INJECTLATER marker.
|
502 |
+
* These are somewhat "special", in the sense that they're additionally wrapped
|
503 |
+
* within an "exclamation mark style" comment, so that they're not stripped
|
504 |
+
* out by minifiers.
|
505 |
+
* They also currently contain the hash of the file's contents too (unlike other markers).
|
506 |
+
*
|
507 |
+
* @param string $filepath Filepath.
|
508 |
+
* @param string $hash Hash.
|
509 |
+
*
|
510 |
+
* @return string
|
511 |
+
*/
|
512 |
+
public static function build_injectlater_marker( $filepath, $hash )
|
513 |
+
{
|
514 |
+
$contents = '/*!' . self::build_marker( 'INJECTLATER', $filepath, $hash ) . '*/';
|
515 |
+
|
516 |
+
return $contents;
|
517 |
+
}
|
518 |
+
|
519 |
+
/**
|
520 |
+
* Creates and returns a `%%`-style named marker which holds
|
521 |
+
* the base64 encoded `$data`.
|
522 |
+
* If `$hash` is provided, it's appended to the base64 encoded string
|
523 |
+
* using `|` as the separator (in order to support building the
|
524 |
+
* somewhat special/different INJECTLATER marker).
|
525 |
+
*
|
526 |
+
* @param string $name Marker name.
|
527 |
+
* @param string $data Marker data which will be base64-encoded.
|
528 |
+
* @param string|null $hash Optional.
|
529 |
+
*
|
530 |
+
* @return string
|
531 |
+
*/
|
532 |
+
public static function build_marker( $name, $data, $hash = null )
|
533 |
+
{
|
534 |
+
// Start the marker, add the data.
|
535 |
+
$marker = '%%' . $name . AUTOPTIMIZE_HASH . '%%' . base64_encode( $data );
|
536 |
+
|
537 |
+
// Add the hash if provided.
|
538 |
+
if ( null !== $hash ) {
|
539 |
+
$marker .= '|' . $hash;
|
540 |
+
}
|
541 |
+
|
542 |
+
// Close the marker.
|
543 |
+
$marker .= '%%' . $name . '%%';
|
544 |
+
|
545 |
+
return $marker;
|
546 |
+
}
|
547 |
+
|
548 |
+
/**
|
549 |
+
* Searches for `$search` in `$content` (using either `preg_match()`
|
550 |
+
* or `strpos()`, depending on whether `$search` is a valid regex pattern or not).
|
551 |
+
* If something is found, it replaces `$content` using `$re_replace_pattern`,
|
552 |
+
* effectively creating our named markers (`%%{$marker}%%`.
|
553 |
+
* These are then at some point replaced back to their actual/original/modified
|
554 |
+
* contents using `autoptimizeBase::restore_marked_content()`.
|
555 |
+
*
|
556 |
+
* @param string $marker Marker name (without percent characters).
|
557 |
+
* @param string $search A string or full blown regex pattern to search for in $content. Uses `strpos()` or `preg_match()`.
|
558 |
+
* @param string $re_replace_pattern Regex pattern to use when replacing contents.
|
559 |
+
* @param string $content Content to work on.
|
560 |
+
*
|
561 |
+
* @return string
|
562 |
+
*/
|
563 |
+
protected function replace_contents_with_marker_if_exists( $marker, $search, $re_replace_pattern, $content )
|
564 |
+
{
|
565 |
+
$found = false;
|
566 |
+
|
567 |
+
$is_regex = autoptimizeUtils::str_is_valid_regex( $search );
|
568 |
+
if ( $is_regex ) {
|
569 |
+
$found = preg_match( $search, $content );
|
570 |
+
} else {
|
571 |
+
$found = ( false !== strpos( $content, $search ) );
|
572 |
+
}
|
573 |
+
|
574 |
+
if ( $found ) {
|
575 |
+
$content = preg_replace_callback(
|
576 |
+
$re_replace_pattern,
|
577 |
+
function( $matches ) use ( $marker ) {
|
578 |
+
return autoptimizeBase::build_marker( $marker, $matches[0] );
|
579 |
+
},
|
580 |
+
$content
|
581 |
+
);
|
582 |
+
}
|
583 |
+
|
584 |
+
return $content;
|
585 |
+
}
|
586 |
+
|
587 |
+
/**
|
588 |
+
* Complements `autoptimizeBase::replace_contents_with_marker_if_exists()`.
|
589 |
+
*
|
590 |
+
* @param string $marker Marker.
|
591 |
+
* @param string $content Markup.
|
592 |
+
*
|
593 |
+
* @return string
|
594 |
+
*/
|
595 |
+
protected function restore_marked_content( $marker, $content )
|
596 |
+
{
|
597 |
+
if ( false !== strpos( $content, $marker ) ) {
|
598 |
+
$content = preg_replace_callback(
|
599 |
+
'#%%' . $marker . AUTOPTIMIZE_HASH . '%%(.*?)%%' . $marker . '%%#is',
|
600 |
+
function ( $matches ) {
|
601 |
+
return base64_decode( $matches[1] );
|
602 |
+
},
|
603 |
+
$content
|
604 |
+
);
|
605 |
+
}
|
606 |
+
|
607 |
+
return $content;
|
608 |
+
}
|
609 |
+
|
610 |
+
/**
|
611 |
+
* Logs given `$data` for debugging purposes (when debug logging is on).
|
612 |
+
*
|
613 |
+
* @param mixed $data Data to log.
|
614 |
+
*
|
615 |
+
* @return void
|
616 |
+
*/
|
617 |
+
protected function debug_log( $data )
|
618 |
+
{
|
619 |
+
if ( ! isset( $this->debug_log ) || ! $this->debug_log ) {
|
620 |
+
return;
|
621 |
+
}
|
622 |
+
|
623 |
+
if ( ! is_string( $data ) && ! is_resource( $data ) ) {
|
624 |
+
$data = var_export( $data, true );
|
625 |
+
}
|
626 |
+
|
627 |
+
error_log( $data );
|
628 |
+
}
|
629 |
+
|
630 |
+
/**
|
631 |
+
* Checks if a single local css/js file can be minified and returns source if so.
|
632 |
+
*
|
633 |
+
* @param string $filepath Filepath.
|
634 |
+
*
|
635 |
+
* @return bool|string to be minified code or false.
|
636 |
+
*/
|
637 |
+
protected function prepare_minify_single( $filepath )
|
638 |
+
{
|
639 |
+
// Decide what we're dealing with, return false if we don't know.
|
640 |
+
if ( $this->str_ends_in( $filepath, '.js' ) ) {
|
641 |
+
$type = 'js';
|
642 |
+
} elseif ( $this->str_ends_in( $filepath, '.css' ) ) {
|
643 |
+
$type = 'css';
|
644 |
+
} else {
|
645 |
+
return false;
|
646 |
+
}
|
647 |
+
|
648 |
+
// Bail if it looks like its already minifed (by having -min or .min
|
649 |
+
// in filename) or if it looks like WP jquery.js (which is minified).
|
650 |
+
$minified_variants = array(
|
651 |
+
'-min.' . $type,
|
652 |
+
'.min.' . $type,
|
653 |
+
'js/jquery/jquery.js',
|
654 |
+
);
|
655 |
+
foreach ( $minified_variants as $ending ) {
|
656 |
+
if ( $this->str_ends_in( $filepath, $ending ) ) {
|
657 |
+
return false;
|
658 |
+
}
|
659 |
+
}
|
660 |
+
|
661 |
+
// Get file contents, bail if empty.
|
662 |
+
$contents = file_get_contents( $filepath );
|
663 |
+
|
664 |
+
return $contents;
|
665 |
+
}
|
666 |
+
|
667 |
+
/**
|
668 |
+
* Given an autoptimizeCache instance returns the (maybe cdn-ed) url of
|
669 |
+
* the cached file.
|
670 |
+
*
|
671 |
+
* @param autoptimizeCache $cache autoptimizeCache instance.
|
672 |
+
*
|
673 |
+
* @return string
|
674 |
+
*/
|
675 |
+
protected function build_minify_single_url( autoptimizeCache $cache )
|
676 |
+
{
|
677 |
+
$url = AUTOPTIMIZE_CACHE_URL . $cache->getname();
|
678 |
+
|
679 |
+
// CDN-replace the resulting URL if needed...
|
680 |
+
$url = $this->url_replace_cdn( $url );
|
681 |
+
|
682 |
+
return $url;
|
683 |
+
}
|
684 |
+
|
685 |
+
/**
|
686 |
+
* Returns true if given $str ends with given $test.
|
687 |
+
*
|
688 |
+
* @param string $str String to check.
|
689 |
+
* @param string $test Ending to match.
|
690 |
+
*
|
691 |
+
* @return bool
|
692 |
+
*/
|
693 |
+
protected function str_ends_in( $str, $test )
|
694 |
+
{
|
695 |
+
// @codingStandardsIgnoreStart
|
696 |
+
// substr_compare() is bugged on 5.5.11: https://3v4l.org/qGYBH
|
697 |
+
// return ( 0 === substr_compare( $str, $test, -strlen( $test ) ) );
|
698 |
+
// @codingStandardsIgnoreEnd
|
699 |
+
|
700 |
+
$length = strlen( $test );
|
701 |
+
|
702 |
+
return ( substr( $str, -$length, $length ) === $test );
|
703 |
+
}
|
704 |
}
|
classes/autoptimizeCLI.php
CHANGED
@@ -1,22 +1,34 @@
|
|
1 |
<?php
|
2 |
-
|
|
|
|
|
|
|
3 |
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
-
|
5 |
}
|
6 |
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
*
|
12 |
-
* @subcommand clear
|
13 |
-
*/
|
14 |
-
public function clear( $args, $args_assoc ) {
|
15 |
-
WP_CLI::line( esc_html__( 'Flushing the cache...', 'autoptimize' ) );
|
16 |
-
autoptimizeCache::clearall();
|
17 |
-
WP_CLI::success( esc_html__( 'Cache flushed.', 'autoptimize' ) );
|
18 |
-
}
|
19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
}
|
21 |
|
22 |
WP_CLI::add_command( 'autoptimize', 'autoptimizeCLI' );
|
1 |
<?php
|
2 |
+
/**
|
3 |
+
* WP-CLI commands for Autoptimize.
|
4 |
+
*/
|
5 |
+
|
6 |
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
}
|
9 |
|
10 |
+
// This is a WP-CLI command, so bail if it's not available.
|
11 |
+
if ( ! defined( 'WP_CLI' ) ) {
|
12 |
+
return;
|
13 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
+
class autoptimizeCLI extends \WP_CLI_Command
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* Clears the cache.
|
19 |
+
*
|
20 |
+
* @subcommand clear
|
21 |
+
*
|
22 |
+
* @param array $args Args.
|
23 |
+
* @param array $args_assoc Associative args.
|
24 |
+
*
|
25 |
+
* @return void
|
26 |
+
*/
|
27 |
+
public function clear( $args, $args_assoc ) {
|
28 |
+
WP_CLI::line( esc_html__( 'Flushing the cache...', 'autoptimize' ) );
|
29 |
+
autoptimizeCache::clearall();
|
30 |
+
WP_CLI::success( esc_html__( 'Cache flushed.', 'autoptimize' ) );
|
31 |
+
}
|
32 |
}
|
33 |
|
34 |
WP_CLI::add_command( 'autoptimize', 'autoptimizeCLI' );
|
classes/autoptimizeCSSmin.php
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Thin wrapper around css minifiers to avoid rewriting a bunch of existing code.
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
+
class autoptimizeCSSmin
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Minifier instance.
|
14 |
+
*
|
15 |
+
* @var Autoptimize\tubalmartin\CssMin\Minifier|null
|
16 |
+
*/
|
17 |
+
protected $minifier = null;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Construtor.
|
21 |
+
*
|
22 |
+
* @param bool $raise_limits Whether to raise memory limits or not. Default true.
|
23 |
+
*/
|
24 |
+
public function __construct( $raise_limits = true )
|
25 |
+
{
|
26 |
+
$this->minifier = new Autoptimize\tubalmartin\CssMin\Minifier( $raise_limits );
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Runs the minifier on given string of $css.
|
31 |
+
* Returns the minified css.
|
32 |
+
*
|
33 |
+
* @param string $css CSS to minify.
|
34 |
+
*
|
35 |
+
* @return string
|
36 |
+
*/
|
37 |
+
public function run( $css )
|
38 |
+
{
|
39 |
+
$result = $this->minifier->run( $css );
|
40 |
+
|
41 |
+
return $result;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Static helper.
|
46 |
+
*
|
47 |
+
* @param string $css CSS to minify.
|
48 |
+
*
|
49 |
+
* @return string
|
50 |
+
*/
|
51 |
+
public static function minify( $css )
|
52 |
+
{
|
53 |
+
$minifier = new self();
|
54 |
+
|
55 |
+
return $minifier->run( $css );
|
56 |
+
}
|
57 |
+
}
|
classes/autoptimizeCache.php
CHANGED
@@ -1,192 +1,508 @@
|
|
1 |
<?php
|
2 |
-
|
|
|
|
|
3 |
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
private $filename;
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
7 |
private $cachedir;
|
8 |
-
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
$this->cachedir = AUTOPTIMIZE_CACHE_DIR;
|
12 |
-
$this->
|
13 |
-
$this->nogzip
|
14 |
-
|
15 |
-
$this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX.$md5.'.php';
|
16 |
} else {
|
17 |
-
if (in_array($ext, array(
|
18 |
-
$this->filename = $ext.'/'.AUTOPTIMIZE_CACHEFILE_PREFIX
|
19 |
} else {
|
20 |
-
$this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX
|
21 |
}
|
22 |
}
|
23 |
}
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
|
|
32 |
}
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
} else {
|
39 |
-
return file_get_contents($this->cachedir
|
40 |
}
|
41 |
}
|
42 |
return false;
|
43 |
}
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
} else {
|
58 |
-
// Write code to cache without doing anything else
|
59 |
-
file_put_contents($this->cachedir
|
60 |
-
if (apply_filters('autoptimize_filter_cache_create_static_gzip', false)) {
|
61 |
-
// Create an additional cached gzip file
|
62 |
-
file_put_contents($this->cachedir
|
63 |
}
|
64 |
}
|
65 |
}
|
66 |
-
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
return $this->filename;
|
70 |
}
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
}
|
81 |
-
|
82 |
-
//
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
}
|
89 |
}
|
90 |
}
|
91 |
|
92 |
-
@unlink(AUTOPTIMIZE_CACHE_DIR.
|
93 |
-
|
94 |
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
}
|
100 |
}
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
}
|
113 |
|
114 |
return true;
|
115 |
}
|
116 |
|
117 |
-
|
118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
|
120 |
-
|
121 |
-
|
122 |
-
if(!
|
123 |
return 0;
|
124 |
}
|
125 |
-
|
126 |
-
|
127 |
-
$count
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
|
|
133 |
}
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
}
|
|
|
148 |
}
|
149 |
}
|
150 |
-
$AOstats=array($count,$size,time());
|
151 |
-
if ($count>100) {
|
152 |
-
set_transient( "autoptimize_stats", $AOstats, apply_filters( 'autoptimize_filter_cache_statsexpiry', HOUR_IN_SECONDS ) );
|
153 |
-
}
|
154 |
}
|
155 |
-
// print the number of instances
|
156 |
-
return $AOstats;
|
157 |
-
}
|
158 |
|
159 |
-
|
160 |
-
|
161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
return false;
|
163 |
}
|
164 |
-
|
165 |
-
foreach (array(
|
166 |
-
if(!
|
167 |
return false;
|
168 |
}
|
169 |
}
|
170 |
-
|
171 |
-
/** write index.html here to avoid prying eyes */
|
172 |
-
$indexFile=AUTOPTIMIZE_CACHE_DIR.'/index.html';
|
173 |
-
if(!is_file($indexFile)) {
|
174 |
-
@file_put_contents($indexFile,'<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>');
|
175 |
-
}
|
176 |
|
177 |
-
|
178 |
-
$
|
179 |
-
if(!is_file($
|
180 |
-
/**
|
181 |
-
*
|
182 |
* whatever htaccess rules you might need
|
183 |
* if you want to override default AO htaccess
|
184 |
*/
|
185 |
-
$htaccess_tmpl=WP_CONTENT_DIR.
|
186 |
-
if (is_file($htaccess_tmpl)) {
|
187 |
-
$
|
188 |
-
}
|
189 |
-
$
|
190 |
ExpiresActive On
|
191 |
ExpiresByType text/css A30672000
|
192 |
ExpiresByType text/javascript A30672000
|
@@ -212,7 +528,7 @@ class autoptimizeCache {
|
|
212 |
</Files>
|
213 |
</IfModule>';
|
214 |
} else {
|
215 |
-
$
|
216 |
ExpiresActive On
|
217 |
ExpiresByType text/css A30672000
|
218 |
ExpiresByType text/javascript A30672000
|
@@ -238,33 +554,108 @@ class autoptimizeCache {
|
|
238 |
</Files>
|
239 |
</IfModule>';
|
240 |
}
|
241 |
-
@file_put_contents($
|
242 |
}
|
243 |
|
244 |
-
// All OK
|
245 |
return true;
|
246 |
}
|
247 |
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
253 |
return false;
|
254 |
}
|
255 |
}
|
256 |
|
257 |
-
//
|
258 |
-
if(!is_writable($dir))
|
259 |
return false;
|
260 |
}
|
261 |
|
262 |
-
//
|
263 |
-
$
|
264 |
-
if(!is_file($
|
265 |
-
@file_put_contents($
|
266 |
}
|
267 |
-
|
268 |
return true;
|
269 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
270 |
}
|
1 |
<?php
|
2 |
+
/**
|
3 |
+
* Handles disk-cache-related operations.
|
4 |
+
*/
|
5 |
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
+
class autoptimizeCache
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Cache filename.
|
14 |
+
*
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
private $filename;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Cache directory path (with a trailing slash).
|
21 |
+
*
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
private $cachedir;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Whether gzipping is done by the web server or us.
|
28 |
+
* True => we don't gzip, the web server does it.
|
29 |
+
* False => we do it ourselves.
|
30 |
+
*
|
31 |
+
* @var bool
|
32 |
+
*/
|
33 |
+
private $nogzip;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Ctor.
|
37 |
+
*
|
38 |
+
* @param string $md5 Hash.
|
39 |
+
* @param string $ext Extension.
|
40 |
+
*/
|
41 |
+
public function __construct( $md5, $ext = 'php' )
|
42 |
+
{
|
43 |
$this->cachedir = AUTOPTIMIZE_CACHE_DIR;
|
44 |
+
$this->nogzip = AUTOPTIMIZE_CACHE_NOGZIP;
|
45 |
+
if ( ! $this->nogzip ) {
|
46 |
+
$this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . '.php';
|
|
|
47 |
} else {
|
48 |
+
if ( in_array( $ext, array( 'js', 'css' ) ) ) {
|
49 |
+
$this->filename = $ext . '/' . AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . '.' . $ext;
|
50 |
} else {
|
51 |
+
$this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . '.' . $ext;
|
52 |
}
|
53 |
}
|
54 |
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Returns true if the cached file exists on disk.
|
58 |
+
*
|
59 |
+
* @return bool
|
60 |
+
*/
|
61 |
+
public function check()
|
62 |
+
{
|
63 |
+
return file_exists( $this->cachedir . $this->filename );
|
64 |
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Returns cache contents if they exist, false otherwise.
|
68 |
+
*
|
69 |
+
* @return string|false
|
70 |
+
*/
|
71 |
+
public function retrieve()
|
72 |
+
{
|
73 |
+
if ( $this->check() ) {
|
74 |
+
if ( false == $this->nogzip ) {
|
75 |
+
return file_get_contents( $this->cachedir . $this->filename . '.none' );
|
76 |
} else {
|
77 |
+
return file_get_contents( $this->cachedir . $this->filename );
|
78 |
}
|
79 |
}
|
80 |
return false;
|
81 |
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Stores given $data in cache.
|
85 |
+
*
|
86 |
+
* @param string $data Data to cache.
|
87 |
+
* @param string $mime Mimetype.
|
88 |
+
*
|
89 |
+
* @return void
|
90 |
+
*/
|
91 |
+
public function cache( $data, $mime )
|
92 |
+
{
|
93 |
+
if ( false === $this->nogzip ) {
|
94 |
+
// We handle gzipping ourselves.
|
95 |
+
$file = 'default.php';
|
96 |
+
$phpcode = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $file );
|
97 |
+
$phpcode = str_replace( array( '%%CONTENT%%', 'exit;' ), array( $mime, '' ), $phpcode );
|
98 |
+
|
99 |
+
file_put_contents( $this->cachedir . $this->filename, $phpcode );
|
100 |
+
file_put_contents( $this->cachedir . $this->filename . '.none', $data );
|
101 |
} else {
|
102 |
+
// Write code to cache without doing anything else.
|
103 |
+
file_put_contents( $this->cachedir . $this->filename, $data );
|
104 |
+
if ( apply_filters( 'autoptimize_filter_cache_create_static_gzip', false ) ) {
|
105 |
+
// Create an additional cached gzip file.
|
106 |
+
file_put_contents( $this->cachedir . $this->filename . '.gz', gzencode( $data, 9, FORCE_GZIP ) );
|
107 |
}
|
108 |
}
|
109 |
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Get cache filename.
|
113 |
+
*
|
114 |
+
* @return string
|
115 |
+
*/
|
116 |
+
public function getname()
|
117 |
+
{
|
118 |
+
// NOTE: This could've maybe been a do_action() instead, however,
|
119 |
+
// that ship has sailed.
|
120 |
+
// The original idea here was to provide 3rd party code a hook so that
|
121 |
+
// it can "listen" to all the complete autoptimized-urls that the page
|
122 |
+
// will emit... Or something to that effect I think?
|
123 |
+
apply_filters( 'autoptimize_filter_cache_getname', AUTOPTIMIZE_CACHE_URL . $this->filename );
|
124 |
+
|
125 |
return $this->filename;
|
126 |
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Returns true if given `$file` is considered a valid Autoptimize cache file,
|
130 |
+
* false otherwise.
|
131 |
+
*
|
132 |
+
* @param string $dir Directory name (with a trailing slash).
|
133 |
+
* @param string $file Filename.
|
134 |
+
* @return bool
|
135 |
+
*/
|
136 |
+
protected static function is_valid_cache_file( $dir, $file )
|
137 |
+
{
|
138 |
+
if ( '.' !== $file && '..' !== $file &&
|
139 |
+
false !== strpos( $file, AUTOPTIMIZE_CACHEFILE_PREFIX ) &&
|
140 |
+
is_file( $dir . $file ) ) {
|
141 |
+
|
142 |
+
// It's a valid file!
|
143 |
+
return true;
|
144 |
}
|
145 |
+
|
146 |
+
// Everything else is considered invalid!
|
147 |
+
return false;
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Clears contents of AUTOPTIMIZE_CACHE_DIR.
|
152 |
+
*
|
153 |
+
* @return void
|
154 |
+
*/
|
155 |
+
protected static function clear_cache_classic()
|
156 |
+
{
|
157 |
+
$contents = self::get_cache_contents();
|
158 |
+
foreach ( $contents as $name => $files ) {
|
159 |
+
$dir = rtrim( AUTOPTIMIZE_CACHE_DIR . $name, '/' ) . '/';
|
160 |
+
foreach ( $files as $file ) {
|
161 |
+
if ( self::is_valid_cache_file( $dir, $file ) ) {
|
162 |
+
@unlink( $dir . $file ); // @codingStandardsIgnoreLine
|
163 |
}
|
164 |
}
|
165 |
}
|
166 |
|
167 |
+
@unlink( AUTOPTIMIZE_CACHE_DIR . '/.htaccess' ); // @codingStandardsIgnoreLine
|
168 |
+
}
|
169 |
|
170 |
+
/**
|
171 |
+
* Recursively deletes the specified pathname (file/directory) if possible.
|
172 |
+
* Returns true on success, false otherwise.
|
173 |
+
*
|
174 |
+
* @param string $pathname Pathname to remove.
|
175 |
+
*
|
176 |
+
* @return bool
|
177 |
+
*/
|
178 |
+
protected static function rmdir( $pathname )
|
179 |
+
{
|
180 |
+
$files = self::get_dir_contents( $pathname );
|
181 |
+
foreach ( $files as $file ) {
|
182 |
+
$path = $pathname . '/' . $file;
|
183 |
+
if ( is_dir( $path ) ) {
|
184 |
+
self::rmdir( $path );
|
185 |
+
} else {
|
186 |
+
unlink( $path );
|
187 |
}
|
188 |
}
|
189 |
+
|
190 |
+
return rmdir( $pathname );
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Clears contents of AUTOPTIMIZE_CACHE_DIR by renaming the current
|
195 |
+
* cache directory into a new one with a unique name and then
|
196 |
+
* re-creating the default (empty) cache directory.
|
197 |
+
*
|
198 |
+
* @return bool Returns true when everything is done successfully, false otherwise.
|
199 |
+
*/
|
200 |
+
protected static function clear_cache_via_rename()
|
201 |
+
{
|
202 |
+
$ok = false;
|
203 |
+
$dir = self::get_pathname_base();
|
204 |
+
$new_name = self::get_unique_name();
|
205 |
+
|
206 |
+
// Makes sure the new pathname is on the same level...
|
207 |
+
$new_pathname = dirname( $dir ) . '/' . $new_name;
|
208 |
+
$renamed = @rename( $dir, $new_pathname ); // @codingStandardsIgnoreLine
|
209 |
+
|
210 |
+
// When renamed, re-create the default cache directory back so it's
|
211 |
+
// available again...
|
212 |
+
if ( $renamed ) {
|
213 |
+
$ok = self::cacheavail();
|
214 |
+
}
|
215 |
+
|
216 |
+
return $ok;
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Returns true when advanced cache clearing is enabled.
|
221 |
+
*
|
222 |
+
* @return bool
|
223 |
+
*/
|
224 |
+
public static function advanced_cache_clear_enabled()
|
225 |
+
{
|
226 |
+
return apply_filters( 'autoptimize_filter_cache_clear_advanced', false );
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Returns a (hopefully) unique new cache folder name for renaming purposes.
|
231 |
+
*
|
232 |
+
* @return string
|
233 |
+
*/
|
234 |
+
protected static function get_unique_name()
|
235 |
+
{
|
236 |
+
$prefix = self::get_advanced_cache_clear_prefix();
|
237 |
+
$new_name = uniqid( $prefix, true );
|
238 |
+
|
239 |
+
return $new_name;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Get cache prefix name used in advanced cache clearing mode.
|
244 |
+
*
|
245 |
+
* @return string
|
246 |
+
*/
|
247 |
+
protected static function get_advanced_cache_clear_prefix()
|
248 |
+
{
|
249 |
+
$pathname = self::get_pathname_base();
|
250 |
+
$basename = basename( $pathname );
|
251 |
+
$prefix = $basename . '-';
|
252 |
+
|
253 |
+
return $prefix;
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Returns an array of file and directory names found within
|
258 |
+
* the given $pathname without '.' and '..' elements.
|
259 |
+
*
|
260 |
+
* @param string $pathname Pathname.
|
261 |
+
*
|
262 |
+
* @return array
|
263 |
+
*/
|
264 |
+
protected static function get_dir_contents( $pathname )
|
265 |
+
{
|
266 |
+
return array_slice( scandir( $pathname ), 2 );
|
267 |
+
}
|
268 |
+
|
269 |
+
/**
|
270 |
+
* Wipes directories which were created as part of the fast cache clearing
|
271 |
+
* routine (which renames the current cache directory into a new one with
|
272 |
+
* a custom-prefixed unique name).
|
273 |
+
*
|
274 |
+
* @return bool
|
275 |
+
*/
|
276 |
+
public static function delete_advanced_cache_clear_artifacts()
|
277 |
+
{
|
278 |
+
$dir = self::get_pathname_base();
|
279 |
+
$prefix = self::get_advanced_cache_clear_prefix();
|
280 |
+
$parent = dirname( $dir );
|
281 |
+
$ok = false;
|
282 |
+
|
283 |
+
// Returns the list of files without '.' and '..' elements.
|
284 |
+
$files = self::get_dir_contents( $parent );
|
285 |
+
foreach ( $files as $file ) {
|
286 |
+
$path = $parent . '/' . $file;
|
287 |
+
$prefixed = ( false !== strpos( $path, $prefix ) );
|
288 |
+
// Removing only our own (prefixed) directories...
|
289 |
+
if ( is_dir( $path ) && $prefixed ) {
|
290 |
+
$ok = self::rmdir( $path );
|
291 |
+
}
|
292 |
+
}
|
293 |
+
|
294 |
+
return $ok;
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* Returns the cache directory pathname used.
|
299 |
+
* Done as a function so we canSlightly different
|
300 |
+
* if multisite is used and `autoptimize_separate_blog_caches` filter
|
301 |
+
* is used.
|
302 |
+
*
|
303 |
+
* @return string
|
304 |
+
*/
|
305 |
+
public static function get_pathname()
|
306 |
+
{
|
307 |
+
$pathname = self::get_pathname_base();
|
308 |
+
|
309 |
+
if ( is_multisite() && apply_filters( 'autoptimize_separate_blog_caches', true ) ) {
|
310 |
+
$blog_id = get_current_blog_id();
|
311 |
+
$pathname .= $blog_id . '/';
|
312 |
+
}
|
313 |
+
|
314 |
+
return $pathname;
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* Returns the base path of our cache directory.
|
319 |
+
*
|
320 |
+
* @return string
|
321 |
+
*/
|
322 |
+
protected static function get_pathname_base()
|
323 |
+
{
|
324 |
+
$pathname = WP_CONTENT_DIR . AUTOPTIMIZE_CACHE_CHILD_DIR;
|
325 |
+
|
326 |
+
return $pathname;
|
327 |
+
}
|
328 |
+
|
329 |
+
/**
|
330 |
+
* Deletes everything from the cache directories.
|
331 |
+
*
|
332 |
+
* @param bool $propagate Whether to trigger additional actions when cache is purged.
|
333 |
+
*
|
334 |
+
* @return bool
|
335 |
+
*/
|
336 |
+
public static function clearall( $propagate = true )
|
337 |
+
{
|
338 |
+
if ( ! self::cacheavail() ) {
|
339 |
+
return false;
|
340 |
+
}
|
341 |
+
|
342 |
+
// TODO/FIXME: If cache is big, switch to advanced/new cache clearing automatically?
|
343 |
+
if ( self::advanced_cache_clear_enabled() ) {
|
344 |
+
self::clear_cache_via_rename();
|
345 |
+
} else {
|
346 |
+
self::clear_cache_classic();
|
347 |
+
}
|
348 |
+
|
349 |
+
// Remove the transient so it gets regenerated...
|
350 |
+
delete_transient( 'autoptimize_stats' );
|
351 |
+
|
352 |
+
// Cache was just purged, clear page cache and allow others to hook into our purging...
|
353 |
+
if ( true === $propagate ) {
|
354 |
+
if ( ! function_exists( 'autoptimize_do_cachepurged_action' ) ) {
|
355 |
+
function autoptimize_do_cachepurged_action() {
|
356 |
+
do_action( 'autoptimize_action_cachepurged' );
|
357 |
+
}
|
358 |
+
}
|
359 |
+
add_action( 'shutdown', 'autoptimize_do_cachepurged_action', 11 );
|
360 |
+
add_action( 'autoptimize_action_cachepurged', array( 'autoptimizeCache', 'flushPageCache' ), 10, 0 );
|
361 |
+
}
|
362 |
+
|
363 |
+
// Warm cache (part of speedupper)!
|
364 |
+
if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
|
365 |
+
$url = site_url() . '/?ao_speedup_cachebuster=' . rand( 1, 100000 );
|
366 |
+
$cache = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
|
367 |
+
unset( $cache );
|
368 |
}
|
369 |
|
370 |
return true;
|
371 |
}
|
372 |
|
373 |
+
/**
|
374 |
+
* Wrapper for clearall but with false param
|
375 |
+
* to ensure the event is not propagated to others
|
376 |
+
* through our own hooks (to avoid infinite loops).
|
377 |
+
*
|
378 |
+
* @return bool
|
379 |
+
*/
|
380 |
+
public static function clearall_actionless()
|
381 |
+
{
|
382 |
+
return self::clearall( false );
|
383 |
+
}
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Returns the contents of our cache dirs.
|
387 |
+
*
|
388 |
+
* @return array
|
389 |
+
*/
|
390 |
+
protected static function get_cache_contents()
|
391 |
+
{
|
392 |
+
$contents = array();
|
393 |
+
|
394 |
+
foreach ( array( '', 'js', 'css' ) as $dir ) {
|
395 |
+
$contents[ $dir ] = scandir( AUTOPTIMIZE_CACHE_DIR . $dir );
|
396 |
+
}
|
397 |
+
|
398 |
+
return $contents;
|
399 |
+
}
|
400 |
+
|
401 |
+
/**
|
402 |
+
* Returns stats about cached contents.
|
403 |
+
*
|
404 |
+
* @return array
|
405 |
+
*/
|
406 |
+
public static function stats()
|
407 |
+
{
|
408 |
+
$stats = get_transient( 'autoptimize_stats' );
|
409 |
|
410 |
+
// If no transient, do the actual scan!
|
411 |
+
if ( ! is_array( $stats ) ) {
|
412 |
+
if ( ! self::cacheavail() ) {
|
413 |
return 0;
|
414 |
}
|
415 |
+
$stats = self::stats_scan();
|
416 |
+
$count = $stats[0];
|
417 |
+
if ( $count > 100 ) {
|
418 |
+
// Store results in transient.
|
419 |
+
set_transient(
|
420 |
+
'autoptimize_stats',
|
421 |
+
$stats,
|
422 |
+
apply_filters( 'autoptimize_filter_cache_statsexpiry', HOUR_IN_SECONDS )
|
423 |
+
);
|
424 |
}
|
425 |
+
}
|
426 |
+
|
427 |
+
return $stats;
|
428 |
+
}
|
429 |
+
|
430 |
+
/**
|
431 |
+
* Performs a scan of cache directory contents and returns an array
|
432 |
+
* with 3 values: count, size, timestamp.
|
433 |
+
* count = total number of found files
|
434 |
+
* size = total filesize (in bytes) of found files
|
435 |
+
* timestamp = unix timestamp when the scan was last performed/finished.
|
436 |
+
*
|
437 |
+
* @return array
|
438 |
+
*/
|
439 |
+
protected static function stats_scan()
|
440 |
+
{
|
441 |
+
$count = 0;
|
442 |
+
$size = 0;
|
443 |
+
|
444 |
+
// Scan everything in our cache directories.
|
445 |
+
foreach ( self::get_cache_contents() as $name => $files ) {
|
446 |
+
$dir = rtrim( AUTOPTIMIZE_CACHE_DIR . $name, '/' ) . '/';
|
447 |
+
foreach ( $files as $file ) {
|
448 |
+
if ( self::is_valid_cache_file( $dir, $file ) ) {
|
449 |
+
if ( AUTOPTIMIZE_CACHE_NOGZIP &&
|
450 |
+
(
|
451 |
+
false !== strpos( $file, '.js' ) ||
|
452 |
+
false !== strpos( $file, '.css' ) ||
|
453 |
+
false !== strpos( $file, '.img' ) ||
|
454 |
+
false !== strpos( $file, '.txt' )
|
455 |
+
)
|
456 |
+
) {
|
457 |
+
// Web server is gzipping, we count .js|.css|.img|.txt files.
|
458 |
+
$count++;
|
459 |
+
} elseif ( ! AUTOPTIMIZE_CACHE_NOGZIP && false !== strpos( $file, '.none' ) ) {
|
460 |
+
// We are gzipping ourselves via php, counting only .none files.
|
461 |
+
$count++;
|
462 |
}
|
463 |
+
$size += filesize( $dir . $file );
|
464 |
}
|
465 |
}
|
|
|
|
|
|
|
|
|
466 |
}
|
|
|
|
|
|
|
467 |
|
468 |
+
$stats = array( $count, $size, time() );
|
469 |
+
|
470 |
+
return $stats;
|
471 |
+
}
|
472 |
+
|
473 |
+
/**
|
474 |
+
* Ensures the cache directory exists, is writeable and contains the
|
475 |
+
* required .htaccess files.
|
476 |
+
* Returns false in case it fails to ensure any of those things.
|
477 |
+
*
|
478 |
+
* @return bool
|
479 |
+
*/
|
480 |
+
public static function cacheavail()
|
481 |
+
{
|
482 |
+
if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
|
483 |
+
// We didn't set a cache.
|
484 |
return false;
|
485 |
}
|
486 |
+
|
487 |
+
foreach ( array( '', 'js', 'css' ) as $dir ) {
|
488 |
+
if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
|
489 |
return false;
|
490 |
}
|
491 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
492 |
|
493 |
+
// Using .htaccess inside our cache folder to overrule wp-super-cache.
|
494 |
+
$htaccess = AUTOPTIMIZE_CACHE_DIR . '/.htaccess';
|
495 |
+
if ( ! is_file( $htaccess ) ) {
|
496 |
+
/**
|
497 |
+
* Create `wp-content/AO_htaccess_tmpl` file with
|
498 |
* whatever htaccess rules you might need
|
499 |
* if you want to override default AO htaccess
|
500 |
*/
|
501 |
+
$htaccess_tmpl = WP_CONTENT_DIR . '/AO_htaccess_tmpl';
|
502 |
+
if ( is_file( $htaccess_tmpl ) ) {
|
503 |
+
$content = file_get_contents( $htaccess_tmpl );
|
504 |
+
} elseif ( is_multisite() || ! AUTOPTIMIZE_CACHE_NOGZIP ) {
|
505 |
+
$content = '<IfModule mod_expires.c>
|
506 |
ExpiresActive On
|
507 |
ExpiresByType text/css A30672000
|
508 |
ExpiresByType text/javascript A30672000
|
528 |
</Files>
|
529 |
</IfModule>';
|
530 |
} else {
|
531 |
+
$content = '<IfModule mod_expires.c>
|
532 |
ExpiresActive On
|
533 |
ExpiresByType text/css A30672000
|
534 |
ExpiresByType text/javascript A30672000
|
554 |
</Files>
|
555 |
</IfModule>';
|
556 |
}
|
557 |
+
@file_put_contents( $htaccess, $content ); // @codingStandardsIgnoreLine
|
558 |
}
|
559 |
|
560 |
+
// All OK!
|
561 |
return true;
|
562 |
}
|
563 |
|
564 |
+
/**
|
565 |
+
* Ensures the specified `$dir` exists and is writeable.
|
566 |
+
* Returns false if that's not the case.
|
567 |
+
*
|
568 |
+
* @param string $dir Directory to check/create.
|
569 |
+
*
|
570 |
+
* @return bool
|
571 |
+
*/
|
572 |
+
protected static function check_cache_dir( $dir )
|
573 |
+
{
|
574 |
+
// Try creating the dir if it doesn't exist.
|
575 |
+
if ( ! file_exists( $dir ) ) {
|
576 |
+
@mkdir( $dir, 0775, true ); // @codingStandardsIgnoreLine
|
577 |
+
if ( ! file_exists( $dir ) ) {
|
578 |
return false;
|
579 |
}
|
580 |
}
|
581 |
|
582 |
+
// If we still cannot write, bail.
|
583 |
+
if ( ! is_writable( $dir ) ) {
|
584 |
return false;
|
585 |
}
|
586 |
|
587 |
+
// Create an index.html in there to avoid prying eyes!
|
588 |
+
$idx_file = rtrim( $dir, '/\\' ) . '/index.html';
|
589 |
+
if ( ! is_file( $idx_file ) ) {
|
590 |
+
@file_put_contents( $idx_file, '<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>' ); // @codingStandardsIgnoreLine
|
591 |
}
|
592 |
+
|
593 |
return true;
|
594 |
}
|
595 |
+
|
596 |
+
/**
|
597 |
+
* Flushes as many page cache plugin's caches as possible.
|
598 |
+
*
|
599 |
+
* @return void
|
600 |
+
*/
|
601 |
+
// @codingStandardsIgnoreStart
|
602 |
+
public static function flushPageCache()
|
603 |
+
{
|
604 |
+
if ( function_exists( 'wp_cache_clear_cache' ) ) {
|
605 |
+
if ( is_multisite() ) {
|
606 |
+
$blog_id = get_current_blog_id();
|
607 |
+
wp_cache_clear_cache( $blog_id );
|
608 |
+
} else {
|
609 |
+
wp_cache_clear_cache();
|
610 |
+
}
|
611 |
+
} elseif ( has_action( 'cachify_flush_cache' ) ) {
|
612 |
+
do_action( 'cachify_flush_cache' );
|
613 |
+
} elseif ( function_exists( 'w3tc_pgcache_flush' ) ) {
|
614 |
+
w3tc_pgcache_flush();
|
615 |
+
} elseif ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
|
616 |
+
wp_fast_cache_bulk_delete_all();
|
617 |
+
} elseif ( class_exists( 'WpFastestCache' ) ) {
|
618 |
+
$wpfc = new WpFastestCache();
|
619 |
+
$wpfc->deleteCache();
|
620 |
+
} elseif ( class_exists( 'c_ws_plugin__qcache_purging_routines' ) ) {
|
621 |
+
c_ws_plugin__qcache_purging_routines::purge_cache_dir(); // quick cache
|
622 |
+
} elseif ( class_exists( 'zencache' ) ) {
|
623 |
+
zencache::clear();
|
624 |
+
} elseif ( class_exists( 'comet_cache' ) ) {
|
625 |
+
comet_cache::clear();
|
626 |
+
} elseif ( class_exists( 'WpeCommon' ) ) {
|
627 |
+
// WPEngine cache purge/flush methods to call by default
|
628 |
+
$wpe_methods = array(
|
629 |
+
'purge_varnish_cache',
|
630 |
+
);
|
631 |
+
|
632 |
+
// More agressive clear/flush/purge behind a filter
|
633 |
+
if ( apply_filters( 'autoptimize_flush_wpengine_aggressive', false ) ) {
|
634 |
+
$wpe_methods = array_merge( $wpe_methods, array( 'purge_memcached', 'clear_maxcdn_cache' ) );
|
635 |
+
}
|
636 |
+
|
637 |
+
// Filtering the entire list of WpeCommon methods to be called (for advanced usage + easier testing)
|
638 |
+
$wpe_methods = apply_filters( 'autoptimize_flush_wpengine_methods', $wpe_methods );
|
639 |
+
|
640 |
+
foreach ( $wpe_methods as $wpe_method ) {
|
641 |
+
if ( method_exists( 'WpeCommon', $wpe_method ) ) {
|
642 |
+
WpeCommon::$wpe_method();
|
643 |
+
}
|
644 |
+
}
|
645 |
+
} elseif ( function_exists( 'sg_cachepress_purge_cache' ) ) {
|
646 |
+
sg_cachepress_purge_cache();
|
647 |
+
} elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
|
648 |
+
// fallback for WP-Super-Cache
|
649 |
+
global $cache_path;
|
650 |
+
if ( is_multisite() ) {
|
651 |
+
$blog_id = get_current_blog_id();
|
652 |
+
prune_super_cache( get_supercache_dir( $blog_id ), true );
|
653 |
+
prune_super_cache( $cache_path . 'blogs/', true );
|
654 |
+
} else {
|
655 |
+
prune_super_cache( $cache_path . 'supercache/', true );
|
656 |
+
prune_super_cache( $cache_path, true );
|
657 |
+
}
|
658 |
+
}
|
659 |
+
}
|
660 |
+
// @codingStandardsIgnoreEnd
|
661 |
}
|
classes/autoptimizeCacheChecker.php
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* CacheChecker - new in AO 2.0
|
4 |
+
*
|
5 |
+
* Daily cronned job (filter to change freq. + filter to disable).
|
6 |
+
* Checks if cachesize is > 0.5GB (size is filterable), if so, an option is set which controls showing an admin notice.
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
class autoptimizeCacheChecker
|
14 |
+
{
|
15 |
+
const SCHEDULE_HOOK = 'ao_cachechecker';
|
16 |
+
|
17 |
+
public function __construct()
|
18 |
+
{
|
19 |
+
}
|
20 |
+
|
21 |
+
public function run()
|
22 |
+
{
|
23 |
+
$this->add_hooks();
|
24 |
+
}
|
25 |
+
|
26 |
+
public function add_hooks()
|
27 |
+
{
|
28 |
+
if ( is_admin() ) {
|
29 |
+
add_action( 'plugins_loaded', array( $this, 'setup' ) );
|
30 |
+
}
|
31 |
+
add_action( self::SCHEDULE_HOOK, array( $this, 'cronjob' ) );
|
32 |
+
add_action( 'admin_notices', array( $this, 'show_admin_notice' ) );
|
33 |
+
}
|
34 |
+
|
35 |
+
public function setup()
|
36 |
+
{
|
37 |
+
$do_cache_check = (bool) apply_filters( 'autoptimize_filter_cachecheck_do', true );
|
38 |
+
$schedule = wp_get_schedule( self::SCHEDULE_HOOK );
|
39 |
+
$frequency = apply_filters( 'autoptimize_filter_cachecheck_frequency', 'daily' );
|
40 |
+
if ( ! in_array( $frequency, array( 'hourly', 'daily', 'weekly', 'monthly' ) ) ) {
|
41 |
+
$frequency = 'daily';
|
42 |
+
}
|
43 |
+
if ( $do_cache_check && ( ! $schedule || $schedule !== $frequency ) ) {
|
44 |
+
wp_schedule_event( time(), $frequency, self::SCHEDULE_HOOK );
|
45 |
+
} elseif ( $schedule && ! $do_cache_check ) {
|
46 |
+
wp_clear_scheduled_hook( self::SCHEDULE_HOOK );
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
public function cronjob()
|
51 |
+
{
|
52 |
+
// Check cachesize and act accordingly.
|
53 |
+
$max_size = (int) apply_filters( 'autoptimize_filter_cachecheck_maxsize', 536870912 );
|
54 |
+
$do_cache_check = (bool) apply_filters( 'autoptimize_filter_cachecheck_do', true );
|
55 |
+
$stat_array = autoptimizeCache::stats();
|
56 |
+
$cache_size = round( $stat_array[1] );
|
57 |
+
if ( ( $cache_size > $max_size ) && ( $do_cache_check ) ) {
|
58 |
+
update_option( 'autoptimize_cachesize_notice', true );
|
59 |
+
if ( apply_filters( 'autoptimize_filter_cachecheck_sendmail', true ) ) {
|
60 |
+
$site_url = esc_url( site_url() );
|
61 |
+
$ao_mailto = apply_filters( 'autoptimize_filter_cachecheck_mailto', get_option( 'admin_email', '' ) );
|
62 |
+
|
63 |
+
$ao_mailsubject = __( 'Autoptimize cache size warning', 'autoptimize' ) . ' (' . $site_url . ')';
|
64 |
+
$ao_mailbody = __( 'Autoptimize\'s cache size is getting big, consider purging the cache. Have a look at https://wordpress.org/plugins/autoptimize/faq/ to see how you can keep the cache size under control.', 'autoptimize' ) . ' (site: ' . $site_url . ')';
|
65 |
+
|
66 |
+
if ( ! empty( $ao_mailto ) ) {
|
67 |
+
$ao_mailresult = wp_mail( $ao_mailto, $ao_mailsubject, $ao_mailbody );
|
68 |
+
if ( ! $ao_mailresult ) {
|
69 |
+
error_log( 'Autoptimize could not send cache size warning mail.' );
|
70 |
+
}
|
71 |
+
}
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
// Check if 3rd party services (e.g. image proxy) are up.
|
76 |
+
autoptimizeUtils::check_service_availability();
|
77 |
+
|
78 |
+
// Check image optimization stats.
|
79 |
+
autoptimizeExtra::get_img_provider_stats();
|
80 |
+
|
81 |
+
// Nukes advanced cache clearing artifacts if they exists...
|
82 |
+
autoptimizeCache::delete_advanced_cache_clear_artifacts();
|
83 |
+
}
|
84 |
+
|
85 |
+
public function show_admin_notice()
|
86 |
+
{
|
87 |
+
// fixme: make notices dismissable.
|
88 |
+
if ( (bool) get_option( 'autoptimize_cachesize_notice', false ) ) {
|
89 |
+
echo '<div class="notice notice-warning"><p>';
|
90 |
+
_e( '<strong>Autoptimize\'s cache size is getting big</strong>, consider purging the cache. Have a look at <a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank" rel="noopener noreferrer">the Autoptimize FAQ</a> to see how you can keep the cache size under control.', 'autoptimize' );
|
91 |
+
echo '</p></div>';
|
92 |
+
update_option( 'autoptimize_cachesize_notice', false );
|
93 |
+
}
|
94 |
+
|
95 |
+
// Notice for image proxy usage.
|
96 |
+
$_imgopt_notice = autoptimizeExtra::get_imgopt_status_notice_wrapper();
|
97 |
+
if ( is_array( $_imgopt_notice ) && array_key_exists( 'status', $_imgopt_notice ) && in_array( $_imgopt_notice['status'], array( 1, -1 ) ) ) {
|
98 |
+
$_dismissible = 'ao-img-opt-notice-';
|
99 |
+
$_hide_notice = '7';
|
100 |
+
|
101 |
+
if ( -1 == $_imgopt_notice['status'] ) {
|
102 |
+
$_hide_notice = '1';
|
103 |
+
}
|
104 |
+
|
105 |
+
$_imgopt_notice_dismissible = apply_filters( 'autoptimize_filter_imgopt_notice_dismissable', $_dismissible . $_hide_notice );
|
106 |
+
|
107 |
+
if ( $_imgopt_notice && PAnD::is_admin_notice_active( $_imgopt_notice_dismissible ) ) {
|
108 |
+
echo '<div class="notice notice-warning is-dismissible" data-dismissible="' . $_imgopt_notice_dismissible . '"><p>' . $_imgopt_notice['notice'] . '</p></div>';
|
109 |
+
}
|
110 |
+
}
|
111 |
+
}
|
112 |
+
}
|
classes/autoptimizeConfig.php
CHANGED
@@ -1,49 +1,63 @@
|
|
1 |
<?php
|
2 |
-
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
3 |
|
4 |
-
class autoptimizeConfig
|
5 |
-
|
|
|
6 |
static private $instance = null;
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
23 |
}
|
24 |
|
25 |
-
//Clean cache?
|
26 |
-
if(get_option('autoptimize_cache_clean')) {
|
27 |
autoptimizeCache::clearall();
|
28 |
-
update_option('autoptimize_cache_clean',0);
|
29 |
}
|
|
|
|
|
30 |
}
|
31 |
|
32 |
-
//
|
33 |
-
//(we
|
34 |
$toolbar = new autoptimizeToolbar();
|
35 |
}
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
40 |
self::$instance = new autoptimizeConfig();
|
41 |
}
|
42 |
-
|
43 |
return self::$instance;
|
44 |
-
|
45 |
-
|
46 |
-
public function show()
|
|
|
|
|
47 |
?>
|
48 |
<style>
|
49 |
/* title and button */
|
@@ -88,12 +102,13 @@ class autoptimizeConfig {
|
|
88 |
.itemTitle {
|
89 |
margin-top: 0;
|
90 |
}
|
91 |
-
|
|
|
92 |
#autoptimize_main .cb_label {display: block; padding-left: 25px; text-indent: -25px;}
|
93 |
|
94 |
/* rss block */
|
95 |
#futtta_feed ul{list-style:outside;}
|
96 |
-
#futtta_feed {font-size:medium; margin:0px 20px;}
|
97 |
|
98 |
/* banner + unslider */
|
99 |
.autoptimize_banner {
|
@@ -138,6 +153,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
138 |
.unslider-arrow.next::before {
|
139 |
content: "\f345";
|
140 |
}
|
|
|
141 |
/* responsive stuff: hide admin-feed on smaller screens */
|
142 |
@media (min-width: 961px) {
|
143 |
#autoptimize_main {float:left;width:69%;}
|
@@ -158,42 +174,38 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
158 |
|
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> which
|
163 |
<?php } ?>
|
164 |
|
165 |
-
<?php if (defined('AUTOPTIMIZE_LEGACY_MINIFIERS')) { ?>
|
166 |
-
<div class="notice-error notice"
|
|
|
|
|
167 |
<?php } ?>
|
168 |
|
169 |
<div id="autoptimize_main">
|
170 |
-
<div id="ao_title_and_button">
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
?>
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
$hiddenClass="hidden ";
|
187 |
-
}
|
188 |
-
?>
|
189 |
-
</span>
|
190 |
-
</h1>
|
191 |
-
</div>
|
192 |
|
193 |
-
<?php echo $this->ao_admin_tabs(); ?>
|
194 |
|
195 |
<form method="post" action="options.php">
|
196 |
-
<?php settings_fields('autoptimize'); ?>
|
197 |
|
198 |
<ul>
|
199 |
|
@@ -214,34 +226,39 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
214 |
|
215 |
<li class="itemDetail">
|
216 |
<h2 class="itemTitle"><?php _e('JavaScript Options','autoptimize'); ?></h2>
|
217 |
-
<table class="form-table">
|
218 |
<tr valign="top">
|
219 |
<th scope="row"><?php _e('Optimize JavaScript Code?','autoptimize'); ?></th>
|
220 |
<td><input type="checkbox" id="autoptimize_js" name="autoptimize_js" <?php echo get_option('autoptimize_js')?'checked="checked" ':''; ?>/></td>
|
221 |
</tr>
|
222 |
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
<th scope="row"><?php _e('Force JavaScript in <head>?','autoptimize'); ?></th>
|
224 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_forcehead" <?php echo get_option('autoptimize_js_forcehead')?'checked="checked" ':''; ?>/>
|
225 |
<?php _e('Load JavaScript early, this can potentially fix some JS-errors, but makes the JS render blocking.','autoptimize'); ?></label></td>
|
226 |
</tr>
|
227 |
<?php if (get_option('autoptimize_js_justhead')) { ?>
|
228 |
-
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
|
229 |
<th scope="row"><?php _e('Look for scripts only in <head>?','autoptimize'); echo ' <i>'. __('(deprecated)','autoptimize') . '</i>'; ?></th>
|
230 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_justhead" <?php echo get_option('autoptimize_js_justhead')?'checked="checked" ':''; ?>/>
|
231 |
<?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>
|
232 |
</tr>
|
233 |
<?php } ?>
|
234 |
-
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
|
235 |
-
<th scope="row"><?php _e('Also aggregate inline JS?','autoptimize'); ?></th>
|
236 |
-
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_include_inline" <?php echo get_option('autoptimize_js_include_inline')?'checked="checked" ':''; ?>/>
|
237 |
-
<?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>
|
238 |
-
</tr>
|
239 |
-
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
|
240 |
<th scope="row"><?php _e('Exclude scripts from Autoptimize:','autoptimize'); ?></th>
|
241 |
<td><label><input type="text" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo get_option('autoptimize_js_exclude',"seal.js, js/jquery/jquery.js"); ?>"/><br />
|
242 |
-
<?php _e('A comma-separated list of scripts you want to exclude from being optimized, for example \'whatever.js, another.js\' (without the quotes) to exclude those scripts from being aggregated
|
243 |
</tr>
|
244 |
-
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
|
245 |
<th scope="row"><?php _e('Add try-catch wrapping?','autoptimize'); ?></th>
|
246 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_trycatch" <?php echo get_option('autoptimize_js_trycatch')?'checked="checked" ':''; ?>/>
|
247 |
<?php _e('If your scripts break because of a JS-error, you might want to try this.','autoptimize'); ?></label></td>
|
@@ -251,43 +268,56 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
251 |
|
252 |
<li class="itemDetail">
|
253 |
<h2 class="itemTitle"><?php _e('CSS Options','autoptimize'); ?></h2>
|
254 |
-
<table class="form-table">
|
255 |
<tr valign="top">
|
256 |
<th scope="row"><?php _e('Optimize CSS Code?','autoptimize'); ?></th>
|
257 |
<td><input type="checkbox" id="autoptimize_css" name="autoptimize_css" <?php echo get_option('autoptimize_css')?'checked="checked" ':''; ?>/></td>
|
258 |
</tr>
|
259 |
<tr class="<?php echo $hiddenClass;?>css_sub ao_adv" valign="top">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
260 |
<th scope="row"><?php _e('Generate data: URIs for images?','autoptimize'); ?></th>
|
261 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_datauris" <?php echo get_option('autoptimize_css_datauris')?'checked="checked" ':''; ?>/>
|
262 |
<?php _e('Enable this to include small background-images in the CSS itself instead of as separate downloads.','autoptimize'); ?></label></td>
|
263 |
</tr>
|
264 |
<?php if (get_option('autoptimize_css_justhead')) { ?>
|
265 |
-
<tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv">
|
266 |
<th scope="row"><?php _e('Look for styles only in <head>?','autoptimize'); echo ' <i>'. __('(deprecated)','autoptimize') . '</i>'; ?></th>
|
267 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_justhead" <?php echo get_option('autoptimize_css_justhead')?'checked="checked" ':''; ?>/>
|
268 |
<?php _e('Don\'t autoptimize CSS outside the head-section. If the cache gets big, you might want to enable this.','autoptimize'); ?></label></td>
|
269 |
</tr>
|
270 |
<?php } ?>
|
271 |
<tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv">
|
272 |
-
<th scope="row"><?php _e('Also aggregate inline CSS?','autoptimize'); ?></th>
|
273 |
-
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_include_inline" <?php echo get_option('autoptimize_css_include_inline','1')?'checked="checked" ':''; ?>/>
|
274 |
-
<?php _e('Check this option for Autoptimize to also aggregate CSS in the HTML.','autoptimize'); ?></label></td>
|
275 |
-
</tr>
|
276 |
-
<tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv">
|
277 |
<th scope="row"><?php _e('Inline and Defer CSS?','autoptimize'); ?></th>
|
278 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_defer" id="autoptimize_css_defer" <?php echo get_option('autoptimize_css_defer')?'checked="checked" ':''; ?>/>
|
279 |
-
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
</tr>
|
281 |
<tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv" id="autoptimize_css_defer_inline">
|
282 |
<th scope="row"></th>
|
283 |
<td><label><textarea rows="10" cols="10" style="width:100%;" placeholder="<?php _e('Paste the above the fold CSS here.','autoptimize'); ?>" name="autoptimize_css_defer_inline"><?php echo get_option('autoptimize_css_defer_inline'); ?></textarea></label></td>
|
284 |
</tr>
|
285 |
-
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv css_sub">
|
286 |
<th scope="row"><?php _e('Inline all CSS?','autoptimize'); ?></th>
|
287 |
<td><label class="cb_label"><input type="checkbox" id="autoptimize_css_inline" name="autoptimize_css_inline" <?php echo get_option('autoptimize_css_inline')?'checked="checked" ':''; ?>/>
|
288 |
<?php _e('Inlining all CSS can improve performance for sites with a low pageviews/ visitor-rate, but may slow down performance otherwise.','autoptimize'); ?></label></td>
|
289 |
</tr>
|
290 |
-
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv css_sub">
|
291 |
<th scope="row"><?php _e('Exclude CSS from Autoptimize:','autoptimize'); ?></th>
|
292 |
<td><label><input type="text" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo get_option('autoptimize_css_exclude','wp-content/cache/, wp-content/uploads/, admin-bar.min.css, dashicons.min.css'); ?>"/><br />
|
293 |
<?php _e('A comma-separated list of CSS you want to exclude from being optimized.','autoptimize'); ?></label></td>
|
@@ -297,7 +327,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
297 |
|
298 |
<li class="itemDetail">
|
299 |
<h2 class="itemTitle"><?php _e('CDN Options','autoptimize'); ?></h2>
|
300 |
-
<table class="form-table">
|
301 |
<tr valign="top">
|
302 |
<th scope="row"><?php _e('CDN Base URL','autoptimize'); ?></th>
|
303 |
<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 />
|
@@ -308,7 +338,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
308 |
|
309 |
<li class="<?php echo $hiddenClass;?>itemDetail ao_adv">
|
310 |
<h2 class="itemTitle"><?php _e('Cache Info','autoptimize'); ?></h2>
|
311 |
-
<table class="form-table" >
|
312 |
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
|
313 |
<th scope="row"><?php _e('Cache folder','autoptimize'); ?></th>
|
314 |
<td><?php echo htmlentities(AUTOPTIMIZE_CACHE_DIR); ?></td>
|
@@ -320,9 +350,15 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
320 |
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
|
321 |
<th scope="row"><?php _e('Cached styles and scripts','autoptimize'); ?></th>
|
322 |
<td><?php
|
323 |
-
$AOstatArr=autoptimizeCache::stats();
|
324 |
-
$
|
325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
?></td>
|
327 |
</tr>
|
328 |
</table>
|
@@ -330,31 +366,27 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
330 |
|
331 |
<li class="<?php echo $hiddenClass;?>itemDetail ao_adv">
|
332 |
<h2 class="itemTitle"><?php _e('Misc Options','autoptimize'); ?></h2>
|
333 |
-
<table class="form-table">
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
</tr>
|
355 |
-
<?php
|
356 |
-
}
|
357 |
-
?>
|
358 |
</table>
|
359 |
</li>
|
360 |
|
@@ -371,25 +403,25 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
371 |
</div>
|
372 |
<div id="autoptimize_admin_feed" class="hidden">
|
373 |
<div class="autoptimize_banner hidden">
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
}
|
385 |
}
|
386 |
}
|
387 |
-
echo $AO_banner;
|
388 |
}
|
389 |
-
|
|
|
|
|
390 |
<li><?php _e("Need help? <a href='https://wordpress.org/plugins/autoptimize/faq/'>Check out the FAQ here</a>.","autoptimize"); ?></li>
|
391 |
<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>
|
392 |
-
|
393 |
</div>
|
394 |
<div style="margin-left:10px;margin-top:-5px;">
|
395 |
<h2>
|
@@ -424,7 +456,8 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
424 |
|
425 |
jQuery(document).ready(function() {
|
426 |
check_ini_state();
|
427 |
-
|
|
|
428 |
jQuery('.autoptimize_banner').unslider({autoplay:true, delay:3500, infinite: false, arrows:{prev:'<a class="unslider-arrow prev"></a>', next:'<a class="unslider-arrow next"></a>'}}).fadeTo("slow",1).show();
|
429 |
|
430 |
jQuery( "#feed_dropdown" ).change(function() {
|
@@ -446,7 +479,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
446 |
if (jQuery("#autoptimize_js").attr('checked')) {
|
447 |
jQuery(".js_sub:visible").fadeTo("fast",1);
|
448 |
}
|
449 |
-
check_ini_state()
|
450 |
jQuery( "input#autoptimize_show_adv" ).val("1");
|
451 |
});
|
452 |
|
@@ -461,7 +494,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
461 |
if (!jQuery("#autoptimize_js").attr('checked')) {
|
462 |
jQuery(".js_sub:visible").fadeTo("fast",.33);
|
463 |
}
|
464 |
-
check_ini_state()
|
465 |
jQuery( "input#autoptimize_show_adv" ).val("0");
|
466 |
});
|
467 |
|
@@ -481,6 +514,14 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
481 |
}
|
482 |
});
|
483 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
484 |
jQuery( "#autoptimize_css" ).change(function() {
|
485 |
if (this.checked) {
|
486 |
jQuery(".css_sub:visible").fadeTo("fast",1);
|
@@ -489,6 +530,14 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
489 |
}
|
490 |
});
|
491 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
492 |
jQuery( "#autoptimize_css_inline" ).change(function() {
|
493 |
if (this.checked) {
|
494 |
jQuery("#autoptimize_css_defer").prop("checked",false);
|
@@ -511,7 +560,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
511 |
show_feed(feedid);
|
512 |
})
|
513 |
|
514 |
-
// validate cdn_url
|
515 |
var cdn_url=document.getElementById("cdn_url");
|
516 |
cdn_url_baseCSS=cdn_url.style.cssText;
|
517 |
if ("validity" in cdn_url) {
|
@@ -533,9 +582,15 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
533 |
if (!jQuery("#autoptimize_css").attr('checked')) {
|
534 |
jQuery(".css_sub:visible").fadeTo('fast',.33);
|
535 |
}
|
|
|
|
|
|
|
536 |
if (!jQuery("#autoptimize_js").attr('checked')) {
|
537 |
jQuery(".js_sub:visible").fadeTo('fast',.33);
|
538 |
}
|
|
|
|
|
|
|
539 |
}
|
540 |
|
541 |
function show_feed(id) {
|
@@ -550,122 +605,186 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
550 |
<?php
|
551 |
}
|
552 |
|
553 |
-
public function addmenu()
|
554 |
-
|
555 |
-
|
556 |
-
add_action( '
|
|
|
557 |
}
|
558 |
|
559 |
-
public function autoptimize_admin_scripts()
|
560 |
-
|
561 |
-
wp_enqueue_script('
|
|
|
562 |
}
|
563 |
|
564 |
-
public function autoptimize_admin_styles()
|
565 |
-
|
566 |
-
wp_enqueue_style('unslider
|
|
|
567 |
}
|
568 |
|
569 |
public function registersettings() {
|
570 |
-
register_setting('autoptimize','autoptimize_html');
|
571 |
-
register_setting('autoptimize','autoptimize_html_keepcomments');
|
572 |
-
register_setting('autoptimize','autoptimize_js');
|
573 |
-
register_setting('autoptimize','
|
574 |
-
register_setting('autoptimize','
|
575 |
-
register_setting('autoptimize','
|
576 |
-
register_setting('autoptimize','
|
577 |
-
register_setting('autoptimize','
|
578 |
-
register_setting('autoptimize','
|
579 |
-
register_setting('autoptimize','
|
580 |
-
register_setting('autoptimize','
|
581 |
-
register_setting('autoptimize','
|
582 |
-
register_setting('autoptimize','
|
583 |
-
register_setting('autoptimize','
|
584 |
-
register_setting('autoptimize','
|
585 |
-
register_setting('autoptimize','
|
586 |
-
register_setting('autoptimize','
|
587 |
-
register_setting('autoptimize','
|
588 |
-
register_setting('autoptimize','
|
589 |
-
register_setting('autoptimize','
|
590 |
-
register_setting('autoptimize','
|
591 |
-
register_setting('autoptimize','
|
|
|
|
|
592 |
}
|
593 |
|
594 |
-
public function setmeta($links
|
595 |
-
|
596 |
-
//
|
|
|
597 |
static $plugin;
|
598 |
-
if(empty($plugin))
|
599 |
-
$plugin = plugin_basename(AUTOPTIMIZE_PLUGIN_DIR.'autoptimize.php');
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
|
|
605 |
} else {
|
606 |
-
//2.8
|
607 |
-
//If it's us, add the link
|
608 |
-
if($file === $plugin) {
|
609 |
-
$newlink = array(sprintf('<a href="options-general.php?page=autoptimize">%s</a>',__('Settings')));
|
610 |
-
$links = array_merge($links
|
611 |
}
|
612 |
}
|
613 |
|
614 |
return $links;
|
615 |
}
|
616 |
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
649 |
}
|
650 |
}
|
651 |
|
652 |
-
//Save for next
|
653 |
$this->config = apply_filters( 'autoptimize_filter_get_config', $config );
|
654 |
}
|
655 |
|
656 |
-
if(isset($this->config[$key]))
|
657 |
-
return $this->config[$key];
|
|
|
658 |
|
659 |
return false;
|
660 |
}
|
661 |
|
662 |
private function getFutttaFeeds($url) {
|
663 |
-
if (
|
664 |
$rss = fetch_feed( $url );
|
665 |
$maxitems = 0;
|
666 |
|
667 |
if ( ! is_wp_error( $rss ) ) {
|
668 |
-
$maxitems = $rss->get_item_quantity( 7 );
|
669 |
$rss_items = $rss->get_items( 0, $maxitems );
|
670 |
}
|
671 |
?>
|
@@ -683,34 +802,60 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
|
|
683 |
<?php endforeach; ?>
|
684 |
<?php endif; ?>
|
685 |
</ul>
|
686 |
-
|
687 |
}
|
688 |
}
|
689 |
|
690 |
// based on http://wordpress.stackexchange.com/a/58826
|
691 |
-
static function ao_admin_tabs()
|
692 |
-
|
693 |
-
$
|
694 |
-
|
695 |
-
|
|
|
|
|
696 |
$currentId = $_GET['page'];
|
697 |
} else {
|
698 |
$currentId = "autoptimize";
|
699 |
}
|
700 |
-
$tabContent .=
|
701 |
-
foreach($tabs as $tabId => $tabName){
|
702 |
-
if($currentId == $tabId){
|
703 |
-
$class =
|
704 |
} else{
|
705 |
-
$class =
|
706 |
}
|
707 |
-
$tabContent .= '<a class="nav-tab'
|
708 |
}
|
709 |
-
$tabContent .=
|
710 |
} else {
|
711 |
-
$tabContent =
|
712 |
}
|
713 |
|
714 |
return $tabContent;
|
715 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
716 |
}
|
1 |
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly.
|
3 |
|
4 |
+
class autoptimizeConfig
|
5 |
+
{
|
6 |
+
private $config = null;
|
7 |
static private $instance = null;
|
8 |
|
9 |
+
private $settings_screen_do_remote_http = true;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Singleton.
|
13 |
+
*/
|
14 |
+
private function __construct()
|
15 |
+
{
|
16 |
+
if ( is_admin() ) {
|
17 |
+
// Add the admin page and settings.
|
18 |
+
add_action( 'admin_menu', array( $this, 'addmenu' ) );
|
19 |
+
add_action( 'admin_init', array( $this, 'registersettings' ) );
|
20 |
+
|
21 |
+
// Set meta info.
|
22 |
+
if ( function_exists( 'plugin_row_meta' ) ) {
|
23 |
+
// 2.8 and higher.
|
24 |
+
add_filter( 'plugin_row_meta', array( $this, 'setmeta' ), 10, 2 );
|
25 |
+
} elseif ( function_exists( 'post_class' ) ) {
|
26 |
+
// 2.7 and lower.
|
27 |
+
$plugin = plugin_basename( AUTOPTIMIZE_PLUGIN_DIR . 'autoptimize.php' );
|
28 |
+
add_filter( 'plugin_action_links_' . $plugin, array( $this, 'setmeta' ) );
|
29 |
}
|
30 |
|
31 |
+
// Clean cache?
|
32 |
+
if ( get_option( 'autoptimize_cache_clean' ) ) {
|
33 |
autoptimizeCache::clearall();
|
34 |
+
update_option( 'autoptimize_cache_clean', 0 );
|
35 |
}
|
36 |
+
|
37 |
+
$this->settings_screen_do_remote_http = apply_filters( 'autoptimize_settingsscreen_remotehttp', $this->settings_screen_do_remote_http );
|
38 |
}
|
39 |
|
40 |
+
// Adds the Autoptimize Toolbar to the Admin bar.
|
41 |
+
// (we load outside the is_admin check so it's also displayed on the frontend toolbar).
|
42 |
$toolbar = new autoptimizeToolbar();
|
43 |
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @return autoptimizeConfig
|
47 |
+
*/
|
48 |
+
static public function instance()
|
49 |
+
{
|
50 |
+
// Only one instance.
|
51 |
+
if ( null === self::$instance ) {
|
52 |
self::$instance = new autoptimizeConfig();
|
53 |
}
|
54 |
+
|
55 |
return self::$instance;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function show()
|
59 |
+
{
|
60 |
+
$conf = self::instance();
|
61 |
?>
|
62 |
<style>
|
63 |
/* title and button */
|
102 |
.itemTitle {
|
103 |
margin-top: 0;
|
104 |
}
|
105 |
+
|
106 |
+
input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weight:normal;}
|
107 |
#autoptimize_main .cb_label {display: block; padding-left: 25px; text-indent: -25px;}
|
108 |
|
109 |
/* rss block */
|
110 |
#futtta_feed ul{list-style:outside;}
|
111 |
+
#futtta_feed {font-size:medium; margin:0px 20px;}
|
112 |
|
113 |
/* banner + unslider */
|
114 |
.autoptimize_banner {
|
153 |
.unslider-arrow.next::before {
|
154 |
content: "\f345";
|
155 |
}
|
156 |
+
|
157 |
/* responsive stuff: hide admin-feed on smaller screens */
|
158 |
@media (min-width: 961px) {
|
159 |
#autoptimize_main {float:left;width:69%;}
|
174 |
|
175 |
<div class="wrap">
|
176 |
|
177 |
+
<?php if ( version_compare( PHP_VERSION, '5.3.0' ) < 0 ) { ?>
|
178 |
+
<div class="notice-error notice"><?php echo '<p>' . sprintf( __( '<strong>You are using a very old version of PHP</strong> (5.2.x or older) which has <a href=%s>serious security and performance issues</a>. Support for PHP 5.5 and below will be removed in one of the next AO released, please ask your hoster to provide you with an upgrade path to 7.x.', 'autoptimize' ), '"http://blog.futtta.be/2016/03/15/why-would-you-still-be-on-php-5-2/" target="_blank"' ) . '</p>'; ?></div>
|
179 |
<?php } ?>
|
180 |
|
181 |
+
<?php if ( defined( 'AUTOPTIMIZE_LEGACY_MINIFIERS' ) ) { ?>
|
182 |
+
<div class="notice-error notice"><p>
|
183 |
+
<?php _e( "You are using the (no longer supported) AUTOPTIMIZE_LEGACY_MINIFIERS constant. Ensure your site is working properly and remove the constant, it doesn't do anything any more.", 'autoptimize' ); ?>
|
184 |
+
</p></div>
|
185 |
<?php } ?>
|
186 |
|
187 |
<div id="autoptimize_main">
|
188 |
+
<div id="ao_title_and_button">
|
189 |
+
<h1 id="ao_title"><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?>
|
190 |
+
<span id="ao_adv_button">
|
191 |
+
<?php if ( get_option( 'autoptimize_show_adv', '0' ) == '1' ) { ?>
|
192 |
+
<a href="javascript:void(0);" id="ao_show_adv" class="button" style="display:none;"><span><?php _e("Show advanced settings","autoptimize") ?></span></a>
|
193 |
+
<a href="javascript:void(0);" id="ao_hide_adv" class="button"><span><?php _e("Hide advanced settings","autoptimize") ?></span></a>
|
194 |
+
<style>tr.ao_adv{display:table-row;} li.ao_adv{display:list-item;}</style>
|
195 |
+
<?php $hiddenClass = ''; ?>
|
196 |
+
<?php } else { ?>
|
197 |
+
<a href="javascript:void(0);" id="ao_show_adv" class="button"><span><?php _e("Show advanced settings","autoptimize") ?></span></a>
|
198 |
+
<a href="javascript:void(0);" id="ao_hide_adv" class="button" style="display:none;"><span><?php _e("Hide advanced settings","autoptimize") ?></span></a>
|
199 |
+
<?php $hiddenClass = 'hidden '; ?>
|
200 |
+
<?php } ?>
|
201 |
+
</span>
|
202 |
+
</h1>
|
203 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
204 |
|
205 |
+
<?php echo $this->ao_admin_tabs(); ?>
|
206 |
|
207 |
<form method="post" action="options.php">
|
208 |
+
<?php settings_fields( 'autoptimize' ); ?>
|
209 |
|
210 |
<ul>
|
211 |
|
226 |
|
227 |
<li class="itemDetail">
|
228 |
<h2 class="itemTitle"><?php _e('JavaScript Options','autoptimize'); ?></h2>
|
229 |
+
<table class="form-table">
|
230 |
<tr valign="top">
|
231 |
<th scope="row"><?php _e('Optimize JavaScript Code?','autoptimize'); ?></th>
|
232 |
<td><input type="checkbox" id="autoptimize_js" name="autoptimize_js" <?php echo get_option('autoptimize_js')?'checked="checked" ':''; ?>/></td>
|
233 |
</tr>
|
234 |
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv">
|
235 |
+
<th scope="row"><?php _e( 'Aggregate JS-files?', 'autoptimize' ); ?></th>
|
236 |
+
<td><label class="cb_label"><input type="checkbox" id="autoptimize_js_aggregate" name="autoptimize_js_aggregate" <?php echo $conf->get( 'autoptimize_js_aggregate' ) ? 'checked="checked" ':''; ?>/>
|
237 |
+
<?php _e( 'Aggregate all linked JS-files to have them loaded non-render blocking? If this option is off, the individual JS-files will remain in place but will be minified.', 'autoptimize' ); ?></label></td>
|
238 |
+
</tr>
|
239 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
|
240 |
+
<th scope="row"><?php _e('Also aggregate inline JS?','autoptimize'); ?></th>
|
241 |
+
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_include_inline" <?php echo get_option('autoptimize_js_include_inline')?'checked="checked" ':''; ?>/>
|
242 |
+
<?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>
|
243 |
+
</tr>
|
244 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
|
245 |
<th scope="row"><?php _e('Force JavaScript in <head>?','autoptimize'); ?></th>
|
246 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_forcehead" <?php echo get_option('autoptimize_js_forcehead')?'checked="checked" ':''; ?>/>
|
247 |
<?php _e('Load JavaScript early, this can potentially fix some JS-errors, but makes the JS render blocking.','autoptimize'); ?></label></td>
|
248 |
</tr>
|
249 |
<?php if (get_option('autoptimize_js_justhead')) { ?>
|
250 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
|
251 |
<th scope="row"><?php _e('Look for scripts only in <head>?','autoptimize'); echo ' <i>'. __('(deprecated)','autoptimize') . '</i>'; ?></th>
|
252 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_justhead" <?php echo get_option('autoptimize_js_justhead')?'checked="checked" ':''; ?>/>
|
253 |
<?php _e('Mostly useful in combination with previous option when using jQuery-based templates, but might help keeping cache size under control.','autoptimize'); ?></label></td>
|
254 |
</tr>
|
255 |
<?php } ?>
|
256 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
|
|
|
|
|
|
|
|
|
|
|
257 |
<th scope="row"><?php _e('Exclude scripts from Autoptimize:','autoptimize'); ?></th>
|
258 |
<td><label><input type="text" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo get_option('autoptimize_js_exclude',"seal.js, js/jquery/jquery.js"); ?>"/><br />
|
259 |
+
<?php _e('A comma-separated list of scripts you want to exclude from being optimized, for example \'whatever.js, another.js\' (without the quotes) to exclude those scripts from being aggregated by Autoptimize.','autoptimize'); ?></label></td>
|
260 |
</tr>
|
261 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>js_sub ao_adv js_aggregate">
|
262 |
<th scope="row"><?php _e('Add try-catch wrapping?','autoptimize'); ?></th>
|
263 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_js_trycatch" <?php echo get_option('autoptimize_js_trycatch')?'checked="checked" ':''; ?>/>
|
264 |
<?php _e('If your scripts break because of a JS-error, you might want to try this.','autoptimize'); ?></label></td>
|
268 |
|
269 |
<li class="itemDetail">
|
270 |
<h2 class="itemTitle"><?php _e('CSS Options','autoptimize'); ?></h2>
|
271 |
+
<table class="form-table">
|
272 |
<tr valign="top">
|
273 |
<th scope="row"><?php _e('Optimize CSS Code?','autoptimize'); ?></th>
|
274 |
<td><input type="checkbox" id="autoptimize_css" name="autoptimize_css" <?php echo get_option('autoptimize_css')?'checked="checked" ':''; ?>/></td>
|
275 |
</tr>
|
276 |
<tr class="<?php echo $hiddenClass;?>css_sub ao_adv" valign="top">
|
277 |
+
<th scope="row"><?php _e( 'Aggregate CSS-files?', 'autoptimize' ); ?></th>
|
278 |
+
<td><label class="cb_label"><input type="checkbox" id="autoptimize_css_aggregate" name="autoptimize_css_aggregate" <?php echo $conf->get( 'autoptimize_css_aggregate' ) ? 'checked="checked" ' : ''; ?>/>
|
279 |
+
<?php _e('Aggregate all linked CSS-files? If this option is off, the individual CSS-files will remain in place but will be minified.', 'autoptimize' ); ?></label></td>
|
280 |
+
</tr>
|
281 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv css_aggregate">
|
282 |
+
<th scope="row"><?php _e('Also aggregate inline CSS?','autoptimize'); ?></th>
|
283 |
+
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_include_inline" <?php echo get_option('autoptimize_css_include_inline','1')?'checked="checked" ':''; ?>/>
|
284 |
+
<?php _e('Check this option for Autoptimize to also aggregate CSS in the HTML.','autoptimize'); ?></label></td>
|
285 |
+
</tr>
|
286 |
+
<tr class="<?php echo $hiddenClass;?>css_sub ao_adv css_aggregate" valign="top">
|
287 |
<th scope="row"><?php _e('Generate data: URIs for images?','autoptimize'); ?></th>
|
288 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_datauris" <?php echo get_option('autoptimize_css_datauris')?'checked="checked" ':''; ?>/>
|
289 |
<?php _e('Enable this to include small background-images in the CSS itself instead of as separate downloads.','autoptimize'); ?></label></td>
|
290 |
</tr>
|
291 |
<?php if (get_option('autoptimize_css_justhead')) { ?>
|
292 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv css_aggregate">
|
293 |
<th scope="row"><?php _e('Look for styles only in <head>?','autoptimize'); echo ' <i>'. __('(deprecated)','autoptimize') . '</i>'; ?></th>
|
294 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_justhead" <?php echo get_option('autoptimize_css_justhead')?'checked="checked" ':''; ?>/>
|
295 |
<?php _e('Don\'t autoptimize CSS outside the head-section. If the cache gets big, you might want to enable this.','autoptimize'); ?></label></td>
|
296 |
</tr>
|
297 |
<?php } ?>
|
298 |
<tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv">
|
|
|
|
|
|
|
|
|
|
|
299 |
<th scope="row"><?php _e('Inline and Defer CSS?','autoptimize'); ?></th>
|
300 |
<td><label class="cb_label"><input type="checkbox" name="autoptimize_css_defer" id="autoptimize_css_defer" <?php echo get_option('autoptimize_css_defer')?'checked="checked" ':''; ?>/>
|
301 |
+
<?php
|
302 |
+
_e( 'Inline "above the fold CSS" while loading the main autoptimized CSS only after page load. <a href="http://wordpress.org/plugins/autoptimize/faq/" target="_blank">Check the FAQ</a> for more info.', 'autoptimize' );
|
303 |
+
if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
|
304 |
+
echo ' ';
|
305 |
+
$critcss_install_url = network_admin_url() . 'plugin-install.php?s=autoptimize+criticalcss&tab=search&type=term';
|
306 |
+
echo sprintf( __( 'This can be fully automated for different types of pages with the %s.', 'autoptimize' ), '<a href="'.$critcss_install_url.'">Autoptimize CriticalCSS Power-Up</a>' );
|
307 |
+
}
|
308 |
+
?>
|
309 |
+
</label></td>
|
310 |
</tr>
|
311 |
<tr valign="top" class="<?php echo $hiddenClass;?>css_sub ao_adv" id="autoptimize_css_defer_inline">
|
312 |
<th scope="row"></th>
|
313 |
<td><label><textarea rows="10" cols="10" style="width:100%;" placeholder="<?php _e('Paste the above the fold CSS here.','autoptimize'); ?>" name="autoptimize_css_defer_inline"><?php echo get_option('autoptimize_css_defer_inline'); ?></textarea></label></td>
|
314 |
</tr>
|
315 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv css_sub css_aggregate">
|
316 |
<th scope="row"><?php _e('Inline all CSS?','autoptimize'); ?></th>
|
317 |
<td><label class="cb_label"><input type="checkbox" id="autoptimize_css_inline" name="autoptimize_css_inline" <?php echo get_option('autoptimize_css_inline')?'checked="checked" ':''; ?>/>
|
318 |
<?php _e('Inlining all CSS can improve performance for sites with a low pageviews/ visitor-rate, but may slow down performance otherwise.','autoptimize'); ?></label></td>
|
319 |
</tr>
|
320 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv css_sub css_aggregate">
|
321 |
<th scope="row"><?php _e('Exclude CSS from Autoptimize:','autoptimize'); ?></th>
|
322 |
<td><label><input type="text" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo get_option('autoptimize_css_exclude','wp-content/cache/, wp-content/uploads/, admin-bar.min.css, dashicons.min.css'); ?>"/><br />
|
323 |
<?php _e('A comma-separated list of CSS you want to exclude from being optimized.','autoptimize'); ?></label></td>
|
327 |
|
328 |
<li class="itemDetail">
|
329 |
<h2 class="itemTitle"><?php _e('CDN Options','autoptimize'); ?></h2>
|
330 |
+
<table class="form-table">
|
331 |
<tr valign="top">
|
332 |
<th scope="row"><?php _e('CDN Base URL','autoptimize'); ?></th>
|
333 |
<td><label><input id="cdn_url" type="text" name="autoptimize_cdn_url" pattern="^(https?:)?\/\/([\da-z\.-]+)\.([\da-z\.]{2,6})([\/\w \.-]*)*(:\d{2,5})?\/?$" style="width:100%" value="<?php echo esc_url(get_option('autoptimize_cdn_url',''),array("http","https")); ?>" /><br />
|
338 |
|
339 |
<li class="<?php echo $hiddenClass;?>itemDetail ao_adv">
|
340 |
<h2 class="itemTitle"><?php _e('Cache Info','autoptimize'); ?></h2>
|
341 |
+
<table class="form-table" >
|
342 |
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
|
343 |
<th scope="row"><?php _e('Cache folder','autoptimize'); ?></th>
|
344 |
<td><?php echo htmlentities(AUTOPTIMIZE_CACHE_DIR); ?></td>
|
350 |
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
|
351 |
<th scope="row"><?php _e('Cached styles and scripts','autoptimize'); ?></th>
|
352 |
<td><?php
|
353 |
+
$AOstatArr = autoptimizeCache::stats();
|
354 |
+
if ( ! empty( $AOstatArr ) && is_array( $AOstatArr ) ) {
|
355 |
+
$AOcacheSize = size_format( $AOstatArr[1], 2 );
|
356 |
+
$details = '';
|
357 |
+
if ( $AOcacheSize > 0 ) {
|
358 |
+
$details = ', ~' . $AOcacheSize . ' total';
|
359 |
+
}
|
360 |
+
printf( __( '%1$s files, totalling %2$s Kbytes (calculated at %3$s)', 'autoptimize' ), $AOstatArr[0], $AOcacheSize, date( 'H:i e', $AOstatArr[2] ) );
|
361 |
+
}
|
362 |
?></td>
|
363 |
</tr>
|
364 |
</table>
|
366 |
|
367 |
<li class="<?php echo $hiddenClass;?>itemDetail ao_adv">
|
368 |
<h2 class="itemTitle"><?php _e('Misc Options','autoptimize'); ?></h2>
|
369 |
+
<table class="form-table">
|
370 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
|
371 |
+
<th scope="row"><?php _e('Save aggregated script/css as static files?','autoptimize'); ?></th>
|
372 |
+
<td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_nogzip" <?php echo get_option('autoptimize_cache_nogzip','1')?'checked="checked" ':''; ?>/>
|
373 |
+
<?php _e('By default files saved are static css/js, uncheck this option if your webserver doesn\'t properly handle the compression and expiry.','autoptimize'); ?></label></td>
|
374 |
+
</tr>
|
375 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
|
376 |
+
<th scope="row"><?php _e('Also optimize for logged in users?','autoptimize'); ?></th>
|
377 |
+
<td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_logged" <?php echo get_option('autoptimize_optimize_logged','1')?'checked="checked" ':''; ?>/>
|
378 |
+
<?php _e('By default Autoptimize is also active for logged on users, uncheck not to optimize when logged in e.g. to use a pagebuilder.','autoptimize'); ?></label></td>
|
379 |
+
</tr>
|
380 |
+
<?php
|
381 |
+
if ( function_exists("is_checkout") || function_exists("is_cart") || function_exists("edd_is_checkout") || function_exists("wpsc_is_cart") || function_exists("wpsc_is_checkout") ) {
|
382 |
+
?>
|
383 |
+
<tr valign="top" class="<?php echo $hiddenClass;?>ao_adv">
|
384 |
+
<th scope="row"><?php _e('Also optimize shop cart/ checkout?','autoptimize'); ?></th>
|
385 |
+
<td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_checkout" <?php echo get_option('autoptimize_optimize_checkout','1')?'checked="checked" ':''; ?>/>
|
386 |
+
<?php _e('By default Autoptimize is also active on your shop\'s cart/ checkout, uncheck not to optimize those.','autoptimize'); ?></label>
|
387 |
+
</td>
|
388 |
+
</tr>
|
389 |
+
<?php } ?>
|
|
|
|
|
|
|
|
|
390 |
</table>
|
391 |
</li>
|
392 |
|
403 |
</div>
|
404 |
<div id="autoptimize_admin_feed" class="hidden">
|
405 |
<div class="autoptimize_banner hidden">
|
406 |
+
<ul>
|
407 |
+
<?php
|
408 |
+
if ( $this->settings_screen_do_remote_http ) {
|
409 |
+
$AO_banner = get_transient( 'autoptimize_banner' );
|
410 |
+
if ( empty( $AO_banner ) ) {
|
411 |
+
$banner_resp = wp_remote_get( 'https://misc.optimizingmatters.com/autoptimize_news.html?ao_ver='.AUTOPTIMIZE_PLUGIN_VERSION );
|
412 |
+
if ( ! is_wp_error( $banner_resp ) ) {
|
413 |
+
if ( '200' == wp_remote_retrieve_response_code( $banner_resp ) ) {
|
414 |
+
$AO_banner = wp_kses_post( wp_remote_retrieve_body( $banner_resp ) );
|
415 |
+
set_transient('autoptimize_banner', $AO_banner, DAY_IN_SECONDS);
|
|
|
416 |
}
|
417 |
}
|
|
|
418 |
}
|
419 |
+
echo $AO_banner;
|
420 |
+
}
|
421 |
+
?>
|
422 |
<li><?php _e("Need help? <a href='https://wordpress.org/plugins/autoptimize/faq/'>Check out the FAQ here</a>.","autoptimize"); ?></li>
|
423 |
<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>
|
424 |
+
</ul>
|
425 |
</div>
|
426 |
<div style="margin-left:10px;margin-top:-5px;">
|
427 |
<h2>
|
456 |
|
457 |
jQuery(document).ready(function() {
|
458 |
check_ini_state();
|
459 |
+
|
460 |
+
jQuery('#autoptimize_admin_feed').fadeTo("slow",1).show();
|
461 |
jQuery('.autoptimize_banner').unslider({autoplay:true, delay:3500, infinite: false, arrows:{prev:'<a class="unslider-arrow prev"></a>', next:'<a class="unslider-arrow next"></a>'}}).fadeTo("slow",1).show();
|
462 |
|
463 |
jQuery( "#feed_dropdown" ).change(function() {
|
479 |
if (jQuery("#autoptimize_js").attr('checked')) {
|
480 |
jQuery(".js_sub:visible").fadeTo("fast",1);
|
481 |
}
|
482 |
+
check_ini_state();
|
483 |
jQuery( "input#autoptimize_show_adv" ).val("1");
|
484 |
});
|
485 |
|
494 |
if (!jQuery("#autoptimize_js").attr('checked')) {
|
495 |
jQuery(".js_sub:visible").fadeTo("fast",.33);
|
496 |
}
|
497 |
+
check_ini_state();
|
498 |
jQuery( "input#autoptimize_show_adv" ).val("0");
|
499 |
});
|
500 |
|
514 |
}
|
515 |
});
|
516 |
|
517 |
+
jQuery( "#autoptimize_js_aggregate" ).change(function() {
|
518 |
+
if (this.checked && jQuery("#autoptimize_js").attr('checked')) {
|
519 |
+
jQuery(".js_aggregate:visible").fadeTo("fast",1);
|
520 |
+
} else {
|
521 |
+
jQuery(".js_aggregate:visible").fadeTo("fast",.33);
|
522 |
+
}
|
523 |
+
});
|
524 |
+
|
525 |
jQuery( "#autoptimize_css" ).change(function() {
|
526 |
if (this.checked) {
|
527 |
jQuery(".css_sub:visible").fadeTo("fast",1);
|
530 |
}
|
531 |
});
|
532 |
|
533 |
+
jQuery( "#autoptimize_css_aggregate" ).change(function() {
|
534 |
+
if (this.checked && jQuery("#autoptimize_css").attr('checked')) {
|
535 |
+
jQuery(".css_aggregate:visible").fadeTo("fast",1);
|
536 |
+
} else {
|
537 |
+
jQuery(".css_aggregate:visible").fadeTo("fast",.33);
|
538 |
+
}
|
539 |
+
});
|
540 |
+
|
541 |
jQuery( "#autoptimize_css_inline" ).change(function() {
|
542 |
if (this.checked) {
|
543 |
jQuery("#autoptimize_css_defer").prop("checked",false);
|
560 |
show_feed(feedid);
|
561 |
})
|
562 |
|
563 |
+
// validate cdn_url.
|
564 |
var cdn_url=document.getElementById("cdn_url");
|
565 |
cdn_url_baseCSS=cdn_url.style.cssText;
|
566 |
if ("validity" in cdn_url) {
|
582 |
if (!jQuery("#autoptimize_css").attr('checked')) {
|
583 |
jQuery(".css_sub:visible").fadeTo('fast',.33);
|
584 |
}
|
585 |
+
if (!jQuery("#autoptimize_css_aggregate").attr('checked')) {
|
586 |
+
jQuery(".css_aggregate:visible").fadeTo('fast',.33);
|
587 |
+
}
|
588 |
if (!jQuery("#autoptimize_js").attr('checked')) {
|
589 |
jQuery(".js_sub:visible").fadeTo('fast',.33);
|
590 |
}
|
591 |
+
if (!jQuery("#autoptimize_js_aggregate").attr('checked')) {
|
592 |
+
jQuery(".js_aggregate:visible").fadeTo('fast',.33);
|
593 |
+
}
|
594 |
}
|
595 |
|
596 |
function show_feed(id) {
|
605 |
<?php
|
606 |
}
|
607 |
|
608 |
+
public function addmenu()
|
609 |
+
{
|
610 |
+
$hook = add_options_page( __( 'Autoptimize Options', 'autoptimize' ), 'Autoptimize', 'manage_options', 'autoptimize', array( $this, 'show' ) );
|
611 |
+
add_action( 'admin_print_scripts-' . $hook, array( $this, 'autoptimize_admin_scripts' ) );
|
612 |
+
add_action( 'admin_print_styles-' . $hook, array( $this, 'autoptimize_admin_styles' ) );
|
613 |
}
|
614 |
|
615 |
+
public function autoptimize_admin_scripts()
|
616 |
+
{
|
617 |
+
wp_enqueue_script( 'jqcookie', plugins_url( '/external/js/jquery.cookie.min.js', __FILE__ ), array( 'jquery' ), null, true );
|
618 |
+
wp_enqueue_script( 'unslider', plugins_url( '/external/js/unslider-min.js', __FILE__ ), array( 'jquery' ), null, true );
|
619 |
}
|
620 |
|
621 |
+
public function autoptimize_admin_styles()
|
622 |
+
{
|
623 |
+
wp_enqueue_style( 'unslider', plugins_url( '/external/js/unslider.css', __FILE__ ) );
|
624 |
+
wp_enqueue_style( 'unslider-dots', plugins_url( '/external/js/unslider-dots.css', __FILE__ ) );
|
625 |
}
|
626 |
|
627 |
public function registersettings() {
|
628 |
+
register_setting( 'autoptimize', 'autoptimize_html' );
|
629 |
+
register_setting( 'autoptimize', 'autoptimize_html_keepcomments' );
|
630 |
+
register_setting( 'autoptimize', 'autoptimize_js' );
|
631 |
+
register_setting( 'autoptimize', 'autoptimize_js_aggregate' );
|
632 |
+
register_setting( 'autoptimize', 'autoptimize_js_exclude' );
|
633 |
+
register_setting( 'autoptimize', 'autoptimize_js_trycatch' );
|
634 |
+
register_setting( 'autoptimize', 'autoptimize_js_justhead' );
|
635 |
+
register_setting( 'autoptimize', 'autoptimize_js_forcehead' );
|
636 |
+
register_setting( 'autoptimize', 'autoptimize_js_include_inline' );
|
637 |
+
register_setting( 'autoptimize', 'autoptimize_css' );
|
638 |
+
register_setting( 'autoptimize', 'autoptimize_css_aggregate' );
|
639 |
+
register_setting( 'autoptimize', 'autoptimize_css_exclude' );
|
640 |
+
register_setting( 'autoptimize', 'autoptimize_css_justhead' );
|
641 |
+
register_setting( 'autoptimize', 'autoptimize_css_datauris' );
|
642 |
+
register_setting( 'autoptimize', 'autoptimize_css_defer' );
|
643 |
+
register_setting( 'autoptimize', 'autoptimize_css_defer_inline' );
|
644 |
+
register_setting( 'autoptimize', 'autoptimize_css_inline' );
|
645 |
+
register_setting( 'autoptimize', 'autoptimize_css_include_inline' );
|
646 |
+
register_setting( 'autoptimize', 'autoptimize_cdn_url' );
|
647 |
+
register_setting( 'autoptimize', 'autoptimize_cache_clean' );
|
648 |
+
register_setting( 'autoptimize', 'autoptimize_cache_nogzip' );
|
649 |
+
register_setting( 'autoptimize', 'autoptimize_show_adv' );
|
650 |
+
register_setting( 'autoptimize', 'autoptimize_optimize_logged' );
|
651 |
+
register_setting( 'autoptimize', 'autoptimize_optimize_checkout' );
|
652 |
}
|
653 |
|
654 |
+
public function setmeta($links, $file = null)
|
655 |
+
{
|
656 |
+
// Inspired on http://wpengineer.com/meta-links-for-wordpress-plugins/.
|
657 |
+
// Do it only once - saves time.
|
658 |
static $plugin;
|
659 |
+
if ( empty( $plugin ) ) {
|
660 |
+
$plugin = plugin_basename( AUTOPTIMIZE_PLUGIN_DIR . 'autoptimize.php' );
|
661 |
+
}
|
662 |
+
|
663 |
+
if ( null === $file ) {
|
664 |
+
// 2.7 and lower.
|
665 |
+
$settings_link = sprintf( '<a href="options-general.php?page=autoptimize">%s</a>', __( 'Settings' ) );
|
666 |
+
array_unshift( $links, $settings_link );
|
667 |
} else {
|
668 |
+
// 2.8 and higher.
|
669 |
+
// If it's us, add the link.
|
670 |
+
if ( $file === $plugin ) {
|
671 |
+
$newlink = array( sprintf( '<a href="options-general.php?page=autoptimize">%s</a>', __( 'Settings' ) ) );
|
672 |
+
$links = array_merge( $links, $newlink );
|
673 |
}
|
674 |
}
|
675 |
|
676 |
return $links;
|
677 |
}
|
678 |
|
679 |
+
/**
|
680 |
+
* @return array
|
681 |
+
*/
|
682 |
+
public static function get_defaults()
|
683 |
+
{
|
684 |
+
static $config = array(
|
685 |
+
'autoptimize_html' => 0,
|
686 |
+
'autoptimize_html_keepcomments' => 0,
|
687 |
+
'autoptimize_js' => 0,
|
688 |
+
'autoptimize_js_aggregate' => 1,
|
689 |
+
'autoptimize_js_exclude' => 'seal.js, js/jquery/jquery.js',
|
690 |
+
'autoptimize_js_trycatch' => 0,
|
691 |
+
'autoptimize_js_justhead' => 0,
|
692 |
+
'autoptimize_js_include_inline' => 0,
|
693 |
+
'autoptimize_js_forcehead' => 0,
|
694 |
+
'autoptimize_css' => 0,
|
695 |
+
'autoptimize_css_aggregate' => 1,
|
696 |
+
'autoptimize_css_exclude' => 'admin-bar.min.css, dashicons.min.css, wp-content/cache/, wp-content/uploads/',
|
697 |
+
'autoptimize_css_justhead' => 0,
|
698 |
+
'autoptimize_css_include_inline' => 1,
|
699 |
+
'autoptimize_css_defer' => 0,
|
700 |
+
'autoptimize_css_defer_inline' => '',
|
701 |
+
'autoptimize_css_inline' => 0,
|
702 |
+
'autoptimize_css_datauris' => 0,
|
703 |
+
'autoptimize_cdn_url' => '',
|
704 |
+
'autoptimize_cache_nogzip' => 1,
|
705 |
+
'autoptimize_show_adv' => 0,
|
706 |
+
'autoptimize_optimize_logged' => 1,
|
707 |
+
'autoptimize_optimize_checkout' => 1
|
708 |
+
);
|
709 |
+
|
710 |
+
return $config;
|
711 |
+
}
|
712 |
+
|
713 |
+
/**
|
714 |
+
* Returns default option values for autoptimizeExtra.
|
715 |
+
*
|
716 |
+
* @return array
|
717 |
+
*/
|
718 |
+
public static function get_ao_extra_default_options()
|
719 |
+
{
|
720 |
+
$defaults = array(
|
721 |
+
'autoptimize_extra_checkbox_field_1' => '0',
|
722 |
+
'autoptimize_extra_checkbox_field_0' => '0',
|
723 |
+
'autoptimize_extra_radio_field_4' => '1',
|
724 |
+
'autoptimize_extra_text_field_2' => '',
|
725 |
+
'autoptimize_extra_text_field_3' => '',
|
726 |
+
'autoptimize_extra_checkbox_field_5' => '0',
|
727 |
+
'autoptimize_extra_select_field_6' => '2',
|
728 |
+
);
|
729 |
+
|
730 |
+
return $defaults;
|
731 |
+
}
|
732 |
+
|
733 |
+
/**
|
734 |
+
* Returns preload polyfill JS.
|
735 |
+
*
|
736 |
+
* @return string
|
737 |
+
*/
|
738 |
+
public static function get_ao_css_preload_polyfill()
|
739 |
+
{
|
740 |
+
$preload_poly = apply_filters('autoptimize_css_preload_polyfill','<script data-cfasync=\'false\'>!function(t){"use strict";t.loadCSS||(t.loadCSS=function(){});var e=loadCSS.relpreload={};if(e.support=function(){var e;try{e=t.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),e.bindMediaToggle=function(t){function e(){t.media=a}var a=t.media||"all";t.addEventListener?t.addEventListener("load",e):t.attachEvent&&t.attachEvent("onload",e),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(e,3e3)},e.poly=function(){if(!e.support())for(var a=t.document.getElementsByTagName("link"),n=0;n<a.length;n++){var o=a[n];"preload"!==o.rel||"style"!==o.getAttribute("as")||o.getAttribute("data-loadcss")||(o.setAttribute("data-loadcss",!0),e.bindMediaToggle(o))}},!e.support()){e.poly();var a=t.setInterval(e.poly,500);t.addEventListener?t.addEventListener("load",function(){e.poly(),t.clearInterval(a)}):t.attachEvent&&t.attachEvent("onload",function(){e.poly(),t.clearInterval(a)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:t.loadCSS=loadCSS}("undefined"!=typeof global?global:this);</script>');
|
741 |
+
return $preload_poly;
|
742 |
+
}
|
743 |
+
|
744 |
+
/**
|
745 |
+
* Returns preload JS onload handler.
|
746 |
+
*
|
747 |
+
* @return string
|
748 |
+
*/
|
749 |
+
public static function get_ao_css_preload_onload()
|
750 |
+
{
|
751 |
+
$preload_onload = apply_filters('autoptimize_filter_css_preload_onload',"this.onload=null;this.rel='stylesheet'");
|
752 |
+
return $preload_onload;
|
753 |
+
}
|
754 |
+
|
755 |
+
public function get($key)
|
756 |
+
{
|
757 |
+
if ( ! is_array( $this->config ) ) {
|
758 |
+
// Default config.
|
759 |
+
$config = self::get_defaults();
|
760 |
+
|
761 |
+
// Override with user settings.
|
762 |
+
foreach ( array_keys( $config ) as $name ) {
|
763 |
+
$conf = get_option( $name );
|
764 |
+
if ( false !== $conf ) {
|
765 |
+
// It was set before!
|
766 |
+
$config[ $name ] = $conf;
|
767 |
}
|
768 |
}
|
769 |
|
770 |
+
// Save for next call.
|
771 |
$this->config = apply_filters( 'autoptimize_filter_get_config', $config );
|
772 |
}
|
773 |
|
774 |
+
if ( isset( $this->config[ $key ] ) ) {
|
775 |
+
return $this->config[ $key ];
|
776 |
+
}
|
777 |
|
778 |
return false;
|
779 |
}
|
780 |
|
781 |
private function getFutttaFeeds($url) {
|
782 |
+
if ( $this->settings_screen_do_remote_http ) {
|
783 |
$rss = fetch_feed( $url );
|
784 |
$maxitems = 0;
|
785 |
|
786 |
if ( ! is_wp_error( $rss ) ) {
|
787 |
+
$maxitems = $rss->get_item_quantity( 7 );
|
788 |
$rss_items = $rss->get_items( 0, $maxitems );
|
789 |
}
|
790 |
?>
|
802 |
<?php endforeach; ?>
|
803 |
<?php endif; ?>
|
804 |
</ul>
|
805 |
+
<?php
|
806 |
}
|
807 |
}
|
808 |
|
809 |
// based on http://wordpress.stackexchange.com/a/58826
|
810 |
+
static function ao_admin_tabs()
|
811 |
+
{
|
812 |
+
$tabs = apply_filters( 'autoptimize_filter_settingsscreen_tabs' ,array( 'autoptimize' => __( 'Main', 'autoptimize' ) ) );
|
813 |
+
$tabContent = '';
|
814 |
+
$tabs_count = count($tabs);
|
815 |
+
if ( $tabs_count > 1 ) {
|
816 |
+
if ( isset( $_GET['page'] ) ) {
|
817 |
$currentId = $_GET['page'];
|
818 |
} else {
|
819 |
$currentId = "autoptimize";
|
820 |
}
|
821 |
+
$tabContent .= '<h2 class="nav-tab-wrapper">';
|
822 |
+
foreach ($tabs as $tabId => $tabName) {
|
823 |
+
if ( $currentId == $tabId ) {
|
824 |
+
$class = ' nav-tab-active';
|
825 |
} else{
|
826 |
+
$class = '';
|
827 |
}
|
828 |
+
$tabContent .= '<a class="nav-tab' . $class . '" href="?page=' . $tabId . '">' . $tabName . '</a>';
|
829 |
}
|
830 |
+
$tabContent .= '</h2>';
|
831 |
} else {
|
832 |
+
$tabContent = '<hr/>';
|
833 |
}
|
834 |
|
835 |
return $tabContent;
|
836 |
}
|
837 |
+
|
838 |
+
/**
|
839 |
+
* Returns true if in admin (and not in admin-ajax.php!)
|
840 |
+
*
|
841 |
+
* @return bool
|
842 |
+
*/
|
843 |
+
public static function is_admin_and_not_ajax()
|
844 |
+
{
|
845 |
+
return ( is_admin() && ! self::doing_ajax() );
|
846 |
+
}
|
847 |
+
|
848 |
+
/**
|
849 |
+
* Returns true if doing ajax.
|
850 |
+
*
|
851 |
+
* @return type
|
852 |
+
*/
|
853 |
+
protected static function doing_ajax()
|
854 |
+
{
|
855 |
+
if ( function_exists( 'wp_doing_ajax' ) ) {
|
856 |
+
return wp_doing_ajax();
|
857 |
+
} else {
|
858 |
+
return ( defined( 'DOING_AJAX' ) && DOING_AJAX );
|
859 |
+
}
|
860 |
+
}
|
861 |
}
|
classes/autoptimizeExtra.php
ADDED
@@ -0,0 +1,921 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Handles autoptimizeExtra frontend features + admin options page
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
+
class autoptimizeExtra
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Options
|
14 |
+
*
|
15 |
+
* @var array
|
16 |
+
*/
|
17 |
+
protected $options = array();
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Creates an instance and calls run().
|
21 |
+
*
|
22 |
+
* @param array $options Optional. Allows overriding options without having to specify them via admin options page.
|
23 |
+
*/
|
24 |
+
public function __construct( $options = array() )
|
25 |
+
{
|
26 |
+
if ( empty( $options ) ) {
|
27 |
+
$options = $this->fetch_options();
|
28 |
+
}
|
29 |
+
|
30 |
+
$this->options = $options;
|
31 |
+
}
|
32 |
+
|
33 |
+
public function run()
|
34 |
+
{
|
35 |
+
if ( is_admin() ) {
|
36 |
+
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
|
37 |
+
add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_extra_tab' ) );
|
38 |
+
} else {
|
39 |
+
$this->run_on_frontend();
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
protected function fetch_options()
|
44 |
+
{
|
45 |
+
$value = get_option( 'autoptimize_extra_settings' );
|
46 |
+
if ( empty( $value ) ) {
|
47 |
+
// Fallback to returning defaults when no stored option exists yet.
|
48 |
+
$value = autoptimizeConfig::get_ao_extra_default_options();
|
49 |
+
}
|
50 |
+
|
51 |
+
// get service availability.
|
52 |
+
$value['availabilities'] = get_option( 'autoptimize_service_availablity' );
|
53 |
+
|
54 |
+
if ( empty( $value['availabilities'] ) ) {
|
55 |
+
$value['availabilities'] = autoptimizeUtils::check_service_availability( true );
|
56 |
+
}
|
57 |
+
|
58 |
+
return $value;
|
59 |
+
}
|
60 |
+
|
61 |
+
public function disable_emojis()
|
62 |
+
{
|
63 |
+
// Removing all actions related to emojis!
|
64 |
+
remove_action( 'admin_print_styles', 'print_emoji_styles' );
|
65 |
+
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
|
66 |
+
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
|
67 |
+
remove_action( 'wp_print_styles', 'print_emoji_styles' );
|
68 |
+
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
|
69 |
+
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
|
70 |
+
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
|
71 |
+
|
72 |
+
// Removes TinyMCE emojis.
|
73 |
+
add_filter( 'tiny_mce_plugins', array( $this, 'filter_disable_emojis_tinymce' ) );
|
74 |
+
|
75 |
+
// Removes emoji dns-preftech.
|
76 |
+
add_filter( 'wp_resource_hints', array( $this, 'filter_remove_emoji_dns_prefetch' ), 10, 2 );
|
77 |
+
}
|
78 |
+
|
79 |
+
public function filter_disable_emojis_tinymce( $plugins )
|
80 |
+
{
|
81 |
+
if ( is_array( $plugins ) ) {
|
82 |
+
return array_diff( $plugins, array( 'wpemoji' ) );
|
83 |
+
} else {
|
84 |
+
return array();
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
public function filter_remove_qs( $src ) {
|
89 |
+
if ( strpos( $src, '?ver=' ) ) {
|
90 |
+
$src = remove_query_arg( 'ver', $src );
|
91 |
+
}
|
92 |
+
|
93 |
+
return $src;
|
94 |
+
}
|
95 |
+
|
96 |
+
public function extra_async_js( $in )
|
97 |
+
{
|
98 |
+
$exclusions = array();
|
99 |
+
if ( ! empty( $in ) ) {
|
100 |
+
$exclusions = array_fill_keys( array_filter( array_map( 'trim', explode( ',', $in ) ) ), '' );
|
101 |
+
}
|
102 |
+
|
103 |
+
$settings = $this->options['autoptimize_extra_text_field_3'];
|
104 |
+
$async = array_fill_keys( array_filter( array_map( 'trim', explode( ',', $settings ) ) ), '' );
|
105 |
+
$attr = apply_filters( 'autoptimize_filter_extra_async', 'async' );
|
106 |
+
foreach ( $async as $k => $v ) {
|
107 |
+
$async[ $k ] = $attr;
|
108 |
+
}
|
109 |
+
|
110 |
+
// Merge exclusions & asyncs in one array and return to AO API.
|
111 |
+
$merged = array_merge( $exclusions, $async );
|
112 |
+
|
113 |
+
return $merged;
|
114 |
+
}
|
115 |
+
|
116 |
+
protected function run_on_frontend()
|
117 |
+
{
|
118 |
+
$options = $this->options;
|
119 |
+
|
120 |
+
// Disable emojis if specified.
|
121 |
+
if ( ! empty( $options['autoptimize_extra_checkbox_field_1'] ) ) {
|
122 |
+
$this->disable_emojis();
|
123 |
+
}
|
124 |
+
|
125 |
+
// Remove version query parameters.
|
126 |
+
if ( ! empty( $options['autoptimize_extra_checkbox_field_0'] ) ) {
|
127 |
+
add_filter( 'script_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
|
128 |
+
add_filter( 'style_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
|
129 |
+
}
|
130 |
+
|
131 |
+
// Making sure is_plugin_active() exists when we need it.
|
132 |
+
if ( ! function_exists( 'is_plugin_active' ) ) {
|
133 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
134 |
+
}
|
135 |
+
// Avoiding conflicts of interest when async-javascript plugin is active!
|
136 |
+
$async_js_plugin_active = false;
|
137 |
+
if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'async-javascript/async-javascript.php' ) ) {
|
138 |
+
$async_js_plugin_active = true;
|
139 |
+
}
|
140 |
+
if ( ! empty( $options['autoptimize_extra_text_field_3'] ) && ! $async_js_plugin_active ) {
|
141 |
+
add_filter( 'autoptimize_filter_js_exclude', array( $this, 'extra_async_js' ), 10, 1 );
|
142 |
+
}
|
143 |
+
|
144 |
+
// Optimize google fonts!
|
145 |
+
if ( ! empty( $options['autoptimize_extra_radio_field_4'] ) && ( '1' !== $options['autoptimize_extra_radio_field_4'] ) ) {
|
146 |
+
add_filter( 'wp_resource_hints', array( $this, 'filter_remove_gfonts_dnsprefetch' ), 10, 2 );
|
147 |
+
add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_optimize_google_fonts' ), 10, 1 );
|
148 |
+
add_filter( 'autoptimize_extra_filter_tobepreconn', array( $this, 'filter_preconnect_google_fonts' ), 10, 1 );
|
149 |
+
}
|
150 |
+
|
151 |
+
// Preconnect!
|
152 |
+
if ( ! empty( $options['autoptimize_extra_text_field_2'] ) || has_filter( 'autoptimize_extra_filter_tobepreconn' ) ) {
|
153 |
+
add_filter( 'wp_resource_hints', array( $this, 'filter_preconnect' ), 10, 2 );
|
154 |
+
}
|
155 |
+
|
156 |
+
// Optimize Images!
|
157 |
+
if ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && 'down' !== $options['availabilities']['extra_imgopt']['status'] && ( 'launch' !== $options['availabilities']['extra_imgopt']['status'] || $this->imgopt_launch_ok() ) ) {
|
158 |
+
if ( apply_filters( 'autoptimize_filter_extra_imgopt_do', true ) ) {
|
159 |
+
add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_optimize_images' ), 10, 1 );
|
160 |
+
$_imgopt_active = true;
|
161 |
+
}
|
162 |
+
if ( apply_filters( 'autoptimize_filter_extra_imgopt_do_css', true ) ) {
|
163 |
+
add_filter( 'autoptimize_filter_base_replace_cdn', array( $this, 'filter_optimize_css_images' ), 10, 1 );
|
164 |
+
$_imgopt_active = true;
|
165 |
+
}
|
166 |
+
if ( $_imgopt_active ) {
|
167 |
+
add_filter( 'autoptimize_extra_filter_tobepreconn', array( $this, 'filter_preconnect_imgopt_url' ), 10, 1 );
|
168 |
+
}
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
public function filter_remove_emoji_dns_prefetch( $urls, $relation_type )
|
173 |
+
{
|
174 |
+
$emoji_svg_url = apply_filters( 'emoji_svg_url', 'https://s.w.org/images/core/emoji/' );
|
175 |
+
|
176 |
+
return $this->filter_remove_dns_prefetch( $urls, $relation_type, $emoji_svg_url );
|
177 |
+
}
|
178 |
+
|
179 |
+
public function filter_remove_gfonts_dnsprefetch( $urls, $relation_type )
|
180 |
+
{
|
181 |
+
return $this->filter_remove_dns_prefetch( $urls, $relation_type, 'fonts.googleapis.com' );
|
182 |
+
}
|
183 |
+
|
184 |
+
public function filter_remove_dns_prefetch( $urls, $relation_type, $url_to_remove )
|
185 |
+
{
|
186 |
+
if ( 'dns-prefetch' === $relation_type ) {
|
187 |
+
$cnt = 0;
|
188 |
+
foreach ( $urls as $url ) {
|
189 |
+
if ( false !== strpos( $url, $url_to_remove ) ) {
|
190 |
+
unset( $urls[ $cnt ] );
|
191 |
+
}
|
192 |
+
$cnt++;
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
return $urls;
|
197 |
+
}
|
198 |
+
|
199 |
+
public function filter_optimize_google_fonts( $in )
|
200 |
+
{
|
201 |
+
// Extract fonts, partly based on wp rocket's extraction code.
|
202 |
+
$markup = preg_replace( '/<!--(.*)-->/Uis', '', $in );
|
203 |
+
preg_match_all( '#<link(?:\s+(?:(?!href\s*=\s*)[^>])+)?(?:\s+href\s*=\s*([\'"])((?:https?:)?\/\/fonts\.googleapis\.com\/css(?:(?!\1).)+)\1)(?:\s+[^>]*)?>#iU', $markup, $matches );
|
204 |
+
|
205 |
+
$fonts_collection = array();
|
206 |
+
if ( ! $matches[2] ) {
|
207 |
+
return $in;
|
208 |
+
}
|
209 |
+
|
210 |
+
// Store them in $fonts array.
|
211 |
+
$i = 0;
|
212 |
+
foreach ( $matches[2] as $font ) {
|
213 |
+
if ( ! preg_match( '/rel=["\']dns-prefetch["\']/', $matches[0][ $i ] ) ) {
|
214 |
+
// Get fonts name.
|
215 |
+
$font = str_replace( array( '%7C', '%7c' ), '|', $font );
|
216 |
+
$font = explode( 'family=', $font );
|
217 |
+
$font = ( isset( $font[1] ) ) ? explode( '&', $font[1] ) : array();
|
218 |
+
// Add font to $fonts[$i] but make sure not to pollute with an empty family!
|
219 |
+
$_thisfont = array_values( array_filter( explode( '|', reset( $font ) ) ) );
|
220 |
+
if ( ! empty( $_thisfont ) ) {
|
221 |
+
$fonts_collection[ $i ]['fonts'] = $_thisfont;
|
222 |
+
// And add subset if any!
|
223 |
+
$subset = ( is_array( $font ) ) ? end( $font ) : '';
|
224 |
+
if ( false !== strpos( $subset, 'subset=' ) ) {
|
225 |
+
$subset = explode( 'subset=', $subset );
|
226 |
+
$fonts_collection[ $i ]['subsets'] = explode( ',', $subset[1] );
|
227 |
+
}
|
228 |
+
}
|
229 |
+
// And remove Google Fonts.
|
230 |
+
$in = str_replace( $matches[0][ $i ], '', $in );
|
231 |
+
}
|
232 |
+
$i++;
|
233 |
+
}
|
234 |
+
|
235 |
+
$options = $this->options;
|
236 |
+
$fonts_markup = '';
|
237 |
+
if ( '2' === $options['autoptimize_extra_radio_field_4'] ) {
|
238 |
+
// Remove Google Fonts.
|
239 |
+
unset( $fonts_collection );
|
240 |
+
return $in;
|
241 |
+
} elseif ( '3' === $options['autoptimize_extra_radio_field_4'] || '5' === $options['autoptimize_extra_radio_field_4'] ) {
|
242 |
+
// Aggregate & link!
|
243 |
+
$fonts_string = '';
|
244 |
+
$subset_string = '';
|
245 |
+
foreach ( $fonts_collection as $font ) {
|
246 |
+
$fonts_string .= '|' . trim( implode( '|', $font['fonts'] ), '|' );
|
247 |
+
if ( ! empty( $font['subsets'] ) ) {
|
248 |
+
$subset_string .= implode( ',', $font['subsets'] );
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
if ( ! empty( $subset_string ) ) {
|
253 |
+
$fonts_string = $fonts_string . '#038;subset=' . $subset_string;
|
254 |
+
}
|
255 |
+
|
256 |
+
$fonts_string = str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) );
|
257 |
+
|
258 |
+
if ( ! empty( $fonts_string ) ) {
|
259 |
+
if ( '5' === $options['autoptimize_extra_radio_field_4'] ) {
|
260 |
+
$rel_string = 'rel="preload" as="style" onload="' . autoptimizeConfig::get_ao_css_preload_onload() . '"';
|
261 |
+
} else {
|
262 |
+
$rel_string = 'rel="stylesheet"';
|
263 |
+
}
|
264 |
+
$fonts_markup = '<link ' . $rel_string . ' id="ao_optimized_gfonts" href="https://fonts.googleapis.com/css?family=' . $fonts_string . '" />';
|
265 |
+
}
|
266 |
+
} elseif ( '4' === $options['autoptimize_extra_radio_field_4'] ) {
|
267 |
+
// Aggregate & load async (webfont.js impl.)!
|
268 |
+
$fonts_array = array();
|
269 |
+
foreach ( $fonts_collection as $_fonts ) {
|
270 |
+
if ( ! empty( $_fonts['subsets'] ) ) {
|
271 |
+
$_subset = implode( ',', $_fonts['subsets'] );
|
272 |
+
foreach ( $_fonts['fonts'] as $key => $_one_font ) {
|
273 |
+
$_one_font = $_one_font . ':' . $_subset;
|
274 |
+
$_fonts['fonts'][ $key ] = $_one_font;
|
275 |
+
}
|
276 |
+
}
|
277 |
+
$fonts_array = array_merge( $fonts_array, $_fonts['fonts'] );
|
278 |
+
}
|
279 |
+
|
280 |
+
$fonts_markup = '<script data-cfasync="false" id="ao_optimized_gfonts" type="text/javascript">WebFontConfig={google:{families:[\'';
|
281 |
+
foreach ( $fonts_array as $fnt ) {
|
282 |
+
$fonts_markup .= $fnt . "','";
|
283 |
+
}
|
284 |
+
$fonts_markup = trim( trim( $fonts_markup, "'" ), ',' );
|
285 |
+
$fonts_markup .= '] },classes:false, events:false, timeout:1500};(function() {var wf = document.createElement(\'script\');wf.src=\'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js\';wf.type=\'text/javascript\';wf.async=\'true\';var s=document.getElementsByTagName(\'script\')[0];s.parentNode.insertBefore(wf, s);})();</script>';
|
286 |
+
}
|
287 |
+
|
288 |
+
// Replace back in markup.
|
289 |
+
$out = substr_replace( $in, $fonts_markup . '<link', strpos( $in, '<link' ), strlen( '<link' ) );
|
290 |
+
unset( $fonts_collection );
|
291 |
+
|
292 |
+
// and insert preload polyfill if "link preload" and if the polyfill isn't there yet (courtesy of inline&defer).
|
293 |
+
$preload_polyfill = autoptimizeConfig::get_ao_css_preload_polyfill();
|
294 |
+
if ( '5' === $options['autoptimize_extra_radio_field_4'] && strpos( $out, $preload_polyfill ) === false ) {
|
295 |
+
$out = str_replace( '</body>', $preload_polyfill . '</body>', $out );
|
296 |
+
}
|
297 |
+
return $out;
|
298 |
+
}
|
299 |
+
|
300 |
+
public function filter_preconnect( $hints, $relation_type )
|
301 |
+
{
|
302 |
+
$options = $this->options;
|
303 |
+
|
304 |
+
// Get settings and store in array.
|
305 |
+
$preconns = array_filter( array_map( 'trim', explode( ',', $options['autoptimize_extra_text_field_2'] ) ) );
|
306 |
+
$preconns = apply_filters( 'autoptimize_extra_filter_tobepreconn', $preconns );
|
307 |
+
|
308 |
+
// Walk array, extract domain and add to new array with crossorigin attribute.
|
309 |
+
foreach ( $preconns as $preconn ) {
|
310 |
+
$parsed = parse_url( $preconn );
|
311 |
+
|
312 |
+
if ( is_array( $parsed ) && empty( $parsed['scheme'] ) ) {
|
313 |
+
$domain = '//' . $parsed['host'];
|
314 |
+
} elseif ( is_array( $parsed ) ) {
|
315 |
+
$domain = $parsed['scheme'] . '://' . $parsed['host'];
|
316 |
+
}
|
317 |
+
|
318 |
+
if ( ! empty( $domain ) ) {
|
319 |
+
$hint = array( 'href' => $domain );
|
320 |
+
// Fonts don't get preconnected unless crossorigin flag is set, non-fonts don't get preconnected if origin flag is set
|
321 |
+
// so hardcode fonts.gstatic.com to come with crossorigin and have filter to add other domains if needed.
|
322 |
+
$crossorigins = apply_filters( 'autoptimize_extra_filter_preconn_crossorigin', array( 'https://fonts.gstatic.com' ) );
|
323 |
+
if ( in_array( $domain, $crossorigins ) ) {
|
324 |
+
$hint['crossorigin'] = 'anonymous';
|
325 |
+
}
|
326 |
+
$new_hints[] = $hint;
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
// Merge in WP's preconnect hints.
|
331 |
+
if ( 'preconnect' === $relation_type && ! empty( $new_hints ) ) {
|
332 |
+
$hints = array_merge( $hints, $new_hints );
|
333 |
+
}
|
334 |
+
|
335 |
+
return $hints;
|
336 |
+
}
|
337 |
+
|
338 |
+
public function filter_preconnect_google_fonts( $in )
|
339 |
+
{
|
340 |
+
if ( '2' !== $this->options['autoptimize_extra_radio_field_4'] ) {
|
341 |
+
// Preconnect to fonts.gstatic.com unless we remove gfonts.
|
342 |
+
$in[] = 'https://fonts.gstatic.com';
|
343 |
+
}
|
344 |
+
|
345 |
+
if ( '4' === $this->options['autoptimize_extra_radio_field_4'] ) {
|
346 |
+
// Preconnect even more hosts for webfont.js!
|
347 |
+
$in[] = 'https://ajax.googleapis.com';
|
348 |
+
$in[] = 'https://fonts.googleapis.com';
|
349 |
+
}
|
350 |
+
|
351 |
+
return $in;
|
352 |
+
}
|
353 |
+
|
354 |
+
public function filter_optimize_images( $in )
|
355 |
+
{
|
356 |
+
/*
|
357 |
+
* potential future functional improvements:
|
358 |
+
*
|
359 |
+
* picture element.
|
360 |
+
* filter for critical CSS.
|
361 |
+
*/
|
362 |
+
|
363 |
+
$imgopt_base_url = $this->get_imgopt_base_url();
|
364 |
+
$to_replace = array();
|
365 |
+
|
366 |
+
// extract img tags.
|
367 |
+
if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
|
368 |
+
foreach ( $matches[0] as $tag ) {
|
369 |
+
$orig_tag = $tag;
|
370 |
+
|
371 |
+
// first do (data-)srcsets.
|
372 |
+
if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
|
373 |
+
foreach ( $allsrcsets as $srcset ) {
|
374 |
+
$srcset = $srcset[2];
|
375 |
+
$srcsets = explode( ',', $srcset );
|
376 |
+
foreach ( $srcsets as $indiv_srcset ) {
|
377 |
+
$indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
|
378 |
+
if ( $indiv_srcset_parts[1] && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
|
379 |
+
$imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
|
380 |
+
}
|
381 |
+
if ( $this->can_optimize_image( $indiv_srcset_parts[0] ) ) {
|
382 |
+
$imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
|
383 |
+
$tag = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
|
384 |
+
$to_replace[ $orig_tag ] = $tag;
|
385 |
+
}
|
386 |
+
}
|
387 |
+
}
|
388 |
+
}
|
389 |
+
|
390 |
+
// proceed with img src.
|
391 |
+
// first get width and height and add to $imgopt_size.
|
392 |
+
if ( preg_match( '#width=("|\')(.*)("|\')#Usmi', $tag, $width ) ) {
|
393 |
+
$imgopt_w = $width[2];
|
394 |
+
}
|
395 |
+
if ( preg_match( '#height=("|\')(.*)("|\')#Usmi', $tag, $height ) ) {
|
396 |
+
$imgopt_h = $height[2];
|
397 |
+
}
|
398 |
+
|
399 |
+
// then start replacing images src.
|
400 |
+
if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
|
401 |
+
foreach ( $urls as $url ) {
|
402 |
+
$full_src_orig = $url[0];
|
403 |
+
$url = $url[1];
|
404 |
+
if ( $this->can_optimize_image( $url ) ) {
|
405 |
+
$imgopt_url = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
|
406 |
+
$full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
|
407 |
+
$tag = str_replace( $full_src_orig, $full_imgopt_src, $tag );
|
408 |
+
$to_replace[ $orig_tag ] = $tag;
|
409 |
+
}
|
410 |
+
}
|
411 |
+
}
|
412 |
+
}
|
413 |
+
}
|
414 |
+
$out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
|
415 |
+
|
416 |
+
// img thumbnails in e.g. woocommerce.
|
417 |
+
if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_extra_imgopt_datathumbs', true ) ) {
|
418 |
+
$out = preg_replace_callback(
|
419 |
+
'/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
|
420 |
+
array( $this, 'replace_data_thumbs' ),
|
421 |
+
$out
|
422 |
+
);
|
423 |
+
}
|
424 |
+
|
425 |
+
return $out;
|
426 |
+
}
|
427 |
+
|
428 |
+
public function filter_optimize_css_images( $in )
|
429 |
+
{
|
430 |
+
$imgopt_base_url = $this->get_imgopt_base_url();
|
431 |
+
$in = $this->normalize_img_urls( $in );
|
432 |
+
|
433 |
+
if ( $this->can_optimize_image( $in ) ) {
|
434 |
+
return $this->build_imgopt_url( $in, '', '' );
|
435 |
+
} else {
|
436 |
+
return $in;
|
437 |
+
}
|
438 |
+
}
|
439 |
+
|
440 |
+
private function get_imgopt_base_url()
|
441 |
+
{
|
442 |
+
static $imgopt_base_url = null;
|
443 |
+
|
444 |
+
if ( is_null( $imgopt_base_url ) ) {
|
445 |
+
$imgopt_host = $this->get_imgopt_host();
|
446 |
+
$quality = $this->get_img_quality_string();
|
447 |
+
$ret_val = apply_filters( 'autoptimize_filter_extra_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
|
448 |
+
$imgopt_base_url = $imgopt_host . 'client/' . $quality . ',' . $ret_val;
|
449 |
+
$imgopt_base_url = apply_filters( 'autoptimize_filter_extra_imgopt_base_url', $imgopt_base_url );
|
450 |
+
}
|
451 |
+
|
452 |
+
return $imgopt_base_url;
|
453 |
+
}
|
454 |
+
|
455 |
+
private function can_optimize_image( $url )
|
456 |
+
{
|
457 |
+
static $cdn_url = null;
|
458 |
+
static $nopti_images = null;
|
459 |
+
|
460 |
+
if ( is_null( $cdn_url ) ) {
|
461 |
+
$cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
|
462 |
+
}
|
463 |
+
|
464 |
+
if ( is_null( $nopti_images ) ) {
|
465 |
+
$nopti_images = apply_filters( 'autoptimize_filter_extra_imgopt_noptimize', '' );
|
466 |
+
}
|
467 |
+
|
468 |
+
$imgopt_base_url = $this->get_imgopt_base_url();
|
469 |
+
$site_host = AUTOPTIMIZE_SITE_DOMAIN;
|
470 |
+
$url_parsed = parse_url( $url );
|
471 |
+
|
472 |
+
if ( $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
|
473 |
+
return false;
|
474 |
+
} elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && $url_parsed['host'] !== $site_host ) {
|
475 |
+
return false;
|
476 |
+
} elseif ( strpos( $url, '.php' ) !== false ) {
|
477 |
+
return false;
|
478 |
+
} elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
|
479 |
+
// fixme: better check against end of string.
|
480 |
+
return false;
|
481 |
+
} elseif ( ! empty( $nopti_images ) ) {
|
482 |
+
$nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
|
483 |
+
foreach ( $nopti_images_array as $nopti_image ) {
|
484 |
+
if ( strpos( $url, $nopti_image ) !== false ) {
|
485 |
+
return false;
|
486 |
+
}
|
487 |
+
}
|
488 |
+
}
|
489 |
+
return true;
|
490 |
+
}
|
491 |
+
|
492 |
+
private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
|
493 |
+
{
|
494 |
+
$filtered_url = apply_filters( 'autoptimize_filter_extra_imgopt_build_url', $orig_url, $width, $height );
|
495 |
+
|
496 |
+
if ( $filtered_url !== $orig_url ) {
|
497 |
+
return $filtered_url;
|
498 |
+
}
|
499 |
+
|
500 |
+
$orig_url = $this->normalize_img_urls( $orig_url );
|
501 |
+
$imgopt_base_url = $this->get_imgopt_base_url();
|
502 |
+
$imgopt_size = '';
|
503 |
+
|
504 |
+
if ( $width && 0 !== $width ) {
|
505 |
+
$imgopt_size = ',w_' . $width;
|
506 |
+
}
|
507 |
+
|
508 |
+
if ( $height && 0 !== $height ) {
|
509 |
+
$imgopt_size .= ',h_' . $height;
|
510 |
+
}
|
511 |
+
|
512 |
+
$url = $imgopt_base_url . $imgopt_size . '/' . $orig_url;
|
513 |
+
|
514 |
+
return $url;
|
515 |
+
}
|
516 |
+
|
517 |
+
public function replace_data_thumbs( $matches )
|
518 |
+
{
|
519 |
+
if ( $this->can_optimize_image( $matches[1] ) ) {
|
520 |
+
return str_replace( $matches[1], $this->build_imgopt_url( $matches[1], 150, 150 ), $matches[0] );
|
521 |
+
} else {
|
522 |
+
return $matches[0];
|
523 |
+
}
|
524 |
+
}
|
525 |
+
|
526 |
+
public function filter_preconnect_imgopt_url( $in )
|
527 |
+
{
|
528 |
+
$imgopt_url_array = parse_url( $this->get_imgopt_base_url() );
|
529 |
+
$in[] = $imgopt_url_array['scheme'] . '://' . $imgopt_url_array['host'];
|
530 |
+
|
531 |
+
return $in;
|
532 |
+
}
|
533 |
+
|
534 |
+
private function normalize_img_urls( $in )
|
535 |
+
{
|
536 |
+
$parsed_site_url = parse_url( site_url() );
|
537 |
+
|
538 |
+
if ( strpos( $in, 'http' ) !== 0 && strpos( $in, '//' ) === 0 ) {
|
539 |
+
$in = $parsed_site_url['scheme'] . ':' . $in;
|
540 |
+
} elseif ( strpos( $in, '/' ) === 0 ) {
|
541 |
+
$in = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $in;
|
542 |
+
}
|
543 |
+
|
544 |
+
return apply_filters( 'autoptimize_filter_extra_imgopt_normalized_url', $in );
|
545 |
+
}
|
546 |
+
|
547 |
+
private function get_img_quality_array()
|
548 |
+
{
|
549 |
+
static $img_quality_array = null;
|
550 |
+
|
551 |
+
if ( is_null( $img_quality_array ) ) {
|
552 |
+
$img_quality_array = array(
|
553 |
+
'1' => 'lossy',
|
554 |
+
'2' => 'glossy',
|
555 |
+
'3' => 'lossless',
|
556 |
+
);
|
557 |
+
$img_quality_array = apply_filters( 'autoptimize_filter_extra_imgopt_quality_array', $img_quality_array );
|
558 |
+
}
|
559 |
+
|
560 |
+
return $img_quality_array;
|
561 |
+
}
|
562 |
+
|
563 |
+
private function get_img_quality_setting()
|
564 |
+
{
|
565 |
+
static $_img_q = null;
|
566 |
+
|
567 |
+
if ( is_null( $_img_q ) ) {
|
568 |
+
$_setting = $this->options['autoptimize_extra_select_field_6'];
|
569 |
+
|
570 |
+
if ( ! $_setting || empty( $_setting ) || ( '1' !== $_setting && '3' !== $_setting ) ) {
|
571 |
+
// default image opt. value is 2 ("glossy").
|
572 |
+
$_img_q = '2';
|
573 |
+
} else {
|
574 |
+
$_img_q = $_setting;
|
575 |
+
}
|
576 |
+
}
|
577 |
+
|
578 |
+
return $_img_q;
|
579 |
+
}
|
580 |
+
|
581 |
+
private function get_img_quality_string()
|
582 |
+
{
|
583 |
+
static $_img_q_string = null;
|
584 |
+
|
585 |
+
if ( is_null( $_img_q_string ) ) {
|
586 |
+
$_quality_array = $this->get_img_quality_array();
|
587 |
+
$_setting = $this->get_img_quality_setting();
|
588 |
+
$_img_q_string = apply_filters( 'autoptimize_filter_extra_imgopt_quality', 'q_' . $_quality_array[ $_setting ] );
|
589 |
+
}
|
590 |
+
|
591 |
+
return $_img_q_string;
|
592 |
+
}
|
593 |
+
|
594 |
+
public static function get_img_provider_stats()
|
595 |
+
{
|
596 |
+
// wrapper around query_img_provider_stats() so we can get to $this->options from cronjob() in autoptimizeCacheChecker.
|
597 |
+
$self = new self();
|
598 |
+
return $self->query_img_provider_stats();
|
599 |
+
}
|
600 |
+
|
601 |
+
public function query_img_provider_stats()
|
602 |
+
{
|
603 |
+
if ( ! empty( $this->options['autoptimize_extra_checkbox_field_5'] ) ) {
|
604 |
+
$_img_provider_stat_url = '';
|
605 |
+
$_img_provider_endpoint = $this->get_imgopt_host() . 'read-domain/';
|
606 |
+
$_site_host = AUTOPTIMIZE_SITE_DOMAIN;
|
607 |
+
|
608 |
+
// make sure parse_url result makes sense, keeping $_img_provider_stat_url empty if not.
|
609 |
+
if ( $_site_host && ! empty( $_site_host ) ) {
|
610 |
+
$_img_provider_stat_url = $_img_provider_endpoint . $_site_host;
|
611 |
+
}
|
612 |
+
|
613 |
+
$_img_provider_stat_url = apply_filters( 'autoptimize_filter_extra_imgopt_stat_url', $_img_provider_stat_url );
|
614 |
+
|
615 |
+
// only do the remote call if $_img_provider_stat_url is not empty to make sure no parse_url weirdness results in useless calls.
|
616 |
+
if ( ! empty( $_img_provider_stat_url ) ) {
|
617 |
+
$_img_stat_resp = wp_remote_get( $_img_provider_stat_url );
|
618 |
+
if ( ! is_wp_error( $_img_stat_resp ) ) {
|
619 |
+
if ( '200' == wp_remote_retrieve_response_code( $_img_stat_resp ) ) {
|
620 |
+
$_img_provider_stat = json_decode( wp_remote_retrieve_body( $_img_stat_resp ), true );
|
621 |
+
update_option( 'autoptimize_imgopt_provider_stat', $_img_provider_stat );
|
622 |
+
}
|
623 |
+
}
|
624 |
+
}
|
625 |
+
}
|
626 |
+
}
|
627 |
+
|
628 |
+
public function imgopt_launch_ok()
|
629 |
+
{
|
630 |
+
static $launch_status = null;
|
631 |
+
|
632 |
+
if ( is_null( $launch_status ) ) {
|
633 |
+
$avail_imgopt = $this->options['availabilities']['extra_imgopt'];
|
634 |
+
$magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
|
635 |
+
$has_launched = get_option( 'autoptimize_imgopt_launched', '' );
|
636 |
+
if ( $has_launched || ( array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
|
637 |
+
$launch_status = true;
|
638 |
+
if ( ! $has_launched ) {
|
639 |
+
update_option( 'autoptimize_imgopt_launched', 'on' );
|
640 |
+
}
|
641 |
+
} else {
|
642 |
+
$launch_status = false;
|
643 |
+
}
|
644 |
+
}
|
645 |
+
|
646 |
+
return $launch_status;
|
647 |
+
}
|
648 |
+
|
649 |
+
public function get_imgopt_host()
|
650 |
+
{
|
651 |
+
static $imgopt_host = null;
|
652 |
+
|
653 |
+
if ( is_null( $imgopt_host ) ) {
|
654 |
+
$avail_imgopt = $this->options['availabilities']['extra_imgopt'];
|
655 |
+
if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
|
656 |
+
$imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
|
657 |
+
} else {
|
658 |
+
$imgopt_host = 'https://cdn.shortpixel.ai/';
|
659 |
+
}
|
660 |
+
}
|
661 |
+
|
662 |
+
return $imgopt_host;
|
663 |
+
}
|
664 |
+
|
665 |
+
public static function get_imgopt_host_wrapper()
|
666 |
+
{
|
667 |
+
// needed for CI tests.
|
668 |
+
$self = new self();
|
669 |
+
return $self->get_imgopt_host();
|
670 |
+
}
|
671 |
+
|
672 |
+
public function get_imgopt_status_notice() {
|
673 |
+
$_extra_options = $this->options;
|
674 |
+
if ( ! empty( $_extra_options ) && is_array( $_extra_options ) && array_key_exists( 'autoptimize_extra_checkbox_field_5', $_extra_options ) && ! empty( $_extra_options['autoptimize_extra_checkbox_field_5'] ) ) {
|
675 |
+
$_imgopt_notice = '';
|
676 |
+
$_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
|
677 |
+
$_site_host = AUTOPTIMIZE_SITE_DOMAIN;
|
678 |
+
$_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
|
679 |
+
|
680 |
+
if ( is_array( $_stat ) ) {
|
681 |
+
if ( 1 == $_stat['Status'] ) {
|
682 |
+
// translators: "add more credits" will appear in a "a href".
|
683 |
+
$_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota is almost used, make sure you %1$sadd more credits%2$s to avoid slowing down your website.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
|
684 |
+
} elseif ( -1 == $_stat['Status'] ) {
|
685 |
+
// translators: "add more credits" will appear in a "a href".
|
686 |
+
$_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota was used, %1$sadd more credits%2$s to keep fast serving optimized images on your site.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
|
687 |
+
} else {
|
688 |
+
$_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
|
689 |
+
// translators: "log in to check your account" will appear in a "a href".
|
690 |
+
$_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
|
691 |
+
}
|
692 |
+
$_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
|
693 |
+
|
694 |
+
return array(
|
695 |
+
'status' => $_stat[ 'Status' ],
|
696 |
+
'notice' => $_imgopt_notice,
|
697 |
+
);
|
698 |
+
}
|
699 |
+
}
|
700 |
+
return false;
|
701 |
+
}
|
702 |
+
|
703 |
+
public static function get_imgopt_status_notice_wrapper() {
|
704 |
+
// needed for notice being shown in autoptimizeCacheChecker.php.
|
705 |
+
$self = new self();
|
706 |
+
return $self->get_imgopt_status_notice();
|
707 |
+
}
|
708 |
+
|
709 |
+
public function admin_menu()
|
710 |
+
{
|
711 |
+
add_submenu_page( null, 'autoptimize_extra', 'autoptimize_extra', 'manage_options', 'autoptimize_extra', array( $this, 'options_page' ) );
|
712 |
+
register_setting( 'autoptimize_extra_settings', 'autoptimize_extra_settings' );
|
713 |
+
}
|
714 |
+
|
715 |
+
public function add_extra_tab( $in )
|
716 |
+
{
|
717 |
+
$in = array_merge( $in, array( 'autoptimize_extra' => __( 'Extra', 'autoptimize' ) ) );
|
718 |
+
|
719 |
+
return $in;
|
720 |
+
}
|
721 |
+
|
722 |
+
public function options_page()
|
723 |
+
{
|
724 |
+
// Working with actual option values from the database here.
|
725 |
+
// That way any saves are still processed as expected, but we can still
|
726 |
+
// override behavior by using `new autoptimizeExtra($custom_options)` and not have that custom
|
727 |
+
// behavior being persisted in the DB even if save is done here.
|
728 |
+
$options = $this->fetch_options();
|
729 |
+
$gfonts = $options['autoptimize_extra_radio_field_4'];
|
730 |
+
$sp_url_suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
|
731 |
+
?>
|
732 |
+
<style>
|
733 |
+
#ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
|
734 |
+
#ao_settings_form .form-table th {font-weight: normal;}
|
735 |
+
#autoptimize_extra_descr{font-size: 120%;}
|
736 |
+
</style>
|
737 |
+
<div class="wrap">
|
738 |
+
<h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
|
739 |
+
<?php echo autoptimizeConfig::ao_admin_tabs(); ?>
|
740 |
+
<?php
|
741 |
+
if ( 'on' !== get_option( 'autoptimize_js' ) && 'on' !== get_option( 'autoptimize_css' ) && 'on' !== get_option( 'autoptimize_html' ) ) {
|
742 |
+
?>
|
743 |
+
<div class="notice-warning notice"><p>
|
744 |
+
<?php
|
745 |
+
_e( 'Most of below Extra optimizations require at least one of HTML, JS or CSS autoptimizations being active.', 'autoptimize' );
|
746 |
+
?>
|
747 |
+
</p></div>
|
748 |
+
<?php
|
749 |
+
}
|
750 |
+
|
751 |
+
if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) {
|
752 |
+
?>
|
753 |
+
<div class="notice-warning notice"><p>
|
754 |
+
<?php
|
755 |
+
// translators: "Autoptimize support forum" will appear in a "a href".
|
756 |
+
echo sprintf( __( 'The image optimization service is currently down, image optimization will be skipped until further notice. Check the %1$sAutoptimize support forum%2$s for more info.', 'autoptimize' ), '<a href="https://wordpress.org/support/plugin/autoptimize/" target="_blank">', '</a>' );
|
757 |
+
?>
|
758 |
+
</p></div>
|
759 |
+
<?php
|
760 |
+
}
|
761 |
+
|
762 |
+
if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] && ! $this->imgopt_launch_ok() ) {
|
763 |
+
?>
|
764 |
+
<div class="notice-warning notice"><p>
|
765 |
+
<?php
|
766 |
+
_e( 'The image optimization service is launching, but not yet available for this domain, it should become available in the next couple of days.', 'autoptimize' );
|
767 |
+
?>
|
768 |
+
</p></div>
|
769 |
+
<?php
|
770 |
+
}
|
771 |
+
|
772 |
+
?>
|
773 |
+
<form id='ao_settings_form' action='options.php' method='post'>
|
774 |
+
<?php settings_fields( 'autoptimize_extra_settings' ); ?>
|
775 |
+
<h2><?php _e( 'Extra Auto-Optimizations', 'autoptimize' ); ?></h2>
|
776 |
+
<span id='autoptimize_extra_descr'><?php _e( 'The following settings can improve your site\'s performance even more.', 'autoptimize' ); ?></span>
|
777 |
+
<table class="form-table">
|
778 |
+
<tr>
|
779 |
+
<th scope="row"><?php _e( 'Google Fonts', 'autoptimize' ); ?></th>
|
780 |
+
<td>
|
781 |
+
<input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="1" <?php if ( ! in_array( $gfonts, array( 2, 3, 4, 5 ) ) ) { echo 'checked'; } ?> ><?php _e( 'Leave as is', 'autoptimize' ); ?><br/>
|
782 |
+
<input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="2" <?php checked( 2, $gfonts, true ); ?> ><?php _e( 'Remove Google Fonts', 'autoptimize' ); ?><br/>
|
783 |
+
<input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="3" <?php checked( 3, $gfonts, true ); ?> ><?php _e( 'Combine and link in head (fonts load fast but are render-blocking)', 'autoptimize' ); ?><br/>
|
784 |
+
<input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="5" <?php checked( 5, $gfonts, true ); ?> ><?php _e( 'Combine and preload in head (fonts load late, but are not render-blocking)', 'autoptimize' ); ?><br/>
|
785 |
+
<input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="4" <?php checked( 4, $gfonts, true ); ?> ><?php _e( 'Combine and load fonts asynchronously with <a href="https://github.com/typekit/webfontloader#readme" target="_blank">webfont.js</a>', 'autoptimize' ); ?><br/>
|
786 |
+
</td>
|
787 |
+
</tr>
|
788 |
+
<tr>
|
789 |
+
<th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
|
790 |
+
<td>
|
791 |
+
<label><input id='autoptimize_imgopt_checkbox' type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_5]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && '1' === $options['autoptimize_extra_checkbox_field_5'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Optimize images on the fly and serve them from a CDN.', 'autoptimize' ); ?></label>
|
792 |
+
<?php
|
793 |
+
// show shortpixel status.
|
794 |
+
$_notice = $this->get_imgopt_status_notice();
|
795 |
+
if ( $_notice ) {
|
796 |
+
switch ( $_notice['status'] ) {
|
797 |
+
case 2:
|
798 |
+
$_notice_color = 'green';
|
799 |
+
break;
|
800 |
+
case 1:
|
801 |
+
$_notice_color = 'orange';
|
802 |
+
break;
|
803 |
+
case -1:
|
804 |
+
$_notice_color = 'red';
|
805 |
+
break;
|
806 |
+
default:
|
807 |
+
$_notice_color = 'green';
|
808 |
+
}
|
809 |
+
echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
|
810 |
+
} else {
|
811 |
+
$upsell_msg_1 = '<p>' . __( 'Get more Google love and improve your website\'s loading speed by having the images optimized on the fly by', 'autoptimize' );
|
812 |
+
$upsell_link = ' <a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">ShortPixel</a> ';
|
813 |
+
$upsell_msg_2 = __( 'and then cached and served fast from a CDN.', 'autoptimize' ) . ' ';
|
814 |
+
if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
|
815 |
+
$upsell_msg_3 = __( 'For a limited time only, this service is offered free-for-all, <b>don\'t miss the chance to test it</b> and see how much it could improve your site\'s speed.', 'autoptimize' );
|
816 |
+
} else {
|
817 |
+
$upsell_msg_3 = __( 'The service is offered for free for 100 images/month regardless of the traffic used. More image optimizations can be purchased starting with $4.99.', 'autoptimize' );
|
818 |
+
}
|
819 |
+
echo apply_filters( 'autoptimize_extra_imgopt_settings_copy', $upsell_msg_1 . $upsell_link . $upsell_msg_2 . $upsell_msg_3 . '</p>' );
|
820 |
+
}
|
821 |
+
echo '<p>' . __( 'Usage of this feature is subject to Shortpixel\'s', 'autoptimize' ) . ' <a href="https://shortpixel.com/tos' . $sp_url_suffix . '" target="_blank">' . __( 'Terms of Use', 'autoptimize' ) . '</a> ' . __( 'and', 'autoptimize' ) . ' <a href="https://shortpixel.com/pp' . $sp_url_suffix . '" target="_blank">Privacy policy</a>.</p>';
|
822 |
+
?>
|
823 |
+
</td>
|
824 |
+
</tr>
|
825 |
+
<tr id='autoptimize_imgopt_quality' <?php if ( ! array_key_exists( 'autoptimize_extra_checkbox_field_5', $options ) || ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && '1' !== $options['autoptimize_extra_checkbox_field_5'] ) ) { echo 'class="hidden"'; } ?>>
|
826 |
+
<th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
|
827 |
+
<td>
|
828 |
+
<label>
|
829 |
+
<select name='autoptimize_extra_settings[autoptimize_extra_select_field_6]'>
|
830 |
+
<?php
|
831 |
+
$_imgopt_array = $this->get_img_quality_array();
|
832 |
+
$_imgopt_val = $this->get_img_quality_setting();
|
833 |
+
|
834 |
+
foreach ( $_imgopt_array as $key => $value ) {
|
835 |
+
echo '<option value="' . $key . '"';
|
836 |
+
if ( $_imgopt_val == $key ) {
|
837 |
+
echo ' selected';
|
838 |
+
}
|
839 |
+
echo '>' . ucfirst( $value ) . '</option>';
|
840 |
+
}
|
841 |
+
echo "\n";
|
842 |
+
?>
|
843 |
+
</select>
|
844 |
+
</label>
|
845 |
+
<p><?php echo apply_filters( 'autoptimize_extra_imgopt_quality_copy', __( 'You can', 'autoptimize' ) . ' <a href="https://shortpixel.com/oic' . $sp_url_suffix . '" target="_blank">' . __( 'test compression levels here', 'autoptimize' ) . '</a>.' ); ?></p>
|
846 |
+
</td>
|
847 |
+
</tr>
|
848 |
+
<tr>
|
849 |
+
<th scope="row"><?php _e( 'Remove emojis', 'autoptimize' ); ?></th>
|
850 |
+
<td>
|
851 |
+
<label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_1'] ) && '1' === $options['autoptimize_extra_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Removes WordPress\' core emojis\' inline CSS, inline JavaScript, and an otherwise un-autoptimized JavaScript file.', 'autoptimize' ); ?></label>
|
852 |
+
</td>
|
853 |
+
</tr>
|
854 |
+
<tr>
|
855 |
+
<th scope="row"><?php _e( 'Remove query strings from static resources', 'autoptimize' ); ?></th>
|
856 |
+
<td>
|
857 |
+
<label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_0]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_0'] ) && '1' === $options['autoptimize_extra_checkbox_field_0'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Removing query strings (or more specifically the <code>ver</code> parameter) will not improve load time, but might improve performance scores.', 'autoptimize' ); ?></label>
|
858 |
+
</td>
|
859 |
+
</tr>
|
860 |
+
<tr>
|
861 |
+
<th scope="row"><?php _e( 'Preconnect to 3rd party domains <em>(advanced users)</em>', 'autoptimize' ); ?></th>
|
862 |
+
<td>
|
863 |
+
<label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_2]' value='<?php echo esc_attr( $options['autoptimize_extra_text_field_2'] ); ?>'><br /><?php _e( 'Add 3rd party domains you want the browser to <a href="https://www.keycdn.com/support/preconnect/#primary" target="_blank">preconnect</a> to, separated by comma\'s. Make sure to include the correct protocol (HTTP or HTTPS).', 'autoptimize' ); ?></label>
|
864 |
+
</td>
|
865 |
+
</tr>
|
866 |
+
<tr>
|
867 |
+
<th scope="row"><?php _e( 'Async Javascript-files <em>(advanced users)</em>', 'autoptimize' ); ?></th>
|
868 |
+
<td>
|
869 |
+
<?php
|
870 |
+
if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'async-javascript/async-javascript.php' ) ) {
|
871 |
+
_e( 'You have "Async JavaScript" installed,', 'autoptimize' );
|
872 |
+
$asj_config_url = 'options-general.php?page=async-javascript';
|
873 |
+
echo sprintf( ' <a href="' . $asj_config_url . '">%s</a>', __( 'configuration of async javascript is best done there.', 'autoptimize' ) );
|
874 |
+
} else {
|
875 |
+
?>
|
876 |
+
<input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_3]' value='<?php echo esc_attr( $options['autoptimize_extra_text_field_3'] ); ?>'>
|
877 |
+
<br />
|
878 |
+
<?php
|
879 |
+
_e( 'Comma-separated list of local or 3rd party JS-files that should loaded with the <code>async</code> flag. JS-files from your own site will be automatically excluded if added here. ', 'autoptimize' );
|
880 |
+
// translators: %s will be replaced by a link to the "async javascript" plugin.
|
881 |
+
echo sprintf( __( 'Configuration of async javascript is easier and more flexible using the %s plugin.', 'autoptimize' ), '"<a href="https://wordpress.org/plugins/async-javascript" target="_blank">Async Javascript</a>"' );
|
882 |
+
$asj_install_url = network_admin_url() . 'plugin-install.php?s=async+javascript&tab=search&type=term';
|
883 |
+
echo sprintf( ' <a href="' . $asj_install_url . '">%s</a>', __( 'Click here to install and activate it.', 'autoptimize' ) );
|
884 |
+
}
|
885 |
+
?>
|
886 |
+
</td>
|
887 |
+
</tr>
|
888 |
+
<tr>
|
889 |
+
<th scope="row"><?php _e( 'Optimize YouTube videos', 'autoptimize' ); ?></th>
|
890 |
+
<td>
|
891 |
+
<?php
|
892 |
+
if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'wp-youtube-lyte/wp-youtube-lyte.php' ) ) {
|
893 |
+
_e( 'Great, you have WP YouTube Lyte installed.', 'autoptimize' );
|
894 |
+
$lyte_config_url = 'options-general.php?page=lyte_settings_page';
|
895 |
+
echo sprintf( ' <a href="' . $lyte_config_url . '">%s</a>', __( 'Click here to configure it.', 'autoptimize' ) );
|
896 |
+
} else {
|
897 |
+
// translators: %s will be replaced by a link to "wp youtube lyte" plugin.
|
898 |
+
echo sprintf( __( '%s allows you to “lazy load” your videos, by inserting responsive “Lite YouTube Embeds". ', 'autoptimize' ), '<a href="https://wordpress.org/plugins/wp-youtube-lyte" target="_blank">WP YouTube Lyte</a>' );
|
899 |
+
$lyte_install_url = network_admin_url() . 'plugin-install.php?s=lyte&tab=search&type=term';
|
900 |
+
echo sprintf( ' <a href="' . $lyte_install_url . '">%s</a>', __( 'Click here to install and activate it.', 'autoptimize' ) );
|
901 |
+
}
|
902 |
+
?>
|
903 |
+
</td>
|
904 |
+
</tr>
|
905 |
+
</table>
|
906 |
+
<p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
|
907 |
+
</form>
|
908 |
+
<script>
|
909 |
+
jQuery(document).ready(function() {
|
910 |
+
jQuery( "#autoptimize_imgopt_checkbox" ).change(function() {
|
911 |
+
if (this.checked) {
|
912 |
+
jQuery("#autoptimize_imgopt_quality").show("slow");
|
913 |
+
} else {
|
914 |
+
jQuery("#autoptimize_imgopt_quality").hide("slow");
|
915 |
+
}
|
916 |
+
});
|
917 |
+
});
|
918 |
+
</script>
|
919 |
+
<?php
|
920 |
+
}
|
921 |
+
}
|
classes/autoptimizeHTML.php
CHANGED
@@ -1,92 +1,123 @@
|
|
1 |
<?php
|
2 |
-
|
|
|
|
|
3 |
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
private $keepcomments = false;
|
6 |
-
|
7 |
-
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
// Remove the HTML comments?
|
10 |
$this->keepcomments = (bool) $options['keepcomments'];
|
11 |
-
|
12 |
-
//
|
13 |
$this->forcexhtml = (bool) apply_filters( 'autoptimize_filter_html_forcexhtml', false );
|
14 |
-
|
15 |
-
//
|
16 |
-
$
|
17 |
-
if ($
|
18 |
-
$
|
19 |
-
$this->exclude = array_merge($
|
20 |
}
|
21 |
-
|
22 |
-
// Nothing else for HTML
|
23 |
return true;
|
24 |
}
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
30 |
return false;
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
}
|
39 |
}
|
|
|
40 |
|
41 |
-
|
42 |
-
|
43 |
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
}
|
56 |
-
}
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
// remove the noptimize-wrapper from around the excluded strings
|
62 |
-
foreach ($this->exclude as $exclString) {
|
63 |
-
$replString="<!--noptimize-->".$exclString."<!--/noptimize-->";
|
64 |
-
if (strpos($this->content,$replString)!==false) {
|
65 |
-
$this->content=str_replace($replString,$exclString,$this->content);
|
66 |
-
}
|
67 |
-
}
|
68 |
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
|
|
73 |
}
|
|
|
74 |
|
75 |
-
|
|
|
|
|
|
|
76 |
}
|
77 |
-
|
78 |
-
|
79 |
-
return false;
|
80 |
}
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
|
|
|
|
|
|
|
|
85 |
return true;
|
86 |
}
|
87 |
-
|
88 |
-
|
89 |
-
|
|
|
|
|
|
|
|
|
|
|
90 |
return $this->content;
|
91 |
}
|
92 |
}
|
1 |
<?php
|
2 |
+
/**
|
3 |
+
* Handles minifying HTML markup.
|
4 |
+
*/
|
5 |
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
+
class autoptimizeHTML extends autoptimizeBase
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Whether HTML comments are kept.
|
14 |
+
*
|
15 |
+
* @var bool
|
16 |
+
*/
|
17 |
private $keepcomments = false;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Things to exclude from being minifed.
|
21 |
+
*
|
22 |
+
* @var array
|
23 |
+
*/
|
24 |
+
private $exclude = array(
|
25 |
+
'<!-- ngg_resource_manager_marker -->',
|
26 |
+
'<!--noindex-->',
|
27 |
+
'<!--/noindex-->',
|
28 |
+
);
|
29 |
+
|
30 |
+
public function read( $options )
|
31 |
+
{
|
32 |
// Remove the HTML comments?
|
33 |
$this->keepcomments = (bool) $options['keepcomments'];
|
34 |
+
|
35 |
+
// Filter to force xhtml.
|
36 |
$this->forcexhtml = (bool) apply_filters( 'autoptimize_filter_html_forcexhtml', false );
|
37 |
+
|
38 |
+
// Filterable strings to be excluded from HTML minification.
|
39 |
+
$exclude = apply_filters( 'autoptimize_filter_html_exclude', '' );
|
40 |
+
if ( '' !== $exclude ) {
|
41 |
+
$exclude_arr = array_filter( array_map( 'trim', explode( ',', $exclude ) ) );
|
42 |
+
$this->exclude = array_merge( $exclude_arr, $this->exclude );
|
43 |
}
|
44 |
+
|
45 |
+
// Nothing else for HTML!
|
46 |
return true;
|
47 |
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Minifies HTML.
|
51 |
+
*
|
52 |
+
* @return bool
|
53 |
+
*/
|
54 |
+
public function minify()
|
55 |
+
{
|
56 |
+
$noptimize = apply_filters( 'autoptimize_filter_html_noptimize', false, $this->content );
|
57 |
+
if ( $noptimize ) {
|
58 |
return false;
|
59 |
+
}
|
60 |
+
|
61 |
+
// Wrap the to-be-excluded strings in noptimize tags.
|
62 |
+
foreach ( $this->exclude as $str ) {
|
63 |
+
if ( false !== strpos( $this->content, $str ) ) {
|
64 |
+
$replacement = '<!--noptimize-->' . $str . '<!--/noptimize-->';
|
65 |
+
$this->content = str_replace( $str, $replacement, $this->content );
|
|
|
66 |
}
|
67 |
+
}
|
68 |
|
69 |
+
// Noptimize.
|
70 |
+
$this->content = $this->hide_noptimize( $this->content );
|
71 |
|
72 |
+
// Preparing options for Minify_HTML.
|
73 |
+
$options = array( 'keepComments' => $this->keepcomments );
|
74 |
+
if ( $this->forcexhtml ) {
|
75 |
+
$options['xhtml'] = true;
|
76 |
+
}
|
77 |
|
78 |
+
$tmp_content = Minify_HTML::minify( $this->content, $options );
|
79 |
+
if ( ! empty( $tmp_content ) ) {
|
80 |
+
$this->content = $tmp_content;
|
81 |
+
unset( $tmp_content );
|
82 |
+
}
|
|
|
|
|
83 |
|
84 |
+
// Restore noptimize.
|
85 |
+
$this->content = $this->restore_noptimize( $this->content );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
+
// Remove the noptimize-wrapper from around the excluded strings.
|
88 |
+
foreach ( $this->exclude as $str ) {
|
89 |
+
$replacement = '<!--noptimize-->' . $str . '<!--/noptimize-->';
|
90 |
+
if ( false !== strpos( $this->content, $replacement ) ) {
|
91 |
+
$this->content = str_replace( $replacement, $str, $this->content );
|
92 |
}
|
93 |
+
}
|
94 |
|
95 |
+
// Revslider data attribs somehow suffer from HTML optimization, this fixes that!
|
96 |
+
if ( class_exists( 'RevSlider' ) && apply_filters( 'autoptimize_filter_html_dataattrib_cleanup', false ) ) {
|
97 |
+
$this->content = preg_replace( '#\n(data-.*$)\n#Um', ' $1 ', $this->content );
|
98 |
+
$this->content = preg_replace( '#<[^>]*(=\"[^"\'<>\s]*\")(\w)#', '$1 $2', $this->content );
|
99 |
}
|
100 |
+
|
101 |
+
return true;
|
|
|
102 |
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Doesn't do much in case of HTML (no cache in css/js sense there)
|
106 |
+
*
|
107 |
+
* @return true
|
108 |
+
*/
|
109 |
+
public function cache()
|
110 |
+
{
|
111 |
return true;
|
112 |
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Returns the HTML markup.
|
116 |
+
*
|
117 |
+
* @return string
|
118 |
+
*/
|
119 |
+
public function getcontent()
|
120 |
+
{
|
121 |
return $this->content;
|
122 |
}
|
123 |
}
|
classes/autoptimizeMain.php
ADDED
@@ -0,0 +1,528 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Wraps base plugin logic/hooks and handles activation/deactivation/uninstall.
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
+
class autoptimizeMain
|
11 |
+
{
|
12 |
+
const INIT_EARLIER_PRIORITY = -1;
|
13 |
+
const DEFAULT_HOOK_PRIORITY = 2;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Version string.
|
17 |
+
*
|
18 |
+
* @var string
|
19 |
+
*/
|
20 |
+
protected $version = null;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Main plugin filepath.
|
24 |
+
* Used for activation/deactivation/uninstall hooks.
|
25 |
+
*
|
26 |
+
* @var string
|
27 |
+
*/
|
28 |
+
protected $filepath = null;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Constructor.
|
32 |
+
*
|
33 |
+
* @param string $version Version.
|
34 |
+
* @param string $filepath Filepath. Needed for activation/deactivation/uninstall hooks.
|
35 |
+
*/
|
36 |
+
public function __construct( $version, $filepath )
|
37 |
+
{
|
38 |
+
$this->version = $version;
|
39 |
+
$this->filepath = $filepath;
|
40 |
+
}
|
41 |
+
|
42 |
+
public function run()
|
43 |
+
{
|
44 |
+
$this->add_hooks();
|
45 |
+
|
46 |
+
// Runs cache size checker.
|
47 |
+
$checker = new autoptimizeCacheChecker();
|
48 |
+
$checker->run();
|
49 |
+
}
|
50 |
+
|
51 |
+
protected function add_hooks()
|
52 |
+
{
|
53 |
+
add_action( 'plugins_loaded', array( $this, 'setup' ) );
|
54 |
+
|
55 |
+
add_action( 'autoptimize_setup_done', array( $this, 'version_upgrades_check' ) );
|
56 |
+
add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) );
|
57 |
+
add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_extra' ) );
|
58 |
+
add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_partners_tab' ) );
|
59 |
+
|
60 |
+
add_action( 'init', array( $this, 'load_textdomain' ) );
|
61 |
+
add_action( 'plugins_loaded', array( $this, 'hook_page_cache_purge' ) );
|
62 |
+
add_action( 'admin_init', array( 'PAnD', 'init' ) );
|
63 |
+
|
64 |
+
register_activation_hook( $this->filepath, array( $this, 'on_activate' ) );
|
65 |
+
}
|
66 |
+
|
67 |
+
public function on_activate()
|
68 |
+
{
|
69 |
+
register_uninstall_hook( $this->filepath, 'autoptimizeMain::on_uninstall' );
|
70 |
+
}
|
71 |
+
|
72 |
+
public function load_textdomain()
|
73 |
+
{
|
74 |
+
load_plugin_textdomain( 'autoptimize' );
|
75 |
+
}
|
76 |
+
|
77 |
+
public function setup()
|
78 |
+
{
|
79 |
+
// Do we gzip in php when caching or is the webserver doing it?
|
80 |
+
define( 'AUTOPTIMIZE_CACHE_NOGZIP', (bool) get_option( 'autoptimize_cache_nogzip' ) );
|
81 |
+
|
82 |
+
// These can be overridden by specifying them in wp-config.php or such.
|
83 |
+
if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_NAME' ) ) {
|
84 |
+
define( 'AUTOPTIMIZE_WP_CONTENT_NAME', '/' . wp_basename( WP_CONTENT_DIR ) );
|
85 |
+
}
|
86 |
+
if ( ! defined( 'AUTOPTIMIZE_CACHE_CHILD_DIR' ) ) {
|
87 |
+
define( 'AUTOPTIMIZE_CACHE_CHILD_DIR', '/cache/autoptimize/' );
|
88 |
+
}
|
89 |
+
if ( ! defined( 'AUTOPTIMIZE_CACHEFILE_PREFIX' ) ) {
|
90 |
+
define( 'AUTOPTIMIZE_CACHEFILE_PREFIX', 'autoptimize_' );
|
91 |
+
}
|
92 |
+
// Note: trailing slash is not optional!
|
93 |
+
if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
|
94 |
+
define( 'AUTOPTIMIZE_CACHE_DIR', autoptimizeCache::get_pathname() );
|
95 |
+
}
|
96 |
+
|
97 |
+
define( 'WP_ROOT_DIR', substr( WP_CONTENT_DIR, 0, strlen( WP_CONTENT_DIR ) - strlen( AUTOPTIMIZE_WP_CONTENT_NAME ) ) );
|
98 |
+
|
99 |
+
if ( ! defined( 'AUTOPTIMIZE_WP_SITE_URL' ) ) {
|
100 |
+
if ( function_exists( 'domain_mapping_siteurl' ) ) {
|
101 |
+
define( 'AUTOPTIMIZE_WP_SITE_URL', domain_mapping_siteurl( get_current_blog_id() ) );
|
102 |
+
} else {
|
103 |
+
define( 'AUTOPTIMIZE_WP_SITE_URL', site_url() );
|
104 |
+
}
|
105 |
+
}
|
106 |
+
if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_URL' ) ) {
|
107 |
+
if ( function_exists( 'domain_mapping_siteurl' ) ) {
|
108 |
+
define( 'AUTOPTIMIZE_WP_CONTENT_URL', str_replace( get_original_url( AUTOPTIMIZE_WP_SITE_URL ), AUTOPTIMIZE_WP_SITE_URL, content_url() ) );
|
109 |
+
} else {
|
110 |
+
define( 'AUTOPTIMIZE_WP_CONTENT_URL', content_url() );
|
111 |
+
}
|
112 |
+
}
|
113 |
+
if ( ! defined( 'AUTOPTIMIZE_CACHE_URL' ) ) {
|
114 |
+
if ( is_multisite() && apply_filters( 'autoptimize_separate_blog_caches', true ) ) {
|
115 |
+
$blog_id = get_current_blog_id();
|
116 |
+
define( 'AUTOPTIMIZE_CACHE_URL', AUTOPTIMIZE_WP_CONTENT_URL . AUTOPTIMIZE_CACHE_CHILD_DIR . $blog_id . '/' );
|
117 |
+
} else {
|
118 |
+
define( 'AUTOPTIMIZE_CACHE_URL', AUTOPTIMIZE_WP_CONTENT_URL . AUTOPTIMIZE_CACHE_CHILD_DIR );
|
119 |
+
}
|
120 |
+
}
|
121 |
+
if ( ! defined( 'AUTOPTIMIZE_WP_ROOT_URL' ) ) {
|
122 |
+
define( 'AUTOPTIMIZE_WP_ROOT_URL', str_replace( AUTOPTIMIZE_WP_CONTENT_NAME, '', AUTOPTIMIZE_WP_CONTENT_URL ) );
|
123 |
+
}
|
124 |
+
if ( ! defined( 'AUTOPTIMIZE_HASH' ) ) {
|
125 |
+
define( 'AUTOPTIMIZE_HASH', wp_hash( AUTOPTIMIZE_CACHE_URL ) );
|
126 |
+
}
|
127 |
+
if ( ! defined( 'AUTOPTIMIZE_SITE_DOMAIN' ) ) {
|
128 |
+
define( 'AUTOPTIMIZE_SITE_DOMAIN', parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) );
|
129 |
+
}
|
130 |
+
do_action( 'autoptimize_setup_done' );
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Checks if there's a need to upgrade/update options and whatnot,
|
135 |
+
* in which case we might need to do stuff and flush the cache
|
136 |
+
* to avoid old versions of aggregated files lingering around.
|
137 |
+
*/
|
138 |
+
public function version_upgrades_check()
|
139 |
+
{
|
140 |
+
autoptimizeVersionUpdatesHandler::check_installed_and_update( $this->version );
|
141 |
+
}
|
142 |
+
|
143 |
+
public function check_cache_and_run()
|
144 |
+
{
|
145 |
+
if ( autoptimizeCache::cacheavail() ) {
|
146 |
+
$conf = autoptimizeConfig::instance();
|
147 |
+
if ( $conf->get( 'autoptimize_html' ) || $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) ) {
|
148 |
+
// Hook into WordPress frontend.
|
149 |
+
if ( defined( 'AUTOPTIMIZE_INIT_EARLIER' ) ) {
|
150 |
+
add_action(
|
151 |
+
'init',
|
152 |
+
array( $this, 'start_buffering' ),
|
153 |
+
self::INIT_EARLIER_PRIORITY
|
154 |
+
);
|
155 |
+
} else {
|
156 |
+
if ( ! defined( 'AUTOPTIMIZE_HOOK_INTO' ) ) {
|
157 |
+
define( 'AUTOPTIMIZE_HOOK_INTO', 'template_redirect' );
|
158 |
+
}
|
159 |
+
add_action(
|
160 |
+
constant( 'AUTOPTIMIZE_HOOK_INTO' ),
|
161 |
+
array( $this, 'start_buffering' ),
|
162 |
+
self::DEFAULT_HOOK_PRIORITY
|
163 |
+
);
|
164 |
+
}
|
165 |
+
}
|
166 |
+
} else {
|
167 |
+
add_action( 'admin_notices', 'autoptimizeMain::notice_cache_unavailable' );
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
public function maybe_run_ao_extra()
|
172 |
+
{
|
173 |
+
if ( apply_filters( 'autoptimize_filter_extra_activate', true ) ) {
|
174 |
+
$ao_extra = new autoptimizeExtra();
|
175 |
+
$ao_extra->run();
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
public function maybe_run_partners_tab()
|
180 |
+
{
|
181 |
+
// Loads partners tab code if in admin (and not in admin-ajax.php)!
|
182 |
+
if ( autoptimizeConfig::is_admin_and_not_ajax() ) {
|
183 |
+
new autoptimizePartners();
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
public function hook_page_cache_purge()
|
188 |
+
{
|
189 |
+
// hook into a collection of page cache purge actions if filter allows.
|
190 |
+
if ( apply_filters( 'autoptimize_filter_main_hookpagecachepurge', true ) ) {
|
191 |
+
$page_cache_purge_actions = array(
|
192 |
+
'after_rocket_clean_domain', // exists.
|
193 |
+
'hyper_cache_purged', // Stefano confirmed this will be added.
|
194 |
+
'w3tc_flush_posts', // exits.
|
195 |
+
'w3tc_flush_all', // exists.
|
196 |
+
'ce_action_cache_cleared', // Sven confirmed this will be added.
|
197 |
+
'comet_cache_wipe_cache', // still to be confirmed by Raam.
|
198 |
+
'wp_cache_cleared', // cfr. https://github.com/Automattic/wp-super-cache/pull/537.
|
199 |
+
'wpfc_delete_cache', // Emre confirmed this will be added this.
|
200 |
+
'swift_performance_after_clear_all_cache', // swift perf. yeah!
|
201 |
+
);
|
202 |
+
$page_cache_purge_actions = apply_filters( 'autoptimize_filter_main_pagecachepurgeactions', $page_cache_purge_actions );
|
203 |
+
foreach ( $page_cache_purge_actions as $purge_action ) {
|
204 |
+
add_action( $purge_action, 'autoptimizeCache::clearall_actionless' );
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Setup output buffering if needed.
|
211 |
+
*
|
212 |
+
* @return void
|
213 |
+
*/
|
214 |
+
public function start_buffering()
|
215 |
+
{
|
216 |
+
if ( $this->should_buffer() ) {
|
217 |
+
|
218 |
+
// Load speedupper conditionally (true by default).
|
219 |
+
if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
|
220 |
+
$ao_speedupper = new autoptimizeSpeedupper();
|
221 |
+
}
|
222 |
+
|
223 |
+
$conf = autoptimizeConfig::instance();
|
224 |
+
|
225 |
+
if ( $conf->get( 'autoptimize_js' ) ) {
|
226 |
+
if ( ! defined( 'CONCATENATE_SCRIPTS' ) ) {
|
227 |
+
define( 'CONCATENATE_SCRIPTS', false );
|
228 |
+
}
|
229 |
+
if ( ! defined( 'COMPRESS_SCRIPTS' ) ) {
|
230 |
+
define( 'COMPRESS_SCRIPTS', false );
|
231 |
+
}
|
232 |
+
}
|
233 |
+
|
234 |
+
if ( $conf->get( 'autoptimize_css' ) ) {
|
235 |
+
if ( ! defined( 'COMPRESS_CSS' ) ) {
|
236 |
+
define( 'COMPRESS_CSS', false );
|
237 |
+
}
|
238 |
+
}
|
239 |
+
|
240 |
+
if ( apply_filters( 'autoptimize_filter_obkiller', false ) ) {
|
241 |
+
while ( ob_get_level() > 0 ) {
|
242 |
+
ob_end_clean();
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
246 |
+
// Now, start the real thing!
|
247 |
+
ob_start( array( $this, 'end_buffering' ) );
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* Returns true if all the conditions to start output buffering are satisfied.
|
253 |
+
*
|
254 |
+
* @param bool $doing_tests Allows overriding the optimization of only
|
255 |
+
* deciding once per request (for use in tests).
|
256 |
+
* @return bool
|
257 |
+
*/
|
258 |
+
public function should_buffer( $doing_tests = false )
|
259 |
+
{
|
260 |
+
static $do_buffering = null;
|
261 |
+
|
262 |
+
// Only check once in case we're called multiple times by others but
|
263 |
+
// still allows multiple calls when doing tests.
|
264 |
+
if ( null === $do_buffering || $doing_tests ) {
|
265 |
+
|
266 |
+
$ao_noptimize = false;
|
267 |
+
|
268 |
+
// Checking for DONOTMINIFY constant as used by e.g. WooCommerce POS.
|
269 |
+
if ( defined( 'DONOTMINIFY' ) && ( constant( 'DONOTMINIFY' ) === true || constant( 'DONOTMINIFY' ) === 'true' ) ) {
|
270 |
+
$ao_noptimize = true;
|
271 |
+
}
|
272 |
+
|
273 |
+
// Skip checking query strings if they're disabled.
|
274 |
+
if ( apply_filters( 'autoptimize_filter_honor_qs_noptimize', true ) ) {
|
275 |
+
// Check for `ao_noptimize` (and other) keys in the query string
|
276 |
+
// to get non-optimized page for debugging.
|
277 |
+
$keys = array(
|
278 |
+
'ao_noptimize',
|
279 |
+
'ao_noptirocket',
|
280 |
+
);
|
281 |
+
foreach ( $keys as $key ) {
|
282 |
+
if ( array_key_exists( $key, $_GET ) && '1' === $_GET[ $key ] ) {
|
283 |
+
$ao_noptimize = true;
|
284 |
+
break;
|
285 |
+
}
|
286 |
+
}
|
287 |
+
}
|
288 |
+
|
289 |
+
// If setting says not to optimize logged in user and user is logged in...
|
290 |
+
if ( 'on' !== get_option( 'autoptimize_optimize_logged', 'on' ) && is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
|
291 |
+
$ao_noptimize = true;
|
292 |
+
}
|
293 |
+
|
294 |
+
// If setting says not to optimize cart/checkout.
|
295 |
+
if ( 'on' !== get_option( 'autoptimize_optimize_checkout', 'on' ) ) {
|
296 |
+
// Checking for woocommerce, easy digital downloads and wp ecommerce...
|
297 |
+
foreach ( array( 'is_checkout', 'is_cart', 'edd_is_checkout', 'wpsc_is_cart', 'wpsc_is_checkout' ) as $func ) {
|
298 |
+
if ( function_exists( $func ) && $func() ) {
|
299 |
+
$ao_noptimize = true;
|
300 |
+
break;
|
301 |
+
}
|
302 |
+
}
|
303 |
+
}
|
304 |
+
|
305 |
+
// Allows blocking of autoptimization on your own terms regardless of above decisions.
|
306 |
+
$ao_noptimize = (bool) apply_filters( 'autoptimize_filter_noptimize', $ao_noptimize );
|
307 |
+
|
308 |
+
// Check for site being previewed in the Customizer (available since WP 4.0).
|
309 |
+
$is_customize_preview = false;
|
310 |
+
if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) {
|
311 |
+
$is_customize_preview = is_customize_preview();
|
312 |
+
}
|
313 |
+
|
314 |
+
/**
|
315 |
+
* We only buffer the frontend requests (and then only if not a feed
|
316 |
+
* and not turned off explicitly and not when being previewed in Customizer)!
|
317 |
+
* NOTE: Tests throw a notice here due to is_feed() being called
|
318 |
+
* while the main query hasn't been ran yet. Thats why we use
|
319 |
+
* AUTOPTIMIZE_INIT_EARLIER in tests.
|
320 |
+
*/
|
321 |
+
$do_buffering = ( ! is_admin() && ! is_feed() && ! $ao_noptimize && ! $is_customize_preview );
|
322 |
+
}
|
323 |
+
|
324 |
+
return $do_buffering;
|
325 |
+
}
|
326 |
+
|
327 |
+
/**
|
328 |
+
* Returns true if given markup is considered valid/processable/optimizable.
|
329 |
+
*
|
330 |
+
* @param string $content Markup.
|
331 |
+
*
|
332 |
+
* @return bool
|
333 |
+
*/
|
334 |
+
public function is_valid_buffer( $content )
|
335 |
+
{
|
336 |
+
// Defaults to true.
|
337 |
+
$valid = true;
|
338 |
+
|
339 |
+
$has_no_html_tag = ( false === stripos( $content, '<html' ) );
|
340 |
+
$has_xsl_stylesheet = ( false !== stripos( $content, '<xsl:stylesheet' ) );
|
341 |
+
$has_html5_doctype = ( preg_match( '/^<!DOCTYPE.+html>/i', $content ) > 0 );
|
342 |
+
|
343 |
+
if ( $has_no_html_tag ) {
|
344 |
+
// Can't be valid amp markup without an html tag preceding it.
|
345 |
+
$is_amp_markup = false;
|
346 |
+
} else {
|
347 |
+
$is_amp_markup = self::is_amp_markup( $content );
|
348 |
+
}
|
349 |
+
|
350 |
+
// If it's not html, or if it's amp or contains xsl stylesheets we don't touch it.
|
351 |
+
if ( $has_no_html_tag && ! $has_html5_doctype || $is_amp_markup || $has_xsl_stylesheet ) {
|
352 |
+
$valid = false;
|
353 |
+
}
|
354 |
+
|
355 |
+
return $valid;
|
356 |
+
}
|
357 |
+
|
358 |
+
/**
|
359 |
+
* Returns true if given $content is considered to be AMP markup.
|
360 |
+
* This is far from actual validation against AMP spec, but it'll do for now.
|
361 |
+
*
|
362 |
+
* @param string $content Markup to check.
|
363 |
+
*
|
364 |
+
* @return bool
|
365 |
+
*/
|
366 |
+
public static function is_amp_markup( $content )
|
367 |
+
{
|
368 |
+
$is_amp_markup = preg_match( '/<html[^>]*(?:amp|⚡)/i', $content );
|
369 |
+
|
370 |
+
return (bool) $is_amp_markup;
|
371 |
+
}
|
372 |
+
|
373 |
+
/**
|
374 |
+
* Processes/optimizes the output-buffered content and returns it.
|
375 |
+
* If the content is not processable, it is returned unmodified.
|
376 |
+
*
|
377 |
+
* @param string $content Buffered content.
|
378 |
+
*
|
379 |
+
* @return string
|
380 |
+
*/
|
381 |
+
public function end_buffering( $content )
|
382 |
+
{
|
383 |
+
// Bail early without modifying anything if we can't handle the content.
|
384 |
+
if ( ! $this->is_valid_buffer( $content ) ) {
|
385 |
+
return $content;
|
386 |
+
}
|
387 |
+
|
388 |
+
$conf = autoptimizeConfig::instance();
|
389 |
+
|
390 |
+
// Determine what needs to be ran.
|
391 |
+
$classes = array();
|
392 |
+
if ( $conf->get( 'autoptimize_js' ) ) {
|
393 |
+
$classes[] = 'autoptimizeScripts';
|
394 |
+
}
|
395 |
+
if ( $conf->get( 'autoptimize_css' ) ) {
|
396 |
+
$classes[] = 'autoptimizeStyles';
|
397 |
+
}
|
398 |
+
if ( $conf->get( 'autoptimize_html' ) ) {
|
399 |
+
$classes[] = 'autoptimizeHTML';
|
400 |
+
}
|
401 |
+
|
402 |
+
$classoptions = array(
|
403 |
+
'autoptimizeScripts' => array(
|
404 |
+
'aggregate' => $conf->get( 'autoptimize_js_aggregate' ),
|
405 |
+
'justhead' => $conf->get( 'autoptimize_js_justhead' ),
|
406 |
+
'forcehead' => $conf->get( 'autoptimize_js_forcehead' ),
|
407 |
+
'trycatch' => $conf->get( 'autoptimize_js_trycatch' ),
|
408 |
+
'js_exclude' => $conf->get( 'autoptimize_js_exclude' ),
|
409 |
+
'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
|
410 |
+
'include_inline' => $conf->get( 'autoptimize_js_include_inline' ),
|
411 |
+
),
|
412 |
+
'autoptimizeStyles' => array(
|
413 |
+
'aggregate' => $conf->get( 'autoptimize_css_aggregate' ),
|
414 |
+
'justhead' => $conf->get( 'autoptimize_css_justhead' ),
|
415 |
+
'datauris' => $conf->get( 'autoptimize_css_datauris' ),
|
416 |
+
'defer' => $conf->get( 'autoptimize_css_defer' ),
|
417 |
+
'defer_inline' => $conf->get( 'autoptimize_css_defer_inline' ),
|
418 |
+
'inline' => $conf->get( 'autoptimize_css_inline' ),
|
419 |
+
'css_exclude' => $conf->get( 'autoptimize_css_exclude' ),
|
420 |
+
'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
|
421 |
+
'include_inline' => $conf->get( 'autoptimize_css_include_inline' ),
|
422 |
+
'nogooglefont' => $conf->get( 'autoptimize_css_nogooglefont' ),
|
423 |
+
),
|
424 |
+
'autoptimizeHTML' => array(
|
425 |
+
'keepcomments' => $conf->get( 'autoptimize_html_keepcomments' ),
|
426 |
+
),
|
427 |
+
);
|
428 |
+
|
429 |
+
$content = apply_filters( 'autoptimize_filter_html_before_minify', $content );
|
430 |
+
|
431 |
+
// Run the classes!
|
432 |
+
foreach ( $classes as $name ) {
|
433 |
+
$instance = new $name( $content );
|
434 |
+
if ( $instance->read( $classoptions[ $name ] ) ) {
|
435 |
+
$instance->minify();
|
436 |
+
$instance->cache();
|
437 |
+
$content = $instance->getcontent();
|
438 |
+
}
|
439 |
+
unset( $instance );
|
440 |
+
}
|
441 |
+
|
442 |
+
$content = apply_filters( 'autoptimize_html_after_minify', $content );
|
443 |
+
|
444 |
+
return $content;
|
445 |
+
}
|
446 |
+
|
447 |
+
public static function on_uninstall()
|
448 |
+
{
|
449 |
+
autoptimizeCache::clearall();
|
450 |
+
|
451 |
+
$delete_options = array(
|
452 |
+
'autoptimize_cache_clean',
|
453 |
+
'autoptimize_cache_nogzip',
|
454 |
+
'autoptimize_css',
|
455 |
+
'autoptimize_css_aggregate',
|
456 |
+
'autoptimize_css_datauris',
|
457 |
+
'autoptimize_css_justhead',
|
458 |
+
'autoptimize_css_defer',
|
459 |
+
'autoptimize_css_defer_inline',
|
460 |
+
'autoptimize_css_inline',
|
461 |
+
'autoptimize_css_exclude',
|
462 |
+
'autoptimize_html',
|
463 |
+
'autoptimize_html_keepcomments',
|
464 |
+
'autoptimize_js',
|
465 |
+
'autoptimize_js_aggregate',
|
466 |
+
'autoptimize_js_exclude',
|
467 |
+
'autoptimize_js_forcehead',
|
468 |
+
'autoptimize_js_justhead',
|
469 |
+
'autoptimize_js_trycatch',
|
470 |
+
'autoptimize_version',
|
471 |
+
'autoptimize_show_adv',
|
472 |
+
'autoptimize_cdn_url',
|
473 |
+
'autoptimize_cachesize_notice',
|
474 |
+
'autoptimize_css_include_inline',
|
475 |
+
'autoptimize_js_include_inline',
|
476 |
+
'autoptimize_optimize_logged',
|
477 |
+
'autoptimize_optimize_checkout',
|
478 |
+
'autoptimize_extra_settings',
|
479 |
+
'autoptimize_service_availablity',
|
480 |
+
'autoptimize_imgopt_provider_stat',
|
481 |
+
'autoptimize_imgopt_launched',
|
482 |
+
);
|
483 |
+
|
484 |
+
if ( ! is_multisite() ) {
|
485 |
+
foreach ( $delete_options as $del_opt ) {
|
486 |
+
delete_option( $del_opt );
|
487 |
+
}
|
488 |
+
} else {
|
489 |
+
global $wpdb;
|
490 |
+
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
491 |
+
$original_blog_id = get_current_blog_id();
|
492 |
+
foreach ( $blog_ids as $blog_id ) {
|
493 |
+
switch_to_blog( $blog_id );
|
494 |
+
foreach ( $delete_options as $del_opt ) {
|
495 |
+
delete_option( $del_opt );
|
496 |
+
}
|
497 |
+
}
|
498 |
+
switch_to_blog( $original_blog_id );
|
499 |
+
}
|
500 |
+
|
501 |
+
if ( wp_get_schedule( 'ao_cachechecker' ) ) {
|
502 |
+
wp_clear_scheduled_hook( 'ao_cachechecker' );
|
503 |
+
}
|
504 |
+
}
|
505 |
+
|
506 |
+
public static function notice_cache_unavailable()
|
507 |
+
{
|
508 |
+
echo '<div class="error"><p>';
|
509 |
+
// Translators: %s is the cache directory location.
|
510 |
+
printf( __( 'Autoptimize cannot write to the cache directory (%s), please fix to enable CSS/ JS optimization!', 'autoptimize' ), AUTOPTIMIZE_CACHE_DIR );
|
511 |
+
echo '</p></div>';
|
512 |
+
}
|
513 |
+
|
514 |
+
public static function notice_installed()
|
515 |
+
{
|
516 |
+
echo '<div class="updated"><p>';
|
517 |
+
_e( 'Thank you for installing and activating Autoptimize. Please configure it under "Settings" -> "Autoptimize" to start improving your site\'s performance.', 'autoptimize' );
|
518 |
+
echo '</p></div>';
|
519 |
+
}
|
520 |
+
|
521 |
+
public static function notice_updated()
|
522 |
+
{
|
523 |
+
echo '<div class="updated"><p>';
|
524 |
+
_e( 'Autoptimize has just been updated. Please <strong>test your site now</strong> and adapt Autoptimize config if needed.', 'autoptimize' );
|
525 |
+
echo '</p></div>';
|
526 |
+
}
|
527 |
+
|
528 |
+
}
|
classes/autoptimizePartners.php
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Handles adding "more tools" tab in AO admin settings page which promotes (future) AO
|
4 |
+
* addons and/or affiliate services.
|
5 |
+
*/
|
6 |
+
|
7 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
8 |
+
exit;
|
9 |
+
}
|
10 |
+
|
11 |
+
class autoptimizePartners
|
12 |
+
{
|
13 |
+
public function __construct()
|
14 |
+
{
|
15 |
+
$this->run();
|
16 |
+
}
|
17 |
+
|
18 |
+
public function run()
|
19 |
+
{
|
20 |
+
if ( $this->enabled() ) {
|
21 |
+
add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_partner_tabs' ), 10, 1 );
|
22 |
+
}
|
23 |
+
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
|
24 |
+
}
|
25 |
+
|
26 |
+
protected function enabled()
|
27 |
+
{
|
28 |
+
return apply_filters( 'autoptimize_filter_show_partner_tabs', true );
|
29 |
+
}
|
30 |
+
|
31 |
+
public function add_partner_tabs( $in )
|
32 |
+
{
|
33 |
+
$in = array_merge( $in, array(
|
34 |
+
'ao_partners' => __( 'Optimize More!', 'autoptimize' ),
|
35 |
+
) );
|
36 |
+
|
37 |
+
return $in;
|
38 |
+
}
|
39 |
+
|
40 |
+
public function add_admin_menu()
|
41 |
+
{
|
42 |
+
if ( $this->enabled() ) {
|
43 |
+
add_submenu_page( null, 'AO partner', 'AO partner', 'manage_options', 'ao_partners', array( $this, 'ao_partners_page' ) );
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
protected function get_ao_partner_feed_markup()
|
48 |
+
{
|
49 |
+
$no_feed_text = __( 'Have a look at <a href="http://optimizingmatters.com/">optimizingmatters.com</a> for Autoptimize power-ups!', 'autoptimize' );
|
50 |
+
$output = '';
|
51 |
+
if ( apply_filters( 'autoptimize_settingsscreen_remotehttp', true ) ) {
|
52 |
+
$rss = fetch_feed( 'http://feeds.feedburner.com/OptimizingMattersDownloads' );
|
53 |
+
$maxitems = 0;
|
54 |
+
|
55 |
+
if ( ! is_wp_error( $rss ) ) {
|
56 |
+
$maxitems = $rss->get_item_quantity( 20 );
|
57 |
+
$rss_items = $rss->get_items( 0, $maxitems );
|
58 |
+
}
|
59 |
+
|
60 |
+
if ( 0 == $maxitems ) {
|
61 |
+
$output .= $no_feed_text;
|
62 |
+
} else {
|
63 |
+
$output .= '<ul>';
|
64 |
+
foreach ( $rss_items as $item ) {
|
65 |
+
$item_url = esc_url( $item->get_permalink() );
|
66 |
+
$enclosure = $item->get_enclosure();
|
67 |
+
|
68 |
+
$output .= '<li class="itemDetail">';
|
69 |
+
$output .= '<h3 class="itemTitle"><a href="' . $item_url . '" target="_blank">' . esc_html( $item->get_title() ) . '</a></h3>';
|
70 |
+
|
71 |
+
if ( $enclosure && ( false !== strpos( $enclosure->get_type(), 'image' ) ) ) {
|
72 |
+
$img_url = esc_url( $enclosure->get_link() );
|
73 |
+
$output .= '<div class="itemImage"><a href="' . $item_url . '" target="_blank"><img src="' . $img_url . '"></a></div>';
|
74 |
+
}
|
75 |
+
|
76 |
+
$output .= '<div class="itemDescription">' . wp_kses_post( $item->get_description() ) . '</div>';
|
77 |
+
$output .= '<div class="itemButtonRow"><div class="itemButton button-secondary"><a href="' . $item_url . '" target="_blank">' . __( 'More info', 'autoptimize' ) . '</a></div></div>';
|
78 |
+
$output .= '</li>';
|
79 |
+
}
|
80 |
+
$output .= '</ul>';
|
81 |
+
}
|
82 |
+
} else {
|
83 |
+
$output .= $no_feed_text;
|
84 |
+
}
|
85 |
+
|
86 |
+
return $output;
|
87 |
+
}
|
88 |
+
|
89 |
+
public function ao_partners_page()
|
90 |
+
{
|
91 |
+
?>
|
92 |
+
<style>
|
93 |
+
.itemDetail {
|
94 |
+
background: #fff;
|
95 |
+
width: 250px;
|
96 |
+
min-height: 290px;
|
97 |
+
border: 1px solid #ccc;
|
98 |
+
float: left;
|
99 |
+
padding: 15px;
|
100 |
+
position: relative;
|
101 |
+
margin: 0 10px 10px 0;
|
102 |
+
}
|
103 |
+
.itemTitle {
|
104 |
+
margin-top:0px;
|
105 |
+
margin-bottom:10px;
|
106 |
+
}
|
107 |
+
.itemImage {
|
108 |
+
text-align: center;
|
109 |
+
}
|
110 |
+
.itemImage img {
|
111 |
+
max-width: 95%;
|
112 |
+
max-height: 150px;
|
113 |
+
}
|
114 |
+
.itemDescription {
|
115 |
+
margin-bottom:30px;
|
116 |
+
}
|
117 |
+
.itemButtonRow {
|
118 |
+
position: absolute;
|
119 |
+
bottom: 10px;
|
120 |
+
right: 10px;
|
121 |
+
width:100%;
|
122 |
+
}
|
123 |
+
.itemButton {
|
124 |
+
float:right;
|
125 |
+
}
|
126 |
+
.itemButton a {
|
127 |
+
text-decoration: none;
|
128 |
+
color: #555;
|
129 |
+
}
|
130 |
+
.itemButton a:hover {
|
131 |
+
text-decoration: none;
|
132 |
+
color: #23282d;
|
133 |
+
}
|
134 |
+
</style>
|
135 |
+
<div class="wrap">
|
136 |
+
<h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
|
137 |
+
<?php echo autoptimizeConfig::ao_admin_tabs(); ?>
|
138 |
+
<?php echo '<h2>' . __( "These Autoptimize power-ups and related services will improve your site's performance even more!", 'autoptimize' ) . '</h2>'; ?>
|
139 |
+
<div>
|
140 |
+
<?php echo $this->get_ao_partner_feed_markup(); ?>
|
141 |
+
</div>
|
142 |
+
</div>
|
143 |
+
<?php
|
144 |
+
}
|
145 |
+
}
|
classes/autoptimizeScripts.php
CHANGED
@@ -1,94 +1,130 @@
|
|
1 |
<?php
|
2 |
-
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
3 |
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
5 |
private $scripts = array();
|
6 |
-
private $
|
7 |
-
|
8 |
-
|
9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
private $alreadyminified = false;
|
11 |
-
private $forcehead
|
12 |
-
private $include_inline
|
13 |
-
private $jscode
|
14 |
-
private $url
|
15 |
-
private $
|
16 |
-
private $
|
17 |
-
private $
|
18 |
-
private $
|
19 |
-
private $jsremovables = array();
|
20 |
private $inject_min_late = '';
|
21 |
-
|
22 |
-
//Reads the page and collects script tags
|
23 |
-
public function read($options)
|
|
|
24 |
$noptimizeJS = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content );
|
25 |
-
if ($noptimizeJS)
|
|
|
|
|
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(
|
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(
|
37 |
}
|
38 |
|
39 |
// only header?
|
40 |
-
if( apply_filters('autoptimize_filter_js_justhead', $options['justhead'])
|
41 |
-
$content
|
42 |
-
$this->content
|
43 |
$this->restofcontent = $content[1];
|
44 |
}
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
// include inline?
|
47 |
-
if( apply_filters('autoptimize_js_include_inline', $options['include_inline'])
|
48 |
$this->include_inline = true;
|
49 |
}
|
50 |
|
51 |
// filter to "late inject minified JS", default to true for now (it is faster)
|
52 |
-
$this->inject_min_late = apply_filters('autoptimize_filter_js_inject_min_late',true);
|
53 |
|
54 |
// filters to override hardcoded do(nt)move(last) array contents (array in, array out!)
|
55 |
-
$this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove );
|
56 |
$this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast );
|
57 |
$this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove );
|
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 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
$
|
|
|
68 |
}
|
69 |
}
|
70 |
-
$exclJSArr = array_keys($excludeJS);
|
71 |
} else {
|
72 |
-
$exclJSArr = array_filter(array_map('trim',explode(
|
73 |
}
|
74 |
-
$this->dontmove = array_merge($exclJSArr
|
75 |
}
|
76 |
|
77 |
-
//Should we add try-catch?
|
78 |
-
if($options['trycatch']
|
79 |
$this->trycatch = true;
|
|
|
80 |
|
81 |
-
// force js in head?
|
82 |
-
if($options['forcehead']
|
83 |
$this->forcehead = true;
|
84 |
} else {
|
85 |
$this->forcehead = false;
|
86 |
}
|
|
|
87 |
$this->forcehead = apply_filters( 'autoptimize_filter_js_forcehead', $this->forcehead );
|
88 |
|
89 |
// get cdn url
|
90 |
$this->cdn_url = $options['cdn_url'];
|
91 |
-
|
92 |
// noptimize me
|
93 |
$this->content = $this->hide_noptimize($this->content);
|
94 |
|
@@ -99,59 +135,59 @@ class autoptimizeScripts extends autoptimizeBase {
|
|
99 |
$this->content = $this->hide_comments($this->content);
|
100 |
|
101 |
// Get script files
|
102 |
-
if (preg_match_all('#<script.*</script>#Usmi'
|
103 |
-
foreach($matches[0] as $tag) {
|
104 |
// only consider script aggregation for types whitelisted in should_aggregate-function
|
105 |
-
|
106 |
-
|
|
|
107 |
continue;
|
108 |
}
|
109 |
|
110 |
-
if (preg_match('#<script[^>]*src=("|\')([^>]*)("|\')#Usmi'
|
111 |
// non-inline script
|
112 |
-
if ($this->isremovable($tag
|
113 |
-
$this->content = str_replace($tag,''
|
114 |
continue;
|
115 |
}
|
116 |
-
|
117 |
-
$
|
|
|
118 |
$path = $this->getpath($url);
|
119 |
-
if(
|
120 |
// ok to optimize, add to array
|
121 |
$this->scripts[] = $path;
|
122 |
} else {
|
123 |
$origTag = $tag;
|
124 |
-
$newTag
|
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
|
131 |
-
|
132 |
}
|
133 |
}
|
134 |
}
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
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;
|
@@ -159,7 +195,7 @@ class autoptimizeScripts extends autoptimizeBase {
|
|
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
|
163 |
$origTag = '';
|
164 |
}
|
165 |
// and forget about the $tag (not to be touched any more)
|
@@ -168,186 +204,229 @@ class autoptimizeScripts extends autoptimizeBase {
|
|
168 |
}
|
169 |
} else {
|
170 |
// Inline script
|
171 |
-
if ($this->isremovable($tag
|
172 |
-
$this->content = str_replace($tag,''
|
173 |
continue;
|
174 |
}
|
175 |
-
|
176 |
// unhide comments, as javascript may be wrapped in comment-tags for old times' sake
|
177 |
$tag = $this->restore_comments($tag);
|
178 |
-
if($this->ismergeable($tag) &&
|
179 |
-
preg_match('#<script.*>(.*)</script>#Usmi'
|
180 |
-
$code = preg_replace('#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm','$1'
|
181 |
-
$code = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/',''
|
182 |
-
$this->scripts[] = 'INLINE;'
|
183 |
} else {
|
184 |
// Can we move this?
|
185 |
$autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
|
186 |
-
if( $this->ismovable($tag) ||
|
187 |
-
if( $this->movetolast($tag) ||
|
188 |
$this->move['last'][] = $tag;
|
189 |
} else {
|
190 |
$this->move['first'][] = $tag;
|
191 |
}
|
192 |
} else {
|
193 |
-
//We shouldn't touch this
|
194 |
$tag = '';
|
195 |
}
|
196 |
}
|
197 |
-
//
|
198 |
$tag = $this->hide_comments($tag);
|
199 |
}
|
200 |
-
|
201 |
//Remove the original script tag
|
202 |
-
$this->content = str_replace($tag,''
|
203 |
}
|
204 |
-
|
205 |
return true;
|
206 |
}
|
207 |
-
|
208 |
// No script files, great ;-)
|
209 |
return false;
|
210 |
}
|
211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
//Joins and optimizes JS
|
213 |
-
public function minify()
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
|
|
|
|
218 |
$script = rtrim( $script, ";\n\t\r" ) . ';';
|
219 |
-
//Add try-catch?
|
220 |
-
if($this->trycatch) {
|
221 |
-
$script = 'try{'
|
222 |
}
|
223 |
$tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, '' );
|
224 |
-
if ( has_filter('autoptimize_js_individual_script') && !empty($tmpscript) ) {
|
225 |
-
$script
|
226 |
-
$this->alreadyminified=true;
|
227 |
}
|
228 |
$this->jscode .= "\n" . $script;
|
229 |
} else {
|
230 |
-
//External script
|
231 |
-
if(
|
232 |
-
$scriptsrc = file_get_contents($script);
|
233 |
-
$
|
234 |
-
$scriptsrc =
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
if($this->trycatch) {
|
239 |
-
$scriptsrc = 'try{'.$scriptsrc.'}catch(e){}';
|
240 |
}
|
241 |
$tmpscriptsrc = apply_filters( 'autoptimize_js_individual_script', $scriptsrc, $script );
|
242 |
-
if ( has_filter('autoptimize_js_individual_script') && !empty($tmpscriptsrc) ) {
|
243 |
-
$scriptsrc
|
244 |
-
$this->alreadyminified=true;
|
245 |
-
} else if ($this->can_inject_late($script)) {
|
246 |
-
$scriptsrc=
|
247 |
}
|
248 |
-
$this->jscode .= "\n"
|
249 |
}/*else{
|
250 |
//Couldn't read JS. Maybe getpath isn't working?
|
251 |
}*/
|
252 |
}
|
253 |
}
|
254 |
|
255 |
-
//Check for already-minified code
|
256 |
-
$this->md5hash = md5($this->jscode);
|
257 |
-
$ccheck = new autoptimizeCache($this->md5hash,'js');
|
258 |
-
if($ccheck->check()) {
|
259 |
$this->jscode = $ccheck->retrieve();
|
260 |
return true;
|
261 |
}
|
262 |
-
unset($ccheck);
|
263 |
-
|
264 |
-
|
265 |
-
if ($this->alreadyminified
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
if (!empty($tmp_jscode)) {
|
270 |
$this->jscode = $tmp_jscode;
|
271 |
-
unset($tmp_jscode);
|
272 |
}
|
273 |
-
$this->jscode = $this->inject_minified($this->jscode);
|
274 |
$this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode );
|
275 |
return true;
|
276 |
} else {
|
277 |
-
$this->jscode = $this->inject_minified($this->jscode);
|
278 |
return false;
|
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 |
-
|
289 |
-
//Caches the JS in uncompressed, deflated and gzipped form.
|
290 |
-
public function cache()
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
|
|
295 |
}
|
296 |
-
$this->url = AUTOPTIMIZE_CACHE_URL
|
297 |
$this->url = $this->url_replace_cdn($this->url);
|
298 |
}
|
299 |
-
|
300 |
// Returns the content
|
301 |
-
public function getcontent()
|
|
|
302 |
// Restore the full content
|
303 |
-
if(!empty($this->restofcontent)) {
|
304 |
$this->content .= $this->restofcontent;
|
305 |
$this->restofcontent = '';
|
306 |
}
|
307 |
-
|
308 |
// Add the scripts taking forcehead/ deferred (default) into account
|
309 |
-
if($this->forcehead
|
310 |
-
$replaceTag=array(
|
311 |
-
$defer=
|
312 |
} else {
|
313 |
-
$replaceTag=array(
|
314 |
-
$defer=
|
315 |
}
|
316 |
-
|
317 |
$defer = apply_filters( 'autoptimize_filter_js_defer', $defer );
|
318 |
-
$bodyreplacementpayload = '<script type="text/javascript" '.$defer.'src="'.$this->url.'"></script>';
|
319 |
-
$bodyreplacementpayload = apply_filters('autoptimize_filter_js_bodyreplacementpayload',$bodyreplacementpayload);
|
320 |
|
321 |
-
$
|
|
|
|
|
|
|
322 |
$bodyreplacement .= $bodyreplacementpayload;
|
323 |
-
$bodyreplacement .= implode(''
|
324 |
|
325 |
$replaceTag = apply_filters( 'autoptimize_filter_js_replacetag', $replaceTag );
|
326 |
|
327 |
-
if (strlen($this->jscode)>0) {
|
328 |
-
$this->inject_in_html($bodyreplacement
|
329 |
}
|
330 |
-
|
331 |
-
// restore comments
|
332 |
-
$this->content = $this->restore_comments($this->content);
|
333 |
|
334 |
-
// Restore
|
335 |
-
$this->content = $this->
|
336 |
-
|
337 |
-
// Restore
|
338 |
-
$this->content = $this->
|
339 |
|
340 |
-
//
|
|
|
|
|
|
|
341 |
return $this->content;
|
342 |
}
|
343 |
-
|
344 |
// Checks against the white- and blacklists
|
345 |
-
private function ismergeable($tag)
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
|
|
|
|
|
|
351 |
return true;
|
352 |
}
|
353 |
}
|
@@ -355,93 +434,69 @@ class autoptimizeScripts extends autoptimizeBase {
|
|
355 |
return false;
|
356 |
} else {
|
357 |
foreach($this->domove as $match) {
|
358 |
-
if(strpos($tag
|
359 |
// Matched something
|
360 |
return false;
|
361 |
}
|
362 |
}
|
363 |
-
|
364 |
-
if ($this->movetolast($tag)) {
|
365 |
return false;
|
366 |
}
|
367 |
-
|
368 |
-
foreach($this->dontmove as $match) {
|
369 |
-
if(strpos($tag
|
370 |
-
//Matched something
|
371 |
return false;
|
372 |
}
|
373 |
}
|
374 |
-
|
375 |
// If we're here it's safe to merge
|
376 |
return true;
|
377 |
}
|
378 |
}
|
379 |
-
|
380 |
-
// Checks
|
381 |
-
private function ismovable($tag)
|
382 |
-
|
|
|
383 |
return false;
|
384 |
}
|
385 |
-
|
386 |
-
foreach($this->domove as $match) {
|
387 |
-
if(strpos($tag
|
388 |
// Matched something
|
389 |
return true;
|
390 |
}
|
391 |
}
|
392 |
-
|
393 |
-
if ($this->movetolast($tag)) {
|
394 |
return true;
|
395 |
}
|
396 |
-
|
397 |
-
foreach($this->dontmove as $match) {
|
398 |
-
if(strpos($tag
|
399 |
// Matched something
|
400 |
return false;
|
401 |
}
|
402 |
}
|
403 |
-
|
404 |
// If we're here it's safe to move
|
405 |
return true;
|
406 |
}
|
407 |
-
|
408 |
-
private function movetolast($tag)
|
409 |
-
|
410 |
-
|
|
|
411 |
// Matched, return true
|
412 |
return true;
|
413 |
}
|
414 |
}
|
415 |
-
|
416 |
// Should be in 'first'
|
417 |
return false;
|
418 |
}
|
419 |
-
|
420 |
-
/**
|
421 |
-
* Determines wheter a <script> $tag should be aggregated or not.
|
422 |
-
*
|
423 |
-
* We consider these as "aggregation-safe" currently:
|
424 |
-
* - script tags without a `type` attribute
|
425 |
-
* - script tags with an explicit `type` of `text/javascript`, 'text/ecmascript',
|
426 |
-
* 'application/javascript' or 'application/ecmascript'
|
427 |
-
*
|
428 |
-
* Everything else should return false.
|
429 |
-
*
|
430 |
-
* @param string $tag
|
431 |
-
* @return bool
|
432 |
-
*
|
433 |
-
* original function by https://github.com/zytzagoo/ on his AO fork, thanks Tomas!
|
434 |
-
*/
|
435 |
-
public function should_aggregate($tag) {
|
436 |
-
preg_match('#<(script[^>]*)>#i',$tag,$scripttag);
|
437 |
-
if ( strpos($scripttag[1], 'type')===false ) {
|
438 |
-
return true;
|
439 |
-
} else if ( preg_match('/type\s*=\s*["\']?(?:text|application)\/(?:javascript|ecmascript)["\']?/i', $scripttag[1]) ) {
|
440 |
-
return true;
|
441 |
-
} else {
|
442 |
-
return false;
|
443 |
-
}
|
444 |
-
}
|
445 |
|
446 |
/**
|
447 |
* Determines wheter a <script> $tag can be excluded from minification (as already minified) based on:
|
@@ -449,21 +504,63 @@ class autoptimizeScripts extends autoptimizeBase {
|
|
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 |
-
|
457 |
-
|
458 |
-
if ( $this->inject_min_late
|
459 |
// late-inject turned off
|
460 |
return false;
|
461 |
-
} else if ( (strpos($jsPath,
|
462 |
-
|
463 |
-
|
464 |
} else {
|
465 |
// phew, all is safe, we can late-inject
|
466 |
return true;
|
467 |
}
|
468 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
469 |
}
|
1 |
<?php
|
|
|
2 |
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit;
|
5 |
+
}
|
6 |
+
|
7 |
+
class autoptimizeScripts extends autoptimizeBase
|
8 |
+
{
|
9 |
private $scripts = array();
|
10 |
+
private $move = array(
|
11 |
+
'first' => array(),
|
12 |
+
'last' => array()
|
13 |
+
);
|
14 |
+
|
15 |
+
private $dontmove = array(
|
16 |
+
'document.write','html5.js','show_ads.js','google_ad','histats.com/js','statcounter.com/counter/counter.js',
|
17 |
+
'ws.amazon.com/widgets','media.fastclick.net','/ads/','comment-form-quicktags/quicktags.php','edToolbar',
|
18 |
+
'intensedebate.com','scripts.chitika.net/','_gaq.push','jotform.com/','admin-bar.min.js','GoogleAnalyticsObject',
|
19 |
+
'plupload.full.min.js','syntaxhighlighter','adsbygoogle','gist.github.com','_stq','nonce','post_id','data-noptimize'
|
20 |
+
,'logHuman'
|
21 |
+
);
|
22 |
+
private $domove = array(
|
23 |
+
'gaJsHost','load_cmc','jd.gallery.transitions.js','swfobject.embedSWF(','tiny_mce.js','tinyMCEPreInit.go'
|
24 |
+
);
|
25 |
+
private $domovelast = array(
|
26 |
+
'addthis.com','/afsonline/show_afs_search.js','disqus.js','networkedblogs.com/getnetworkwidget','infolinks.com/js/',
|
27 |
+
'jd.gallery.js.php','jd.gallery.transitions.js','swfobject.embedSWF(','linkwithin.com/widget.js','tiny_mce.js','tinyMCEPreInit.go'
|
28 |
+
);
|
29 |
+
|
30 |
+
private $aggregate = true;
|
31 |
+
private $trycatch = false;
|
32 |
private $alreadyminified = false;
|
33 |
+
private $forcehead = true;
|
34 |
+
private $include_inline = false;
|
35 |
+
private $jscode = '';
|
36 |
+
private $url = '';
|
37 |
+
private $restofcontent = '';
|
38 |
+
private $md5hash = '';
|
39 |
+
private $whitelist = '';
|
40 |
+
private $jsremovables = array();
|
|
|
41 |
private $inject_min_late = '';
|
42 |
+
|
43 |
+
// Reads the page and collects script tags
|
44 |
+
public function read($options)
|
45 |
+
{
|
46 |
$noptimizeJS = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content );
|
47 |
+
if ( $noptimizeJS ) {
|
48 |
+
return false;
|
49 |
+
}
|
50 |
|
51 |
// only optimize known good JS?
|
52 |
$whitelistJS = apply_filters( 'autoptimize_filter_js_whitelist', '', $this->content );
|
53 |
+
if ( ! empty( $whitelistJS ) ) {
|
54 |
+
$this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelistJS ) ) );
|
55 |
}
|
56 |
|
57 |
// is there JS we should simply remove
|
58 |
$removableJS = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
|
59 |
if (!empty($removableJS)) {
|
60 |
+
$this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removableJS ) ) );
|
61 |
}
|
62 |
|
63 |
// only header?
|
64 |
+
if ( apply_filters( 'autoptimize_filter_js_justhead', $options['justhead'] ) ) {
|
65 |
+
$content = explode( '</head>', $this->content, 2 );
|
66 |
+
$this->content = $content[0] . '</head>';
|
67 |
$this->restofcontent = $content[1];
|
68 |
}
|
69 |
+
|
70 |
+
// Determine whether we're doing JS-files aggregation or not.
|
71 |
+
if ( ! $options['aggregate'] ) {
|
72 |
+
$this->aggregate = false;
|
73 |
+
}
|
74 |
+
// Returning true for "dontaggregate" turns off aggregation.
|
75 |
+
if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) {
|
76 |
+
$this->aggregate = false;
|
77 |
+
}
|
78 |
+
|
79 |
// include inline?
|
80 |
+
if ( apply_filters( 'autoptimize_js_include_inline', $options['include_inline'] ) ) {
|
81 |
$this->include_inline = true;
|
82 |
}
|
83 |
|
84 |
// filter to "late inject minified JS", default to true for now (it is faster)
|
85 |
+
$this->inject_min_late = apply_filters( 'autoptimize_filter_js_inject_min_late', true );
|
86 |
|
87 |
// filters to override hardcoded do(nt)move(last) array contents (array in, array out!)
|
88 |
+
$this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove );
|
89 |
$this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast );
|
90 |
$this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove );
|
91 |
|
92 |
// get extra exclusions settings or filter
|
93 |
$excludeJS = $options['js_exclude'];
|
94 |
$excludeJS = apply_filters( 'autoptimize_filter_js_exclude', $excludeJS, $this->content );
|
95 |
+
|
96 |
+
if ( '' !== $excludeJS ) {
|
97 |
+
if ( is_array( $excludeJS ) ) {
|
98 |
+
if ( ( $removeKeys = array_keys( $excludeJS, 'remove' ) ) !== false ) {
|
99 |
+
foreach ( $removeKeys as $removeKey ) {
|
100 |
+
unset( $excludeJS[$removeKey] );
|
101 |
+
$this->jsremovables[] = $removeKey;
|
102 |
}
|
103 |
}
|
104 |
+
$exclJSArr = array_keys( $excludeJS );
|
105 |
} else {
|
106 |
+
$exclJSArr = array_filter( array_map( 'trim', explode( ',', $excludeJS ) ) );
|
107 |
}
|
108 |
+
$this->dontmove = array_merge( $exclJSArr, $this->dontmove );
|
109 |
}
|
110 |
|
111 |
+
// Should we add try-catch?
|
112 |
+
if ( $options['trycatch'] ) {
|
113 |
$this->trycatch = true;
|
114 |
+
}
|
115 |
|
116 |
+
// force js in head?
|
117 |
+
if ( $options['forcehead'] ) {
|
118 |
$this->forcehead = true;
|
119 |
} else {
|
120 |
$this->forcehead = false;
|
121 |
}
|
122 |
+
|
123 |
$this->forcehead = apply_filters( 'autoptimize_filter_js_forcehead', $this->forcehead );
|
124 |
|
125 |
// get cdn url
|
126 |
$this->cdn_url = $options['cdn_url'];
|
127 |
+
|
128 |
// noptimize me
|
129 |
$this->content = $this->hide_noptimize($this->content);
|
130 |
|
135 |
$this->content = $this->hide_comments($this->content);
|
136 |
|
137 |
// Get script files
|
138 |
+
if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) {
|
139 |
+
foreach( $matches[0] as $tag ) {
|
140 |
// only consider script aggregation for types whitelisted in should_aggregate-function
|
141 |
+
$should_aggregate = $this->should_aggregate($tag);
|
142 |
+
if ( ! $should_aggregate ) {
|
143 |
+
$tag = '';
|
144 |
continue;
|
145 |
}
|
146 |
|
147 |
+
if ( preg_match( '#<script[^>]*src=("|\')([^>]*)("|\')#Usmi', $tag, $source ) ) {
|
148 |
// non-inline script
|
149 |
+
if ( $this->isremovable($tag, $this->jsremovables) ) {
|
150 |
+
$this->content = str_replace( $tag, '', $this->content );
|
151 |
continue;
|
152 |
}
|
153 |
+
|
154 |
+
$origTag = null;
|
155 |
+
$url = current( explode( '?', $source[2], 2 ) );
|
156 |
$path = $this->getpath($url);
|
157 |
+
if ( false !== $path && preg_match( '#\.js$#', $path ) && $this->ismergeable($tag) ) {
|
158 |
// ok to optimize, add to array
|
159 |
$this->scripts[] = $path;
|
160 |
} else {
|
161 |
$origTag = $tag;
|
162 |
+
$newTag = $tag;
|
163 |
+
|
164 |
// non-mergeable script (excluded or dynamic or external)
|
165 |
+
if ( is_array( $excludeJS ) ) {
|
166 |
// should we add flags?
|
167 |
+
foreach ( $excludeJS as $exclTag => $exclFlags) {
|
168 |
+
if ( false !== strpos( $origTag, $exclTag ) && in_array( $exclFlags, array( 'async', 'defer' ) ) ) {
|
169 |
+
$newTag = str_replace( '<script ', '<script ' . $exclFlags . ' ', $newTag );
|
170 |
}
|
171 |
}
|
172 |
}
|
173 |
+
|
174 |
+
// Should we minify the non-aggregated script?
|
175 |
+
if ( $path && apply_filters( 'autoptimize_filter_js_minify_excluded', true, $url ) ) {
|
176 |
+
$minified_url = $this->minify_single( $path );
|
177 |
+
// replace orig URL with minified URL from cache if so
|
178 |
+
if ( ! empty( $minified_url ) ) {
|
179 |
+
$newTag = str_replace( $url, $minified_url, $newTag );
|
180 |
+
}
|
181 |
+
|
182 |
+
// remove querystring from URL in newTag
|
183 |
+
if ( ! empty( $explUrl[1] ) ) {
|
184 |
+
$newTag = str_replace( '?' . $explUrl[1], '', $newTag );
|
185 |
+
}
|
186 |
+
}
|
187 |
+
|
188 |
+
if ( $this->ismovable($newTag) ) {
|
|
|
|
|
189 |
// can be moved, flags and all
|
190 |
+
if ( $this->movetolast($newTag) ) {
|
191 |
$this->move['last'][] = $newTag;
|
192 |
} else {
|
193 |
$this->move['first'][] = $newTag;
|
195 |
} else {
|
196 |
// cannot be moved, so if flag was added re-inject altered tag immediately
|
197 |
if ( $origTag !== $newTag ) {
|
198 |
+
$this->content = str_replace( $origTag, $newTag, $this->content );
|
199 |
$origTag = '';
|
200 |
}
|
201 |
// and forget about the $tag (not to be touched any more)
|
204 |
}
|
205 |
} else {
|
206 |
// Inline script
|
207 |
+
if ( $this->isremovable($tag, $this->jsremovables) ) {
|
208 |
+
$this->content = str_replace( $tag, '', $this->content );
|
209 |
continue;
|
210 |
}
|
211 |
+
|
212 |
// unhide comments, as javascript may be wrapped in comment-tags for old times' sake
|
213 |
$tag = $this->restore_comments($tag);
|
214 |
+
if ( $this->ismergeable($tag) && $this->include_inline ) {
|
215 |
+
preg_match( '#<script.*>(.*)</script>#Usmi', $tag , $code );
|
216 |
+
$code = preg_replace('#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm', '$1', $code[1] );
|
217 |
+
$code = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code );
|
218 |
+
$this->scripts[] = 'INLINE;' . $code;
|
219 |
} else {
|
220 |
// Can we move this?
|
221 |
$autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
|
222 |
+
if ( $this->ismovable($tag) || '' !== $autoptimize_js_moveable ) {
|
223 |
+
if ( $this->movetolast($tag) || 'last' === $autoptimize_js_moveable ) {
|
224 |
$this->move['last'][] = $tag;
|
225 |
} else {
|
226 |
$this->move['first'][] = $tag;
|
227 |
}
|
228 |
} else {
|
229 |
+
// We shouldn't touch this
|
230 |
$tag = '';
|
231 |
}
|
232 |
}
|
233 |
+
// Re-hide comments to be able to do the removal based on tag from $this->content
|
234 |
$tag = $this->hide_comments($tag);
|
235 |
}
|
236 |
+
|
237 |
//Remove the original script tag
|
238 |
+
$this->content = str_replace( $tag, '', $this->content );
|
239 |
}
|
240 |
+
|
241 |
return true;
|
242 |
}
|
243 |
+
|
244 |
// No script files, great ;-)
|
245 |
return false;
|
246 |
}
|
247 |
+
|
248 |
+
/**
|
249 |
+
* Determines wheter a certain `<script>` $tag should be aggregated or not.
|
250 |
+
*
|
251 |
+
* We consider these as "aggregation-safe" currently:
|
252 |
+
* - script tags without a `type` attribute
|
253 |
+
* - script tags with these `type` attribute values: `text/javascript`, `text/ecmascript`, `application/javascript`,
|
254 |
+
* and `application/ecmascript`
|
255 |
+
*
|
256 |
+
* Everything else should return false.
|
257 |
+
*
|
258 |
+
* @link https://developer.mozilla.org/en/docs/Web/HTML/Element/script#attr-type
|
259 |
+
*
|
260 |
+
* @param string $tag
|
261 |
+
* @return bool
|
262 |
+
*/
|
263 |
+
public function should_aggregate($tag)
|
264 |
+
{
|
265 |
+
// We're only interested in the type attribute of the <script> tag itself, not any possible
|
266 |
+
// inline code that might just contain the 'type=' string...
|
267 |
+
$tag_parts = array();
|
268 |
+
preg_match( '#<(script[^>]*)>#i', $tag, $tag_parts);
|
269 |
+
$tag_without_contents = null;
|
270 |
+
if ( ! empty( $tag_parts[1] ) ) {
|
271 |
+
$tag_without_contents = $tag_parts[1];
|
272 |
+
}
|
273 |
+
|
274 |
+
$has_type = ( strpos( $tag_without_contents, 'type' ) !== false );
|
275 |
+
|
276 |
+
$type_valid = false;
|
277 |
+
if ( $has_type ) {
|
278 |
+
$type_valid = (bool) preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $tag_without_contents );
|
279 |
+
}
|
280 |
+
|
281 |
+
$should_aggregate = false;
|
282 |
+
if ( ! $has_type || $type_valid ) {
|
283 |
+
$should_aggregate = true;
|
284 |
+
}
|
285 |
+
|
286 |
+
return $should_aggregate;
|
287 |
+
}
|
288 |
+
|
289 |
//Joins and optimizes JS
|
290 |
+
public function minify()
|
291 |
+
{
|
292 |
+
foreach ( $this->scripts as $script ) {
|
293 |
+
// TODO/FIXME: some duplicate code here, can be reduced/simplified
|
294 |
+
if ( preg_match( '#^INLINE;#', $script ) ) {
|
295 |
+
// Inline script
|
296 |
+
$script = preg_replace( '#^INLINE;#', '', $script );
|
297 |
$script = rtrim( $script, ";\n\t\r" ) . ';';
|
298 |
+
// Add try-catch?
|
299 |
+
if ( $this->trycatch ) {
|
300 |
+
$script = 'try{' . $script . '}catch(e){}';
|
301 |
}
|
302 |
$tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, '' );
|
303 |
+
if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscript ) ) {
|
304 |
+
$script = $tmpscript;
|
305 |
+
$this->alreadyminified = true;
|
306 |
}
|
307 |
$this->jscode .= "\n" . $script;
|
308 |
} else {
|
309 |
+
// External script
|
310 |
+
if ( false !== $script && file_exists( $script ) && is_readable( $script ) ) {
|
311 |
+
$scriptsrc = file_get_contents( $script );
|
312 |
+
$scriptsrc = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $scriptsrc );
|
313 |
+
$scriptsrc = rtrim( $scriptsrc, ";\n\t\r" ) . ';';
|
314 |
+
// Add try-catch?
|
315 |
+
if ( $this->trycatch ) {
|
316 |
+
$scriptsrc = 'try{' . $scriptsrc . '}catch(e){}';
|
|
|
|
|
317 |
}
|
318 |
$tmpscriptsrc = apply_filters( 'autoptimize_js_individual_script', $scriptsrc, $script );
|
319 |
+
if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscriptsrc ) ) {
|
320 |
+
$scriptsrc = $tmpscriptsrc;
|
321 |
+
$this->alreadyminified = true;
|
322 |
+
} else if ( $this->can_inject_late($script) ) {
|
323 |
+
$scriptsrc = self::build_injectlater_marker($script, md5($scriptsrc));
|
324 |
}
|
325 |
+
$this->jscode .= "\n" . $scriptsrc;
|
326 |
}/*else{
|
327 |
//Couldn't read JS. Maybe getpath isn't working?
|
328 |
}*/
|
329 |
}
|
330 |
}
|
331 |
|
332 |
+
// Check for already-minified code
|
333 |
+
$this->md5hash = md5( $this->jscode );
|
334 |
+
$ccheck = new autoptimizeCache($this->md5hash, 'js');
|
335 |
+
if ( $ccheck->check() ) {
|
336 |
$this->jscode = $ccheck->retrieve();
|
337 |
return true;
|
338 |
}
|
339 |
+
unset( $ccheck );
|
340 |
+
|
341 |
+
// $this->jscode has all the uncompressed code now.
|
342 |
+
if ( true !== $this->alreadyminified ) {
|
343 |
+
if ( apply_filters( 'autoptimize_js_do_minify', true ) ) {
|
344 |
+
$tmp_jscode = trim( JSMin::minify( $this->jscode ) );
|
345 |
+
if ( ! empty( $tmp_jscode ) ) {
|
|
|
346 |
$this->jscode = $tmp_jscode;
|
347 |
+
unset( $tmp_jscode );
|
348 |
}
|
349 |
+
$this->jscode = $this->inject_minified( $this->jscode );
|
350 |
$this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode );
|
351 |
return true;
|
352 |
} else {
|
353 |
+
$this->jscode = $this->inject_minified( $this->jscode );
|
354 |
return false;
|
355 |
}
|
|
|
|
|
|
|
|
|
356 |
}
|
357 |
+
|
358 |
$this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode );
|
359 |
return true;
|
360 |
}
|
361 |
+
|
362 |
+
// Caches the JS in uncompressed, deflated and gzipped form.
|
363 |
+
public function cache()
|
364 |
+
{
|
365 |
+
$cache = new autoptimizeCache($this->md5hash, 'js');
|
366 |
+
if ( ! $cache->check() ) {
|
367 |
+
// Cache our code
|
368 |
+
$cache->cache($this->jscode, 'text/javascript');
|
369 |
}
|
370 |
+
$this->url = AUTOPTIMIZE_CACHE_URL . $cache->getname();
|
371 |
$this->url = $this->url_replace_cdn($this->url);
|
372 |
}
|
373 |
+
|
374 |
// Returns the content
|
375 |
+
public function getcontent()
|
376 |
+
{
|
377 |
// Restore the full content
|
378 |
+
if ( ! empty( $this->restofcontent ) ) {
|
379 |
$this->content .= $this->restofcontent;
|
380 |
$this->restofcontent = '';
|
381 |
}
|
382 |
+
|
383 |
// Add the scripts taking forcehead/ deferred (default) into account
|
384 |
+
if ( $this->forcehead ) {
|
385 |
+
$replaceTag = array( '</head>', 'before' );
|
386 |
+
$defer = '';
|
387 |
} else {
|
388 |
+
$replaceTag = array( '</body>', 'before' );
|
389 |
+
$defer = 'defer ';
|
390 |
}
|
391 |
+
|
392 |
$defer = apply_filters( 'autoptimize_filter_js_defer', $defer );
|
|
|
|
|
393 |
|
394 |
+
$bodyreplacementpayload = '<script type="text/javascript" ' . $defer . 'src="' . $this->url . '"></script>';
|
395 |
+
$bodyreplacementpayload = apply_filters( 'autoptimize_filter_js_bodyreplacementpayload', $bodyreplacementpayload );
|
396 |
+
|
397 |
+
$bodyreplacement = implode( '', $this->move['first'] );
|
398 |
$bodyreplacement .= $bodyreplacementpayload;
|
399 |
+
$bodyreplacement .= implode( '', $this->move['last'] );
|
400 |
|
401 |
$replaceTag = apply_filters( 'autoptimize_filter_js_replacetag', $replaceTag );
|
402 |
|
403 |
+
if ( strlen( $this->jscode ) > 0 ) {
|
404 |
+
$this->inject_in_html( $bodyreplacement, $replaceTag );
|
405 |
}
|
|
|
|
|
|
|
406 |
|
407 |
+
// Restore comments.
|
408 |
+
$this->content = $this->restore_comments( $this->content );
|
409 |
+
|
410 |
+
// Restore IE hacks.
|
411 |
+
$this->content = $this->restore_iehacks( $this->content );
|
412 |
|
413 |
+
// Restore noptimize.
|
414 |
+
$this->content = $this->restore_noptimize( $this->content );
|
415 |
+
|
416 |
+
// Return the modified HTML.
|
417 |
return $this->content;
|
418 |
}
|
419 |
+
|
420 |
// Checks against the white- and blacklists
|
421 |
+
private function ismergeable($tag)
|
422 |
+
{
|
423 |
+
if ( ! $this->aggregate ) {
|
424 |
+
return false;
|
425 |
+
}
|
426 |
+
|
427 |
+
if ( ! empty( $this->whitelist ) ) {
|
428 |
+
foreach ( $this->whitelist as $match ) {
|
429 |
+
if (false !== strpos( $tag, $match ) ) {
|
430 |
return true;
|
431 |
}
|
432 |
}
|
434 |
return false;
|
435 |
} else {
|
436 |
foreach($this->domove as $match) {
|
437 |
+
if ( false !== strpos( $tag, $match ) ) {
|
438 |
// Matched something
|
439 |
return false;
|
440 |
}
|
441 |
}
|
442 |
+
|
443 |
+
if ( $this->movetolast($tag) ) {
|
444 |
return false;
|
445 |
}
|
446 |
+
|
447 |
+
foreach( $this->dontmove as $match ) {
|
448 |
+
if ( false !== strpos( $tag, $match ) ) {
|
449 |
+
// Matched something
|
450 |
return false;
|
451 |
}
|
452 |
}
|
453 |
+
|
454 |
// If we're here it's safe to merge
|
455 |
return true;
|
456 |
}
|
457 |
}
|
458 |
+
|
459 |
+
// Checks agains the blacklist
|
460 |
+
private function ismovable($tag)
|
461 |
+
{
|
462 |
+
if ( true !== $this->include_inline || apply_filters( 'autoptimize_filter_js_unmovable', true ) ) {
|
463 |
return false;
|
464 |
}
|
465 |
+
|
466 |
+
foreach ( $this->domove as $match ) {
|
467 |
+
if ( false !== strpos( $tag, $match ) ) {
|
468 |
// Matched something
|
469 |
return true;
|
470 |
}
|
471 |
}
|
472 |
+
|
473 |
+
if ( $this->movetolast($tag) ) {
|
474 |
return true;
|
475 |
}
|
476 |
+
|
477 |
+
foreach ( $this->dontmove as $match ) {
|
478 |
+
if ( false !== strpos( $tag, $match ) ) {
|
479 |
// Matched something
|
480 |
return false;
|
481 |
}
|
482 |
}
|
483 |
+
|
484 |
// If we're here it's safe to move
|
485 |
return true;
|
486 |
}
|
487 |
+
|
488 |
+
private function movetolast($tag)
|
489 |
+
{
|
490 |
+
foreach ( $this->domovelast as $match ) {
|
491 |
+
if ( false !== strpos( $tag, $match ) ) {
|
492 |
// Matched, return true
|
493 |
return true;
|
494 |
}
|
495 |
}
|
496 |
+
|
497 |
// Should be in 'first'
|
498 |
return false;
|
499 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
500 |
|
501 |
/**
|
502 |
* Determines wheter a <script> $tag can be excluded from minification (as already minified) based on:
|
504 |
* - filename ending in `min.js`
|
505 |
* - filename matching `js/jquery/jquery.js` (wordpress core jquery, is minified)
|
506 |
* - filename matching one passed in the consider minified filter
|
507 |
+
*
|
508 |
* @param string $jsPath
|
509 |
* @return bool
|
510 |
+
*/
|
511 |
+
private function can_inject_late($jsPath) {
|
512 |
+
$consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
|
513 |
+
if ( true !== $this->inject_min_late ) {
|
514 |
// late-inject turned off
|
515 |
return false;
|
516 |
+
} else if ( ( false === strpos( $jsPath, 'min.js' ) ) && ( false === strpos( $jsPath, 'wp-includes/js/jquery/jquery.js' ) ) && ( str_replace( $consider_minified_array, '', $jsPath ) === $jsPath ) ) {
|
517 |
+
// file not minified based on filename & filter
|
518 |
+
return false;
|
519 |
} else {
|
520 |
// phew, all is safe, we can late-inject
|
521 |
return true;
|
522 |
}
|
523 |
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* Returns whether we're doing aggregation or not.
|
527 |
+
*
|
528 |
+
* @return bool
|
529 |
+
*/
|
530 |
+
public function aggregating()
|
531 |
+
{
|
532 |
+
return $this->aggregate;
|
533 |
+
}
|
534 |
+
|
535 |
+
/**
|
536 |
+
* Minifies a single local js file and returns its (cached) url.
|
537 |
+
*
|
538 |
+
* @param string $filepath Filepath.
|
539 |
+
* @param bool $cache_miss Optional. Force a cache miss. Default false.
|
540 |
+
*
|
541 |
+
* @return bool|string Url pointing to the minified js file or false.
|
542 |
+
*/
|
543 |
+
public function minify_single( $filepath, $cache_miss = false )
|
544 |
+
{
|
545 |
+
$contents = $this->prepare_minify_single( $filepath );
|
546 |
+
|
547 |
+
if ( empty( $contents ) ) {
|
548 |
+
return false;
|
549 |
+
}
|
550 |
+
|
551 |
+
// Check cache.
|
552 |
+
$hash = 'single_' . md5( $contents );
|
553 |
+
$cache = new autoptimizeCache( $hash, 'js' );
|
554 |
+
|
555 |
+
// If not in cache already, minify...
|
556 |
+
if ( ! $cache->check() || $cache_miss ) {
|
557 |
+
$contents = trim( JSMin::minify( $contents ) );
|
558 |
+
// Store in cache.
|
559 |
+
$cache->cache( $contents, 'text/javascript' );
|
560 |
+
}
|
561 |
+
|
562 |
+
$url = $this->build_minify_single_url( $cache );
|
563 |
+
|
564 |
+
return $url;
|
565 |
+
}
|
566 |
}
|
classes/autoptimizeSpeedupper.php
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Autoptimize SpeedUp; minify & cache each JS/ CSS separately
|
4 |
+
* new in Autoptimize 2.2
|
5 |
+
*/
|
6 |
+
|
7 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
8 |
+
exit;
|
9 |
+
}
|
10 |
+
|
11 |
+
class autoptimizeSpeedupper
|
12 |
+
{
|
13 |
+
public function __construct()
|
14 |
+
{
|
15 |
+
$this->add_hooks();
|
16 |
+
}
|
17 |
+
|
18 |
+
public function add_hooks()
|
19 |
+
{
|
20 |
+
if ( apply_filters( 'autoptimize_js_do_minify', true ) ) {
|
21 |
+
add_filter( 'autoptimize_js_individual_script', array( $this, 'js_snippetcacher' ), 10, 2 );
|
22 |
+
add_filter( 'autoptimize_js_after_minify', array( $this, 'js_cleanup' ), 10, 1 );
|
23 |
+
}
|
24 |
+
if ( apply_filters( 'autoptimize_css_do_minify', true ) ) {
|
25 |
+
add_filter( 'autoptimize_css_individual_style', array( $this, 'css_snippetcacher' ), 10, 2 );
|
26 |
+
add_filter( 'autoptimize_css_after_minify', array( $this, 'css_cleanup' ), 10, 1 );
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
public function js_snippetcacher( $jsin, $jsfilename )
|
31 |
+
{
|
32 |
+
$md5hash = 'snippet_' . md5( $jsin );
|
33 |
+
$ccheck = new autoptimizeCache( $md5hash, 'js' );
|
34 |
+
if ( $ccheck->check() ) {
|
35 |
+
$scriptsrc = $ccheck->retrieve();
|
36 |
+
} else {
|
37 |
+
if ( false === ( strpos( $jsfilename, 'min.js' ) ) && ( false === strpos( $jsfilename, 'js/jquery/jquery.js' ) ) && ( str_replace( apply_filters( 'autoptimize_filter_js_consider_minified', false ), '', $jsfilename ) === $jsfilename ) ) {
|
38 |
+
$tmp_jscode = trim( JSMin::minify( $jsin ) );
|
39 |
+
if ( ! empty( $tmp_jscode ) ) {
|
40 |
+
$scriptsrc = $tmp_jscode;
|
41 |
+
unset( $tmp_jscode );
|
42 |
+
} else {
|
43 |
+
$scriptsrc = $jsin;
|
44 |
+
}
|
45 |
+
} else {
|
46 |
+
// Removing comments, linebreaks and stuff!
|
47 |
+
$scriptsrc = preg_replace( '#^\s*\/\/.*$#Um', '', $jsin );
|
48 |
+
$scriptsrc = preg_replace( '#^\s*\/\*[^!].*\*\/\s?#Us', '', $scriptsrc );
|
49 |
+
$scriptsrc = preg_replace( "#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $scriptsrc );
|
50 |
+
}
|
51 |
+
|
52 |
+
$last_char = substr( $scriptsrc, -1, 1 );
|
53 |
+
if ( ';' !== $last_char && '}' !== $last_char ) {
|
54 |
+
$scriptsrc .= ';';
|
55 |
+
}
|
56 |
+
|
57 |
+
if ( ! empty( $jsfilename ) && str_replace( apply_filters( 'autoptimize_filter_js_speedup_cache', false ), '', $jsfilename ) === $jsfilename ) {
|
58 |
+
// Don't cache inline CSS or if filter says no!
|
59 |
+
$ccheck->cache( $scriptsrc, 'text/javascript' );
|
60 |
+
}
|
61 |
+
}
|
62 |
+
unset( $ccheck );
|
63 |
+
|
64 |
+
return $scriptsrc;
|
65 |
+
}
|
66 |
+
|
67 |
+
public function css_snippetcacher( $cssin, $cssfilename )
|
68 |
+
{
|
69 |
+
$md5hash = 'snippet_' . md5( $cssin );
|
70 |
+
$ccheck = new autoptimizeCache( $md5hash, 'css' );
|
71 |
+
if ( $ccheck->check() ) {
|
72 |
+
$stylesrc = $ccheck->retrieve();
|
73 |
+
} else {
|
74 |
+
if ( ( false === strpos( $cssfilename, 'min.css' ) ) && ( str_replace( apply_filters( 'autoptimize_filter_css_consider_minified', false ), '', $cssfilename ) === $cssfilename ) ) {
|
75 |
+
$cssmin = new autoptimizeCSSmin();
|
76 |
+
$tmp_code = trim( $cssmin->run( $cssin ) );
|
77 |
+
|
78 |
+
if ( ! empty( $tmp_code ) ) {
|
79 |
+
$stylesrc = $tmp_code;
|
80 |
+
unset( $tmp_code );
|
81 |
+
} else {
|
82 |
+
$stylesrc = $cssin;
|
83 |
+
}
|
84 |
+
} else {
|
85 |
+
// .min.css -> no heavy-lifting, just some cleanup!
|
86 |
+
$stylesrc = preg_replace( '#^\s*\/\*[^!].*\*\/\s?#Us', '', $cssin );
|
87 |
+
$stylesrc = preg_replace( "#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $stylesrc );
|
88 |
+
$stylesrc = autoptimizeStyles::fixurls( $cssfilename, $stylesrc );
|
89 |
+
}
|
90 |
+
if ( ! empty( $cssfilename ) && ( str_replace( apply_filters( 'autoptimize_filter_css_speedup_cache', false ), '', $cssfilename ) === $cssfilename ) ) {
|
91 |
+
// Only caching CSS if it's not inline and is allowed by filter!
|
92 |
+
$ccheck->cache( $stylesrc, 'text/css' );
|
93 |
+
}
|
94 |
+
}
|
95 |
+
unset( $ccheck );
|
96 |
+
|
97 |
+
return $stylesrc;
|
98 |
+
}
|
99 |
+
|
100 |
+
public function css_cleanup( $cssin )
|
101 |
+
{
|
102 |
+
// Speedupper results in aggregated CSS not being minified, so the filestart-marker AO adds when aggregating needs to be removed.
|
103 |
+
return trim( str_replace( array( '/*FILESTART*/', '/*FILESTART2*/' ), '', $cssin ) );
|
104 |
+
}
|
105 |
+
|
106 |
+
public function js_cleanup( $jsin )
|
107 |
+
{
|
108 |
+
return trim( $jsin );
|
109 |
+
}
|
110 |
+
}
|
classes/autoptimizeStyles.php
CHANGED
@@ -1,701 +1,1070 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
$
|
59 |
-
if ($
|
60 |
-
$this->
|
61 |
-
}
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
$this->
|
72 |
-
|
73 |
-
//
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
//
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
$this->
|
109 |
-
|
110 |
-
//
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
$
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
$
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
$
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
$
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
$
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class for CSS optimization.
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
+
class autoptimizeStyles extends autoptimizeBase
|
11 |
+
{
|
12 |
+
const ASSETS_REGEX = '/url\s*\(\s*(?!["\']?data:)(?![\'|\"]?[\#|\%|])([^)]+)\s*\)([^;},\s]*)/i';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Font-face regex-fu from HamZa at: https://stackoverflow.com/a/21395083
|
16 |
+
* ~
|
17 |
+
* @font-face\s* # Match @font-face and some spaces
|
18 |
+
* ( # Start group 1
|
19 |
+
* \{ # Match {
|
20 |
+
* (?: # A non-capturing group
|
21 |
+
* [^{}]+ # Match anything except {} one or more times
|
22 |
+
* | # Or
|
23 |
+
* (?1) # Recurse/rerun the expression of group 1
|
24 |
+
* )* # Repeat 0 or more times
|
25 |
+
* \} # Match }
|
26 |
+
* ) # End group 1
|
27 |
+
* ~xs';
|
28 |
+
*/
|
29 |
+
const FONT_FACE_REGEX = '~@font-face\s*(\{(?:[^{}]+|(?1))*\})~xsi'; // added `i` flag for case-insensitivity.
|
30 |
+
|
31 |
+
private $css = array();
|
32 |
+
private $csscode = array();
|
33 |
+
private $url = array();
|
34 |
+
private $restofcontent = '';
|
35 |
+
private $datauris = false;
|
36 |
+
private $hashmap = array();
|
37 |
+
private $alreadyminified = false;
|
38 |
+
private $aggregate = true;
|
39 |
+
private $inline = false;
|
40 |
+
private $defer = false;
|
41 |
+
private $defer_inline = false;
|
42 |
+
private $whitelist = '';
|
43 |
+
private $cssinlinesize = '';
|
44 |
+
private $cssremovables = array();
|
45 |
+
private $include_inline = false;
|
46 |
+
private $inject_min_late = '';
|
47 |
+
|
48 |
+
// public $cdn_url; // Used all over the place implicitly, so will have to be either public or protected :/ .
|
49 |
+
|
50 |
+
// Reads the page and collects style tags.
|
51 |
+
public function read( $options )
|
52 |
+
{
|
53 |
+
$noptimizeCSS = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content );
|
54 |
+
if ( $noptimizeCSS ) {
|
55 |
+
return false;
|
56 |
+
}
|
57 |
+
|
58 |
+
$whitelistCSS = apply_filters( 'autoptimize_filter_css_whitelist', '', $this->content );
|
59 |
+
if ( ! empty( $whitelistCSS ) ) {
|
60 |
+
$this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelistCSS ) ) );
|
61 |
+
}
|
62 |
+
|
63 |
+
$removableCSS = apply_filters( 'autoptimize_filter_css_removables', '' );
|
64 |
+
if ( ! empty( $removableCSS ) ) {
|
65 |
+
$this->cssremovables = array_filter( array_map( 'trim', explode( ',', $removableCSS ) ) );
|
66 |
+
}
|
67 |
+
|
68 |
+
$this->cssinlinesize = apply_filters( 'autoptimize_filter_css_inlinesize', 256 );
|
69 |
+
|
70 |
+
// filter to "late inject minified CSS", default to true for now (it is faster).
|
71 |
+
$this->inject_min_late = apply_filters( 'autoptimize_filter_css_inject_min_late', true );
|
72 |
+
|
73 |
+
// Remove everything that's not the header.
|
74 |
+
if ( apply_filters( 'autoptimize_filter_css_justhead', $options['justhead'] ) ) {
|
75 |
+
$content = explode( '</head>', $this->content, 2 );
|
76 |
+
$this->content = $content[0] . '</head>';
|
77 |
+
$this->restofcontent = $content[1];
|
78 |
+
}
|
79 |
+
|
80 |
+
// Determine whether we're doing CSS-files aggregation or not.
|
81 |
+
if ( isset( $options['aggregate'] ) && ! $options['aggregate'] ) {
|
82 |
+
$this->aggregate = false;
|
83 |
+
}
|
84 |
+
// Returning true for "dontaggregate" turns off aggregation.
|
85 |
+
if ( $this->aggregate && apply_filters( 'autoptimize_filter_css_dontaggregate', false ) ) {
|
86 |
+
$this->aggregate = false;
|
87 |
+
}
|
88 |
+
|
89 |
+
// include inline?
|
90 |
+
if ( apply_filters( 'autoptimize_css_include_inline', $options['include_inline'] ) ) {
|
91 |
+
$this->include_inline = true;
|
92 |
+
}
|
93 |
+
|
94 |
+
// List of CSS strings which are excluded from autoptimization.
|
95 |
+
$excludeCSS = apply_filters( 'autoptimize_filter_css_exclude', $options['css_exclude'], $this->content );
|
96 |
+
if ( '' !== $excludeCSS ) {
|
97 |
+
$this->dontmove = array_filter( array_map( 'trim', explode( ',', $excludeCSS ) ) );
|
98 |
+
} else {
|
99 |
+
$this->dontmove = array();
|
100 |
+
}
|
101 |
+
|
102 |
+
// forcefully exclude CSS with data-noptimize attrib.
|
103 |
+
$this->dontmove[] = 'data-noptimize';
|
104 |
+
|
105 |
+
// Should we defer css?
|
106 |
+
// value: true / false.
|
107 |
+
$this->defer = $options['defer'];
|
108 |
+
$this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer, $this->content );
|
109 |
+
|
110 |
+
// Should we inline while deferring?
|
111 |
+
// value: inlined CSS.
|
112 |
+
$this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $options['defer_inline'], $this->content );
|
113 |
+
|
114 |
+
// Should we inline?
|
115 |
+
// value: true / false.
|
116 |
+
$this->inline = $options['inline'];
|
117 |
+
$this->inline = apply_filters( 'autoptimize_filter_css_inline', $this->inline, $this->content );
|
118 |
+
|
119 |
+
// Store cdn url.
|
120 |
+
$this->cdn_url = $options['cdn_url'];
|
121 |
+
|
122 |
+
// Store data: URIs setting for later use.
|
123 |
+
$this->datauris = $options['datauris'];
|
124 |
+
|
125 |
+
// noptimize me.
|
126 |
+
$this->content = $this->hide_noptimize( $this->content );
|
127 |
+
|
128 |
+
// Exclude (no)script, as those may contain CSS which should be left as is.
|
129 |
+
$this->content = $this->replace_contents_with_marker_if_exists(
|
130 |
+
'SCRIPT',
|
131 |
+
'<script',
|
132 |
+
'#<(?:no)?script.*?<\/(?:no)?script>#is',
|
133 |
+
$this->content
|
134 |
+
);
|
135 |
+
|
136 |
+
// Save IE hacks.
|
137 |
+
$this->content = $this->hide_iehacks( $this->content );
|
138 |
+
|
139 |
+
// Hide HTML comments.
|
140 |
+
$this->content = $this->hide_comments( $this->content );
|
141 |
+
|
142 |
+
// Get <style> and <link>.
|
143 |
+
if ( preg_match_all( '#(<style[^>]*>.*</style>)|(<link[^>]*stylesheet[^>]*>)#Usmi', $this->content, $matches ) ) {
|
144 |
+
|
145 |
+
foreach ( $matches[0] as $tag ) {
|
146 |
+
if ( $this->isremovable( $tag, $this->cssremovables ) ) {
|
147 |
+
$this->content = str_replace( $tag, '', $this->content );
|
148 |
+
} elseif ( $this->ismovable( $tag ) ) {
|
149 |
+
// Get the media.
|
150 |
+
if ( false !== strpos( $tag, 'media=' ) ) {
|
151 |
+
preg_match( '#media=(?:"|\')([^>]*)(?:"|\')#Ui', $tag, $medias );
|
152 |
+
$medias = explode( ',', $medias[1] );
|
153 |
+
$media = array();
|
154 |
+
foreach ( $medias as $elem ) {
|
155 |
+
/* $media[] = current(explode(' ',trim($elem),2)); */
|
156 |
+
if ( empty( $elem ) ) {
|
157 |
+
$elem = 'all';
|
158 |
+
}
|
159 |
+
|
160 |
+
$media[] = $elem;
|
161 |
+
}
|
162 |
+
} else {
|
163 |
+
// No media specified - applies to all.
|
164 |
+
$media = array( 'all' );
|
165 |
+
}
|
166 |
+
|
167 |
+
$media = apply_filters( 'autoptimize_filter_css_tagmedia', $media, $tag );
|
168 |
+
|
169 |
+
if ( preg_match( '#<link.*href=("|\')(.*)("|\')#Usmi', $tag, $source ) ) {
|
170 |
+
// <link>.
|
171 |
+
$url = current( explode( '?', $source[2], 2 ) );
|
172 |
+
$path = $this->getpath( $url );
|
173 |
+
|
174 |
+
if ( false !== $path && preg_match( '#\.css$#', $path ) ) {
|
175 |
+
// Good link.
|
176 |
+
$this->css[] = array( $media, $path );
|
177 |
+
} else {
|
178 |
+
// Link is dynamic (.php etc).
|
179 |
+
$tag = '';
|
180 |
+
}
|
181 |
+
} else {
|
182 |
+
// Inline css in style tags can be wrapped in comment tags, so restore comments.
|
183 |
+
$tag = $this->restore_comments( $tag );
|
184 |
+
preg_match( '#<style.*>(.*)</style>#Usmi', $tag, $code );
|
185 |
+
|
186 |
+
// And re-hide them to be able to to the removal based on tag.
|
187 |
+
$tag = $this->hide_comments( $tag );
|
188 |
+
|
189 |
+
if ( $this->include_inline ) {
|
190 |
+
$code = preg_replace( '#^.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*$#sm', '$1', $code[1] );
|
191 |
+
$this->css[] = array( $media, 'INLINE;' . $code );
|
192 |
+
} else {
|
193 |
+
$tag = '';
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
// Remove the original style tag.
|
198 |
+
$this->content = str_replace( $tag, '', $this->content );
|
199 |
+
} else {
|
200 |
+
// Excluded CSS, minify if getpath and filter says so...
|
201 |
+
if ( preg_match( '#<link.*href=("|\')(.*)("|\')#Usmi', $tag, $source ) ) {
|
202 |
+
$exploded_url = explode( '?', $source[2], 2 );
|
203 |
+
$url = $exploded_url[0];
|
204 |
+
$path = $this->getpath( $url );
|
205 |
+
|
206 |
+
if ( $path && apply_filters( 'autoptimize_filter_css_minify_excluded', true, $url ) ) {
|
207 |
+
$minified_url = $this->minify_single( $path );
|
208 |
+
if ( ! empty( $minified_url ) ) {
|
209 |
+
// Replace orig URL with cached minified URL.
|
210 |
+
$new_tag = str_replace( $url, $minified_url, $tag );
|
211 |
+
} else {
|
212 |
+
$new_tag = $tag;
|
213 |
+
}
|
214 |
+
|
215 |
+
// Removes querystring from URL.
|
216 |
+
if ( ! empty( $exploded_url[1] ) ) {
|
217 |
+
$new_tag = str_replace( '?' . $exploded_url[1], '', $new_tag );
|
218 |
+
}
|
219 |
+
|
220 |
+
// Defer single CSS if "inline & defer" is ON and there is inline CSS.
|
221 |
+
if ( $this->defer && ! empty( $this->defer_inline ) ) {
|
222 |
+
// Get/ set (via filter) the JS to be triggers onload of the preloaded CSS.
|
223 |
+
$_preload_onload = apply_filters(
|
224 |
+
'autoptimize_filter_css_preload_onload',
|
225 |
+
"this.onload=null;this.rel='stylesheet'",
|
226 |
+
$url
|
227 |
+
);
|
228 |
+
// Adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback.
|
229 |
+
$new_tag = '<noscript>' . $new_tag . '</noscript>' . str_replace(
|
230 |
+
array(
|
231 |
+
"rel='stylesheet'",
|
232 |
+
'rel="stylesheet"',
|
233 |
+
),
|
234 |
+
"rel='preload' as='style' onload=\"" . $_preload_onload . "\"",
|
235 |
+
$new_tag
|
236 |
+
);
|
237 |
+
}
|
238 |
+
|
239 |
+
// And replace!
|
240 |
+
$this->content = str_replace( $tag, $new_tag, $this->content );
|
241 |
+
}
|
242 |
+
}
|
243 |
+
}
|
244 |
+
}
|
245 |
+
return true;
|
246 |
+
}
|
247 |
+
|
248 |
+
// Really, no styles?
|
249 |
+
return false;
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Checks if the local file referenced by $path is a valid
|
254 |
+
* candidate for being inlined into a data: URI
|
255 |
+
*
|
256 |
+
* @param string $path
|
257 |
+
* @return boolean
|
258 |
+
*/
|
259 |
+
private function is_datauri_candidate( $path )
|
260 |
+
{
|
261 |
+
// Call only once since it's called from a loop.
|
262 |
+
static $max_size = null;
|
263 |
+
if ( null === $max_size ) {
|
264 |
+
$max_size = $this->get_datauri_maxsize();
|
265 |
+
}
|
266 |
+
|
267 |
+
if ( $path && preg_match( '#\.(jpe?g|png|gif|webp|bmp)$#i', $path ) &&
|
268 |
+
file_exists( $path ) && is_readable( $path ) && filesize( $path ) <= $max_size ) {
|
269 |
+
|
270 |
+
// Seems we have a candidate.
|
271 |
+
$is_candidate = true;
|
272 |
+
} else {
|
273 |
+
// Filter allows overriding default decision (which checks for local file existence).
|
274 |
+
$is_candidate = apply_filters( 'autoptimize_filter_css_is_datauri_candidate', false, $path );
|
275 |
+
}
|
276 |
+
|
277 |
+
return $is_candidate;
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* Returns the amount of bytes that shouldn't be exceeded if a file is to
|
282 |
+
* be inlined into a data: URI. Defaults to 4096, passed through
|
283 |
+
* `autoptimize_filter_css_datauri_maxsize` filter.
|
284 |
+
*
|
285 |
+
* @return mixed
|
286 |
+
*/
|
287 |
+
private function get_datauri_maxsize()
|
288 |
+
{
|
289 |
+
static $max_size = null;
|
290 |
+
|
291 |
+
/**
|
292 |
+
* No need to apply the filter multiple times in case the
|
293 |
+
* method itself is invoked multiple times during a single request.
|
294 |
+
* This prevents some wild stuff like having different maxsizes
|
295 |
+
* for different files/site-sections etc. But if you're into that sort
|
296 |
+
* of thing you're probably better of building assets completely
|
297 |
+
* outside of WordPress anyway.
|
298 |
+
*/
|
299 |
+
if ( null === $max_size ) {
|
300 |
+
$max_size = (int) apply_filters( 'autoptimize_filter_css_datauri_maxsize', 4096 );
|
301 |
+
}
|
302 |
+
|
303 |
+
return $max_size;
|
304 |
+
}
|
305 |
+
|
306 |
+
private function check_datauri_exclude_list( $url )
|
307 |
+
{
|
308 |
+
static $exclude_list = null;
|
309 |
+
$no_datauris = array();
|
310 |
+
|
311 |
+
// Again, skip doing certain stuff repeatedly when loop-called.
|
312 |
+
if ( null === $exclude_list ) {
|
313 |
+
$exclude_list = apply_filters( 'autoptimize_filter_css_datauri_exclude', '' );
|
314 |
+
$no_datauris = array_filter( array_map( 'trim', explode( ',', $exclude_list ) ) );
|
315 |
+
}
|
316 |
+
|
317 |
+
$matched = false;
|
318 |
+
|
319 |
+
if ( ! empty( $exclude_list ) ) {
|
320 |
+
foreach ( $no_datauris as $no_datauri ) {
|
321 |
+
if ( false !== strpos( $url, $no_datauri ) ) {
|
322 |
+
$matched = true;
|
323 |
+
break;
|
324 |
+
}
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
return $matched;
|
329 |
+
}
|
330 |
+
|
331 |
+
private function build_or_get_datauri_image( $path )
|
332 |
+
{
|
333 |
+
/**
|
334 |
+
* TODO/FIXME: document the required return array format, or better yet,
|
335 |
+
* use a string, since we don't really need an array for this. That would, however,
|
336 |
+
* require changing even more code, which is not happening right now...
|
337 |
+
*/
|
338 |
+
|
339 |
+
// Allows short-circuiting datauri generation for an image.
|
340 |
+
$result = apply_filters( 'autoptimize_filter_css_datauri_image', array(), $path );
|
341 |
+
if ( ! empty( $result ) ) {
|
342 |
+
if ( is_array( $result ) && isset( $result['full'] ) && isset( $result['base64data'] ) ) {
|
343 |
+
return $result;
|
344 |
+
}
|
345 |
+
}
|
346 |
+
|
347 |
+
$hash = md5( $path );
|
348 |
+
$check = new autoptimizeCache( $hash, 'img' );
|
349 |
+
if ( $check->check() ) {
|
350 |
+
// we have the base64 image in cache.
|
351 |
+
$headAndData = $check->retrieve();
|
352 |
+
$_base64data = explode( ';base64,', $headAndData );
|
353 |
+
$base64data = $_base64data[1];
|
354 |
+
unset( $_base64data );
|
355 |
+
} else {
|
356 |
+
// It's an image and we don't have it in cache, get the type by extension.
|
357 |
+
$exploded_path = explode( '.', $path );
|
358 |
+
$type = end( $exploded_path );
|
359 |
+
|
360 |
+
switch ( $type ) {
|
361 |
+
case 'jpg':
|
362 |
+
case 'jpeg':
|
363 |
+
$dataurihead = 'data:image/jpeg;base64,';
|
364 |
+
break;
|
365 |
+
case 'gif':
|
366 |
+
$dataurihead = 'data:image/gif;base64,';
|
367 |
+
break;
|
368 |
+
case 'png':
|
369 |
+
$dataurihead = 'data:image/png;base64,';
|
370 |
+
break;
|
371 |
+
case 'bmp':
|
372 |
+
$dataurihead = 'data:image/bmp;base64,';
|
373 |
+
break;
|
374 |
+
case 'webp':
|
375 |
+
$dataurihead = 'data:image/webp;base64,';
|
376 |
+
break;
|
377 |
+
default:
|
378 |
+
$dataurihead = 'data:application/octet-stream;base64,';
|
379 |
+
}
|
380 |
+
|
381 |
+
// Encode the data.
|
382 |
+
$base64data = base64_encode( file_get_contents( $path ) );
|
383 |
+
$headAndData = $dataurihead . $base64data;
|
384 |
+
|
385 |
+
// Save in cache.
|
386 |
+
$check->cache( $headAndData, 'text/plain' );
|
387 |
+
}
|
388 |
+
unset( $check );
|
389 |
+
|
390 |
+
return array( 'full' => $headAndData, 'base64data' => $base64data );
|
391 |
+
}
|
392 |
+
|
393 |
+
/**
|
394 |
+
* Given an array of key/value pairs to replace in $string,
|
395 |
+
* it does so by replacing the longest-matching strings first.
|
396 |
+
*
|
397 |
+
* @param string $string
|
398 |
+
* @param array $replacements
|
399 |
+
*
|
400 |
+
* @return string
|
401 |
+
*/
|
402 |
+
protected static function replace_longest_matches_first( $string, $replacements = array() )
|
403 |
+
{
|
404 |
+
if ( ! empty( $replacements ) ) {
|
405 |
+
// Sort the replacements array by key length in desc order (so that the longest strings are replaced first).
|
406 |
+
$keys = array_map( 'strlen', array_keys( $replacements ) );
|
407 |
+
array_multisort( $keys, SORT_DESC, $replacements );
|
408 |
+
$string = str_replace( array_keys( $replacements ), array_values( $replacements ), $string );
|
409 |
+
}
|
410 |
+
|
411 |
+
return $string;
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* Rewrites/Replaces any ASSETS_REGEX-matching urls in a string.
|
416 |
+
* Removes quotes/cruft around each one and passes it through to
|
417 |
+
* `autoptimizeBase::url_replace_cdn()`.
|
418 |
+
* Replacements are performed in a `longest-match-replaced-first` way.
|
419 |
+
*
|
420 |
+
* @param string $code CSS code.
|
421 |
+
*
|
422 |
+
* @return string
|
423 |
+
*/
|
424 |
+
public function replace_urls( $code = '' )
|
425 |
+
{
|
426 |
+
$replacements = array();
|
427 |
+
|
428 |
+
preg_match_all( self::ASSETS_REGEX, $code, $url_src_matches );
|
429 |
+
if ( is_array( $url_src_matches ) && ! empty( $url_src_matches ) ) {
|
430 |
+
foreach ( $url_src_matches[1] as $count => $original_url ) {
|
431 |
+
// Removes quotes and other cruft.
|
432 |
+
$url = trim( $original_url, " \t\n\r\0\x0B\"'" );
|
433 |
+
|
434 |
+
/**
|
435 |
+
* TODO/FIXME: Add a way for other code / callable to be called here
|
436 |
+
* and provide it's own results for the $replacements array
|
437 |
+
* for the "current" key.
|
438 |
+
* If such a result is returned/provided, we sholud then avoid
|
439 |
+
* calling url_replace_cdn() here for the current iteration.
|
440 |
+
*
|
441 |
+
* This would maybe allow the inlining logic currently present
|
442 |
+
* in `autoptimizeStyles::rewrite_assets()` to be "pulled out"
|
443 |
+
* and given as a callable to this method or something... and
|
444 |
+
* then we could "just" call `replace_urls()` from within
|
445 |
+
* `autoptimizeStyles::rewrite_assets()` and avoid some
|
446 |
+
* (currently present) code/logic duplication.
|
447 |
+
*/
|
448 |
+
|
449 |
+
// Do CDN replacement if needed.
|
450 |
+
if ( ! empty( $this->cdn_url ) ) {
|
451 |
+
$replacement_url = $this->url_replace_cdn( $url );
|
452 |
+
// Prepare replacements array.
|
453 |
+
$replacements[ $url_src_matches[1][ $count ] ] = str_replace(
|
454 |
+
$original_url, $replacement_url, $url_src_matches[1][$count]
|
455 |
+
);
|
456 |
+
}
|
457 |
+
}
|
458 |
+
}
|
459 |
+
|
460 |
+
$code = self::replace_longest_matches_first( $code, $replacements );
|
461 |
+
|
462 |
+
return $code;
|
463 |
+
}
|
464 |
+
|
465 |
+
/**
|
466 |
+
* "Hides" @font-face declarations by replacing them with `%%FONTFACE%%` markers.
|
467 |
+
* Also does CDN replacement of any font-urls within those declarations if the `autoptimize_filter_css_fonts_cdn`
|
468 |
+
* filter is used.
|
469 |
+
*
|
470 |
+
* @param string $code
|
471 |
+
* @return string
|
472 |
+
*/
|
473 |
+
public function hide_fontface_and_maybe_cdn( $code )
|
474 |
+
{
|
475 |
+
// Proceed only if @font-face declarations exist within $code.
|
476 |
+
preg_match_all( self::FONT_FACE_REGEX, $code, $fontfaces );
|
477 |
+
if ( isset( $fontfaces[0] ) ) {
|
478 |
+
// Check if we need to cdn fonts or not.
|
479 |
+
$do_font_cdn = apply_filters( 'autoptimize_filter_css_fonts_cdn', false );
|
480 |
+
|
481 |
+
foreach ( $fontfaces[0] as $full_match ) {
|
482 |
+
// Keep original match so we can search/replace it.
|
483 |
+
$match_search = $full_match;
|
484 |
+
|
485 |
+
// Do font cdn if needed.
|
486 |
+
if ( $do_font_cdn ) {
|
487 |
+
$full_match = $this->replace_urls( $full_match );
|
488 |
+
}
|
489 |
+
|
490 |
+
// Replace declaration with its base64 encoded string.
|
491 |
+
$replacement = self::build_marker( 'FONTFACE', $full_match );
|
492 |
+
$code = str_replace( $match_search, $replacement, $code );
|
493 |
+
}
|
494 |
+
}
|
495 |
+
|
496 |
+
return $code;
|
497 |
+
}
|
498 |
+
|
499 |
+
/**
|
500 |
+
* Restores original @font-face declarations that have been "hidden"
|
501 |
+
* using `hide_fontface_and_maybe_cdn()`.
|
502 |
+
*
|
503 |
+
* @param string $code
|
504 |
+
* @return string
|
505 |
+
*/
|
506 |
+
public function restore_fontface( $code )
|
507 |
+
{
|
508 |
+
return $this->restore_marked_content( 'FONTFACE', $code );
|
509 |
+
}
|
510 |
+
|
511 |
+
// Re-write (and/or inline) referenced assets.
|
512 |
+
public function rewrite_assets( $code )
|
513 |
+
{
|
514 |
+
// Handle @font-face rules by hiding and processing them separately.
|
515 |
+
$code = $this->hide_fontface_and_maybe_cdn( $code );
|
516 |
+
|
517 |
+
/**
|
518 |
+
* TODO/FIXME:
|
519 |
+
* Certain code parts below are kind-of repeated now in `replace_urls()`, which is not ideal.
|
520 |
+
* There is maybe a way to separate/refactor things and then be able to keep
|
521 |
+
* the ASSETS_REGEX rewriting/handling logic in a single place (along with removing quotes/cruft from matched urls).
|
522 |
+
* See comments in `replace_urls()` regarding this. The idea is to extract the inlining
|
523 |
+
* logic out (which is the only real difference between replace_urls() and the code below), but still
|
524 |
+
* achieve identical results as before.
|
525 |
+
*/
|
526 |
+
|
527 |
+
// Re-write (and/or inline) URLs to point them to the CDN host.
|
528 |
+
$url_src_matches = array();
|
529 |
+
$imgreplace = array();
|
530 |
+
// Matches and captures anything specified within the literal `url()` and excludes those containing data: URIs.
|
531 |
+
preg_match_all( self::ASSETS_REGEX, $code, $url_src_matches );
|
532 |
+
if ( is_array( $url_src_matches ) && ! empty( $url_src_matches ) ) {
|
533 |
+
foreach ( $url_src_matches[1] as $count => $original_url ) {
|
534 |
+
// Removes quotes and other cruft.
|
535 |
+
$url = trim( $original_url, " \t\n\r\0\x0B\"'" );
|
536 |
+
|
537 |
+
// If datauri inlining is turned on, do it.
|
538 |
+
$inlined = false;
|
539 |
+
if ( $this->datauris ) {
|
540 |
+
$iurl = $url;
|
541 |
+
if ( false !== strpos( $iurl, '?' ) ) {
|
542 |
+
$iurl = strtok( $iurl, '?' );
|
543 |
+
}
|
544 |
+
|
545 |
+
$ipath = $this->getpath( $iurl );
|
546 |
+
|
547 |
+
$excluded = $this->check_datauri_exclude_list( $ipath );
|
548 |
+
if ( ! $excluded ) {
|
549 |
+
$is_datauri_candidate = $this->is_datauri_candidate( $ipath );
|
550 |
+
if ( $is_datauri_candidate ) {
|
551 |
+
$datauri = $this->build_or_get_datauri_image( $ipath );
|
552 |
+
$base64data = $datauri['base64data'];
|
553 |
+
// Add it to the list for replacement.
|
554 |
+
$imgreplace[ $url_src_matches[1][ $count ] ] = str_replace(
|
555 |
+
$original_url,
|
556 |
+
$datauri['full'],
|
557 |
+
$url_src_matches[1][$count]
|
558 |
+
);
|
559 |
+
$inlined = true;
|
560 |
+
}
|
561 |
+
}
|
562 |
+
}
|
563 |
+
|
564 |
+
/**
|
565 |
+
* Doing CDN URL replacement for every found match (if CDN is
|
566 |
+
* specified). This way we make sure to do it even if
|
567 |
+
* inlining isn't turned on, or if a resource is skipped from
|
568 |
+
* being inlined for whatever reason above.
|
569 |
+
*/
|
570 |
+
if ( ! $inlined && ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) ) {
|
571 |
+
// Just do the "simple" CDN replacement.
|
572 |
+
$replacement_url = $this->url_replace_cdn( $url );
|
573 |
+
$imgreplace[ $url_src_matches[1][ $count ] ] = str_replace(
|
574 |
+
$original_url, $replacement_url, $url_src_matches[1][$count]
|
575 |
+
);
|
576 |
+
}
|
577 |
+
}
|
578 |
+
}
|
579 |
+
|
580 |
+
$code = self::replace_longest_matches_first( $code, $imgreplace );
|
581 |
+
|
582 |
+
// Replace back font-face markers with actual font-face declarations.
|
583 |
+
$code = $this->restore_fontface( $code );
|
584 |
+
|
585 |
+
return $code;
|
586 |
+
}
|
587 |
+
|
588 |
+
// Joins and optimizes CSS.
|
589 |
+
public function minify()
|
590 |
+
{
|
591 |
+
foreach ( $this->css as $group ) {
|
592 |
+
list( $media, $css ) = $group;
|
593 |
+
if ( preg_match( '#^INLINE;#', $css ) ) {
|
594 |
+
// <style>.
|
595 |
+
$css = preg_replace( '#^INLINE;#', '', $css );
|
596 |
+
$css = self::fixurls( ABSPATH . 'index.php', $css ); // ABSPATH already contains a trailing slash.
|
597 |
+
$tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, '' );
|
598 |
+
if ( has_filter( 'autoptimize_css_individual_style' ) && ! empty( $tmpstyle ) ) {
|
599 |
+
$css = $tmpstyle;
|
600 |
+
$this->alreadyminified = true;
|
601 |
+
}
|
602 |
+
} else {
|
603 |
+
// <link>
|
604 |
+
if ( false !== $css && file_exists( $css ) && is_readable( $css ) ) {
|
605 |
+
$cssPath = $css;
|
606 |
+
$css = self::fixurls( $cssPath, file_get_contents( $cssPath ) );
|
607 |
+
$css = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $css );
|
608 |
+
$tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, $cssPath );
|
609 |
+
if ( has_filter( 'autoptimize_css_individual_style' ) && ! empty( $tmpstyle ) ) {
|
610 |
+
$css = $tmpstyle;
|
611 |
+
$this->alreadyminified = true;
|
612 |
+
} elseif ( $this->can_inject_late( $cssPath, $css ) ) {
|
613 |
+
$css = self::build_injectlater_marker( $cssPath, md5( $css ) );
|
614 |
+
}
|
615 |
+
} else {
|
616 |
+
// Couldn't read CSS. Maybe getpath isn't working?
|
617 |
+
$css = '';
|
618 |
+
}
|
619 |
+
}
|
620 |
+
|
621 |
+
foreach ( $media as $elem ) {
|
622 |
+
if ( ! empty( $css ) ) {
|
623 |
+
if ( ! isset( $this->csscode[$elem] ) ) {
|
624 |
+
$this->csscode[$elem] = '';
|
625 |
+
}
|
626 |
+
$this->csscode[$elem] .= "\n/*FILESTART*/" . $css;
|
627 |
+
}
|
628 |
+
}
|
629 |
+
}
|
630 |
+
|
631 |
+
// Check for duplicate code.
|
632 |
+
$md5list = array();
|
633 |
+
$tmpcss = $this->csscode;
|
634 |
+
foreach ( $tmpcss as $media => $code ) {
|
635 |
+
$md5sum = md5( $code );
|
636 |
+
$medianame = $media;
|
637 |
+
foreach ( $md5list as $med => $sum ) {
|
638 |
+
// If same code.
|
639 |
+
if ( $sum === $md5sum ) {
|
640 |
+
// Add the merged code.
|
641 |
+
$medianame = $med . ', ' . $media;
|
642 |
+
$this->csscode[$medianame] = $code;
|
643 |
+
$md5list[$medianame] = $md5list[$med];
|
644 |
+
unset( $this->csscode[$med], $this->csscode[$media], $med5list[$med] );
|
645 |
+
}
|
646 |
+
}
|
647 |
+
$md5list[$medianame] = $md5sum;
|
648 |
+
}
|
649 |
+
unset( $tmpcss );
|
650 |
+
|
651 |
+
// Manage @imports, while is for recursive import management.
|
652 |
+
foreach ( $this->csscode as &$thiscss ) {
|
653 |
+
// Flag to trigger import reconstitution and var to hold external imports.
|
654 |
+
$fiximports = false;
|
655 |
+
$external_imports = '';
|
656 |
+
|
657 |
+
// remove comments to avoid importing commented-out imports.
|
658 |
+
$thiscss_nocomments = preg_replace( '#/\*.*\*/#Us', '', $thiscss );
|
659 |
+
while ( preg_match_all( '#@import +(?:url)?(?:(?:\((["\']?)(?:[^"\')]+)\1\)|(["\'])(?:[^"\']+)\2)(?:[^,;"\']+(?:,[^,;"\']+)*)?)(?:;)#mi', $thiscss_nocomments, $matches ) ) {
|
660 |
+
foreach ( $matches[0] as $import ) {
|
661 |
+
if ( $this->isremovable( $import, $this->cssremovables ) ) {
|
662 |
+
$thiscss = str_replace( $import, '', $thiscss );
|
663 |
+
$import_ok = true;
|
664 |
+
} else {
|
665 |
+
$url = trim( preg_replace( '#^.*((?:https?:|ftp:)?//.*\.css).*$#', '$1', trim( $import ) ), " \t\n\r\0\x0B\"'" );
|
666 |
+
$path = $this->getpath( $url );
|
667 |
+
$import_ok = false;
|
668 |
+
if ( file_exists( $path ) && is_readable( $path ) ) {
|
669 |
+
$code = addcslashes( self::fixurls( $path, file_get_contents( $path ) ), "\\" );
|
670 |
+
$code = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $code );
|
671 |
+
$tmpstyle = apply_filters( 'autoptimize_css_individual_style', $code, '' );
|
672 |
+
if ( has_filter( 'autoptimize_css_individual_style' ) && ! empty( $tmpstyle ) ) {
|
673 |
+
$code = $tmpstyle;
|
674 |
+
$this->alreadyminified = true;
|
675 |
+
} elseif ( $this->can_inject_late( $path, $code ) ) {
|
676 |
+
$code = self::build_injectlater_marker( $path, md5( $code ) );
|
677 |
+
}
|
678 |
+
|
679 |
+
if ( ! empty( $code ) ) {
|
680 |
+
$tmp_thiscss = preg_replace( '#(/\*FILESTART\*/.*)' . preg_quote( $import, '#' ) . '#Us', '/*FILESTART2*/' . $code . '$1', $thiscss );
|
681 |
+
if ( ! empty( $tmp_thiscss ) ) {
|
682 |
+
$thiscss = $tmp_thiscss;
|
683 |
+
$import_ok = true;
|
684 |
+
unset( $tmp_thiscss );
|
685 |
+
}
|
686 |
+
}
|
687 |
+
unset( $code );
|
688 |
+
}
|
689 |
+
}
|
690 |
+
if ( ! $import_ok ) {
|
691 |
+
// External imports and general fall-back.
|
692 |
+
$external_imports .= $import;
|
693 |
+
|
694 |
+
$thiscss = str_replace( $import, '', $thiscss );
|
695 |
+
$fiximports = true;
|
696 |
+
}
|
697 |
+
}
|
698 |
+
$thiscss = preg_replace( '#/\*FILESTART\*/#', '', $thiscss );
|
699 |
+
$thiscss = preg_replace( '#/\*FILESTART2\*/#', '/*FILESTART*/', $thiscss );
|
700 |
+
|
701 |
+
// and update $thiscss_nocomments before going into next iteration in while loop.
|
702 |
+
$thiscss_nocomments = preg_replace( '#/\*.*\*/#Us', '', $thiscss );
|
703 |
+
}
|
704 |
+
unset( $thiscss_nocomments );
|
705 |
+
|
706 |
+
// Add external imports to top of aggregated CSS.
|
707 |
+
if ( $fiximports ) {
|
708 |
+
$thiscss = $external_imports . $thiscss;
|
709 |
+
}
|
710 |
+
}
|
711 |
+
unset( $thiscss );
|
712 |
+
|
713 |
+
// $this->csscode has all the uncompressed code now.
|
714 |
+
foreach ( $this->csscode as &$code ) {
|
715 |
+
// Check for already-minified code.
|
716 |
+
$hash = md5( $code );
|
717 |
+
do_action( 'autoptimize_action_css_hash', $hash );
|
718 |
+
$ccheck = new autoptimizeCache( $hash, 'css' );
|
719 |
+
if ( $ccheck->check() ) {
|
720 |
+
$code = $ccheck->retrieve();
|
721 |
+
$this->hashmap[md5( $code )] = $hash;
|
722 |
+
continue;
|
723 |
+
}
|
724 |
+
unset( $ccheck );
|
725 |
+
|
726 |
+
// Rewrite and/or inline referenced assets.
|
727 |
+
$code = $this->rewrite_assets( $code );
|
728 |
+
|
729 |
+
// Minify.
|
730 |
+
$code = $this->run_minifier_on( $code );
|
731 |
+
|
732 |
+
// Bring back INJECTLATER stuff.
|
733 |
+
$code = $this->inject_minified( $code );
|
734 |
+
|
735 |
+
// Filter results.
|
736 |
+
$tmp_code = apply_filters( 'autoptimize_css_after_minify', $code );
|
737 |
+
if ( ! empty( $tmp_code ) ) {
|
738 |
+
$code = $tmp_code;
|
739 |
+
unset( $tmp_code );
|
740 |
+
}
|
741 |
+
|
742 |
+
$this->hashmap[md5( $code )] = $hash;
|
743 |
+
}
|
744 |
+
|
745 |
+
unset( $code );
|
746 |
+
return true;
|
747 |
+
}
|
748 |
+
|
749 |
+
public function run_minifier_on( $code )
|
750 |
+
{
|
751 |
+
if ( ! $this->alreadyminified ) {
|
752 |
+
$do_minify = apply_filters( 'autoptimize_css_do_minify', true );
|
753 |
+
|
754 |
+
if ( $do_minify ) {
|
755 |
+
$cssmin = new autoptimizeCSSmin();
|
756 |
+
$tmp_code = trim( $cssmin->run( $code ) );
|
757 |
+
|
758 |
+
if ( ! empty( $tmp_code ) ) {
|
759 |
+
$code = $tmp_code;
|
760 |
+
unset( $tmp_code );
|
761 |
+
}
|
762 |
+
}
|
763 |
+
}
|
764 |
+
|
765 |
+
return $code;
|
766 |
+
}
|
767 |
+
|
768 |
+
// Caches the CSS in uncompressed, deflated and gzipped form.
|
769 |
+
public function cache()
|
770 |
+
{
|
771 |
+
// CSS cache.
|
772 |
+
foreach ( $this->csscode as $media => $code ) {
|
773 |
+
$md5 = $this->hashmap[md5( $code )];
|
774 |
+
$cache = new autoptimizeCache( $md5, 'css' );
|
775 |
+
if ( ! $cache->check() ) {
|
776 |
+
// Cache our code.
|
777 |
+
$cache->cache( $code, 'text/css' );
|
778 |
+
}
|
779 |
+
$this->url[$media] = AUTOPTIMIZE_CACHE_URL . $cache->getname();
|
780 |
+
}
|
781 |
+
}
|
782 |
+
|
783 |
+
// Returns the content.
|
784 |
+
public function getcontent()
|
785 |
+
{
|
786 |
+
// restore comments.
|
787 |
+
$this->content = $this->restore_comments( $this->content );
|
788 |
+
|
789 |
+
// restore IE hacks.
|
790 |
+
$this->content = $this->restore_iehacks( $this->content );
|
791 |
+
|
792 |
+
// restore (no)script.
|
793 |
+
$this->content = $this->restore_marked_content( 'SCRIPT', $this->content );
|
794 |
+
|
795 |
+
// Restore noptimize.
|
796 |
+
$this->content = $this->restore_noptimize( $this->content );
|
797 |
+
|
798 |
+
// Restore the full content.
|
799 |
+
if ( ! empty( $this->restofcontent ) ) {
|
800 |
+
$this->content .= $this->restofcontent;
|
801 |
+
$this->restofcontent = '';
|
802 |
+
}
|
803 |
+
|
804 |
+
// Inject the new stylesheets.
|
805 |
+
$replaceTag = array( '<title', 'before' );
|
806 |
+
$replaceTag = apply_filters( 'autoptimize_filter_css_replacetag', $replaceTag, $this->content );
|
807 |
+
|
808 |
+
if ( $this->inline ) {
|
809 |
+
foreach ( $this->csscode as $media => $code ) {
|
810 |
+
$this->inject_in_html( '<style type="text/css" media="' . $media . '">' . $code . '</style>', $replaceTag );
|
811 |
+
}
|
812 |
+
} else {
|
813 |
+
if ( $this->defer ) {
|
814 |
+
$preloadCssBlock = '';
|
815 |
+
$noScriptCssBlock = "<noscript id=\"aonoscrcss\">";
|
816 |
+
|
817 |
+
$defer_inline_code = $this->defer_inline;
|
818 |
+
if ( ! empty( $defer_inline_code ) ) {
|
819 |
+
if ( apply_filters( 'autoptimize_filter_css_critcss_minify', true ) ) {
|
820 |
+
$iCssHash = md5( $defer_inline_code );
|
821 |
+
$iCssCache = new autoptimizeCache( $iCssHash, 'css' );
|
822 |
+
if ( $iCssCache->check() ) {
|
823 |
+
// we have the optimized inline CSS in cache.
|
824 |
+
$defer_inline_code = $iCssCache->retrieve();
|
825 |
+
} else {
|
826 |
+
$cssmin = new autoptimizeCSSmin();
|
827 |
+
$tmp_code = trim( $cssmin->run( $defer_inline_code ) );
|
828 |
+
|
829 |
+
if ( ! empty( $tmp_code ) ) {
|
830 |
+
$defer_inline_code = $tmp_code;
|
831 |
+
$iCssCache->cache( $defer_inline_code, 'text/css' );
|
832 |
+
unset( $tmp_code );
|
833 |
+
}
|
834 |
+
}
|
835 |
+
}
|
836 |
+
$code_out = '<style type="text/css" id="aoatfcss" media="all">' . $defer_inline_code . '</style>';
|
837 |
+
$this->inject_in_html( $code_out, $replaceTag );
|
838 |
+
}
|
839 |
+
}
|
840 |
+
|
841 |
+
foreach ( $this->url as $media => $url ) {
|
842 |
+
$url = $this->url_replace_cdn( $url );
|
843 |
+
|
844 |
+
// Add the stylesheet either deferred (import at bottom) or normal links in head.
|
845 |
+
if ( $this->defer ) {
|
846 |
+
$preloadOnLoad = autoptimizeConfig::get_ao_css_preload_onload();
|
847 |
+
|
848 |
+
$preloadCssBlock .= '<link rel="preload" as="style" media="' . $media . '" href="' . $url . '" onload="' . $preloadOnLoad . '" />';
|
849 |
+
$noScriptCssBlock .= '<link type="text/css" media="' . $media . '" href="' . $url . '" rel="stylesheet" />';
|
850 |
+
} else {
|
851 |
+
// $this->inject_in_html('<link type="text/css" media="' . $media . '" href="' . $url . '" rel="stylesheet" />', $replaceTag);
|
852 |
+
if ( strlen( $this->csscode[$media] ) > $this->cssinlinesize ) {
|
853 |
+
$this->inject_in_html( '<link type="text/css" media="' . $media . '" href="' . $url . '" rel="stylesheet" />', $replaceTag );
|
854 |
+
} elseif ( strlen( $this->csscode[$media] ) > 0 ) {
|
855 |
+
$this->inject_in_html( '<style type="text/css" media="' . $media . '">' . $this->csscode[$media] . '</style>', $replaceTag );
|
856 |
+
}
|
857 |
+
}
|
858 |
+
}
|
859 |
+
|
860 |
+
if ( $this->defer ) {
|
861 |
+
$preload_polyfill = autoptimizeConfig::get_ao_css_preload_polyfill();
|
862 |
+
$noScriptCssBlock .= '</noscript>';
|
863 |
+
$this->inject_in_html( $preloadCssBlock . $noScriptCssBlock, $replaceTag );
|
864 |
+
|
865 |
+
// Adds preload polyfill at end of body tag.
|
866 |
+
$this->inject_in_html(
|
867 |
+
apply_filters( 'autoptimize_css_preload_polyfill', $preload_polyfill ),
|
868 |
+
array( '</body>', 'before' )
|
869 |
+
);
|
870 |
+
}
|
871 |
+
}
|
872 |
+
|
873 |
+
// Return the modified stylesheet.
|
874 |
+
return $this->content;
|
875 |
+
}
|
876 |
+
|
877 |
+
static function fixurls( $file, $code )
|
878 |
+
{
|
879 |
+
// Switch all imports to the url() syntax.
|
880 |
+
$code = preg_replace( '#@import ("|\')(.+?)\.css.*?("|\')#', '@import url("${2}.css")', $code );
|
881 |
+
|
882 |
+
if ( preg_match_all( self::ASSETS_REGEX, $code, $matches ) ) {
|
883 |
+
$file = str_replace( WP_ROOT_DIR, '/', $file );
|
884 |
+
/**
|
885 |
+
* rollback as per https://github.com/futtta/autoptimize/issues/94
|
886 |
+
* $file = str_replace( AUTOPTIMIZE_WP_CONTENT_NAME, '', $file );
|
887 |
+
*/
|
888 |
+
$dir = dirname( $file ); // Like /themes/expound/css.
|
889 |
+
|
890 |
+
/**
|
891 |
+
* $dir should not contain backslashes, since it's used to replace
|
892 |
+
* urls, but it can contain them when running on Windows because
|
893 |
+
* fixurls() is sometimes called with `ABSPATH . 'index.php'`
|
894 |
+
*/
|
895 |
+
$dir = str_replace( '\\', '/', $dir );
|
896 |
+
unset( $file ); // not used below at all.
|
897 |
+
|
898 |
+
$replace = array();
|
899 |
+
foreach ( $matches[1] as $k => $url ) {
|
900 |
+
// Remove quotes.
|
901 |
+
$url = trim( $url, " \t\n\r\0\x0B\"'" );
|
902 |
+
$noQurl = trim( $url, "\"'" );
|
903 |
+
if ( $url !== $noQurl ) {
|
904 |
+
$removedQuotes = true;
|
905 |
+
} else {
|
906 |
+
$removedQuotes = false;
|
907 |
+
}
|
908 |
+
|
909 |
+
if ( '' === $noQurl ) {
|
910 |
+
continue;
|
911 |
+
}
|
912 |
+
|
913 |
+
$url = $noQurl;
|
914 |
+
if ( '/' === $url{0} || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) {
|
915 |
+
// URL is protocol-relative, host-relative or something we don't touch.
|
916 |
+
continue;
|
917 |
+
} else {
|
918 |
+
// Relative URL.
|
919 |
+
/**
|
920 |
+
* rollback as per https://github.com/futtta/autoptimize/issues/94
|
921 |
+
* $newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_CONTENT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) );
|
922 |
+
*/
|
923 |
+
$newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_ROOT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) );
|
924 |
+
$newurl = apply_filters( 'autoptimize_filter_css_fixurl_newurl', $newurl );
|
925 |
+
|
926 |
+
/**
|
927 |
+
* Hash the url + whatever was behind potentially for replacement
|
928 |
+
* We must do this, or different css classes referencing the same bg image (but
|
929 |
+
* different parts of it, say, in sprites and such) loose their stuff...
|
930 |
+
*/
|
931 |
+
$hash = md5( $url . $matches[2][$k] );
|
932 |
+
$code = str_replace( $matches[0][$k], $hash, $code );
|
933 |
+
|
934 |
+
if ( $removedQuotes ) {
|
935 |
+
$replace[$hash] = "url('" . $newurl . "')" . $matches[2][$k];
|
936 |
+
} else {
|
937 |
+
$replace[$hash] = 'url(' . $newurl . ')' . $matches[2][$k];
|
938 |
+
}
|
939 |
+
}
|
940 |
+
}
|
941 |
+
|
942 |
+
$code = self::replace_longest_matches_first( $code, $replace );
|
943 |
+
}
|
944 |
+
|
945 |
+
return $code;
|
946 |
+
}
|
947 |
+
|
948 |
+
private function ismovable( $tag )
|
949 |
+
{
|
950 |
+
if ( ! $this->aggregate ) {
|
951 |
+
return false;
|
952 |
+
}
|
953 |
+
|
954 |
+
if ( ! empty( $this->whitelist ) ) {
|
955 |
+
foreach ( $this->whitelist as $match ) {
|
956 |
+
if ( false !== strpos( $tag, $match ) ) {
|
957 |
+
return true;
|
958 |
+
}
|
959 |
+
}
|
960 |
+
// no match with whitelist.
|
961 |
+
return false;
|
962 |
+
} else {
|
963 |
+
if ( is_array( $this->dontmove ) && ! empty( $this->dontmove ) ) {
|
964 |
+
foreach ( $this->dontmove as $match ) {
|
965 |
+
if ( false !== strpos( $tag, $match ) ) {
|
966 |
+
// Matched something.
|
967 |
+
return false;
|
968 |
+
}
|
969 |
+
}
|
970 |
+
}
|
971 |
+
|
972 |
+
// If we're here it's safe to move.
|
973 |
+
return true;
|
974 |
+
}
|
975 |
+
}
|
976 |
+
|
977 |
+
private function can_inject_late( $cssPath, $css )
|
978 |
+
{
|
979 |
+
$consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false, $cssPath );
|
980 |
+
if ( true !== $this->inject_min_late ) {
|
981 |
+
// late-inject turned off.
|
982 |
+
return false;
|
983 |
+
} elseif ( ( false === strpos( $cssPath, 'min.css' ) ) && ( str_replace( $consider_minified_array, '', $cssPath ) === $cssPath ) ) {
|
984 |
+
// file not minified based on filename & filter.
|
985 |
+
return false;
|
986 |
+
} elseif ( false !== strpos( $css, '@import' ) ) {
|
987 |
+
// can't late-inject files with imports as those need to be aggregated.
|
988 |
+
return false;
|
989 |
+
} elseif ( ( false !== strpos( $css, '@font-face' ) ) && ( apply_filters( 'autoptimize_filter_css_fonts_cdn', false ) === true ) && ( ! empty( $this->cdn_url ) ) ) {
|
990 |
+
// don't late-inject CSS with font-src's if fonts are set to be CDN'ed.
|
991 |
+
return false;
|
992 |
+
} elseif ( ( ( $this->datauris == true ) || ( ! empty( $this->cdn_url ) ) ) && preg_match( '#background[^;}]*url\(#Ui', $css ) ) {
|
993 |
+
// don't late-inject CSS with images if CDN is set OR if image inlining is on.
|
994 |
+
return false;
|
995 |
+
} else {
|
996 |
+
// phew, all is safe, we can late-inject.
|
997 |
+
return true;
|
998 |
+
}
|
999 |
+
}
|
1000 |
+
|
1001 |
+
/**
|
1002 |
+
* Minifies (and cdn-replaces) a single local css file
|
1003 |
+
* and returns its (cached) url.
|
1004 |
+
*
|
1005 |
+
* @param string $filepath Filepath.
|
1006 |
+
* @param bool $cache_miss Optional. Force a cache miss. Default false.
|
1007 |
+
*
|
1008 |
+
* @return bool|string Url pointing to the minified css file or false.
|
1009 |
+
*/
|
1010 |
+
public function minify_single( $filepath, $cache_miss = false )
|
1011 |
+
{
|
1012 |
+
$contents = $this->prepare_minify_single( $filepath );
|
1013 |
+
|
1014 |
+
if ( empty( $contents ) ) {
|
1015 |
+
return false;
|
1016 |
+
}
|
1017 |
+
|
1018 |
+
// Check cache.
|
1019 |
+
$hash = 'single_' . md5( $contents );
|
1020 |
+
$cache = new autoptimizeCache( $hash, 'css' );
|
1021 |
+
|
1022 |
+
// If not in cache already, minify...
|
1023 |
+
if ( ! $cache->check() || $cache_miss ) {
|
1024 |
+
// Fixurls...
|
1025 |
+
$contents = self::fixurls( $filepath, $contents );
|
1026 |
+
// CDN-replace any referenced assets if needed...
|
1027 |
+
$contents = $this->replace_urls( $contents );
|
1028 |
+
// Now minify...
|
1029 |
+
$cssmin = new autoptimizeCSSmin();
|
1030 |
+
$contents = trim( $cssmin->run( $contents ) );
|
1031 |
+
// Store in cache.
|
1032 |
+
$cache->cache( $contents, 'text/css' );
|
1033 |
+
}
|
1034 |
+
|
1035 |
+
$url = $this->build_minify_single_url( $cache );
|
1036 |
+
|
1037 |
+
return $url;
|
1038 |
+
}
|
1039 |
+
|
1040 |
+
/**
|
1041 |
+
* Returns whether we're doing aggregation or not.
|
1042 |
+
*
|
1043 |
+
* @return bool
|
1044 |
+
*/
|
1045 |
+
public function aggregating()
|
1046 |
+
{
|
1047 |
+
return $this->aggregate;
|
1048 |
+
}
|
1049 |
+
|
1050 |
+
public function getOptions()
|
1051 |
+
{
|
1052 |
+
return $this->options;
|
1053 |
+
}
|
1054 |
+
|
1055 |
+
public function replaceOptions( $options )
|
1056 |
+
{
|
1057 |
+
$this->options = $options;
|
1058 |
+
}
|
1059 |
+
|
1060 |
+
public function setOption( $name, $value )
|
1061 |
+
{
|
1062 |
+
$this->options[$name] = $value;
|
1063 |
+
$this->$name = $value;
|
1064 |
+
}
|
1065 |
+
|
1066 |
+
public function getOption( $name )
|
1067 |
+
{
|
1068 |
+
return $this->options[$name];
|
1069 |
+
}
|
1070 |
+
}
|
classes/autoptimizeToolbar.php
CHANGED
@@ -1,34 +1,42 @@
|
|
1 |
<?php
|
2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
class autoptimizeToolbar
|
4 |
{
|
5 |
public function __construct()
|
6 |
{
|
7 |
-
// If Cache is not available we don't add the
|
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 |
-
|
|
|
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 |
-
|
|
|
32 |
add_action( 'admin_bar_menu', array( $this, 'add_toolbar' ), 100 );
|
33 |
}
|
34 |
}
|
@@ -38,97 +46,105 @@ class autoptimizeToolbar
|
|
38 |
{
|
39 |
global $wp_admin_bar;
|
40 |
|
41 |
-
// Retrieve the Autoptimize Cache Stats information
|
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];
|
49 |
-
// Retrieve the current Total Size of the cache
|
50 |
$bytes = $stats[1];
|
|
|
51 |
|
52 |
-
|
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 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
64 |
|
65 |
// Create or add new items into the Admin Toolbar.
|
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">' . __(
|
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>' . __(
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
'parent' => 'autoptimize'
|
91 |
));
|
92 |
|
93 |
-
// Delete Cache node
|
94 |
$wp_admin_bar->add_node( array(
|
95 |
'id' => 'autoptimize-delete-cache',
|
96 |
-
'title' => __(
|
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(),
|
117 |
-
|
118 |
-
|
119 |
-
|
|
|
|
|
|
|
120 |
wp_localize_script( 'autoptimize-toolbar', 'autoptimize_ajax_object', array(
|
121 |
-
'ajaxurl'
|
122 |
-
'error_msg'
|
123 |
'dismiss_msg' => __( 'Dismiss this notice.' ),
|
124 |
-
'nonce'
|
125 |
) );
|
126 |
}
|
127 |
|
128 |
-
public function format_filesize($bytes, $decimals = 2)
|
129 |
{
|
130 |
$units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' );
|
131 |
-
|
132 |
-
|
|
|
|
|
133 |
}
|
134 |
}
|
1 |
<?php
|
2 |
+
/**
|
3 |
+
* Handles toolbar-related stuff.
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
class autoptimizeToolbar
|
11 |
{
|
12 |
public function __construct()
|
13 |
{
|
14 |
+
// If Cache is not available we don't add the toolbar.
|
15 |
+
if ( ! autoptimizeCache::cacheavail() ) {
|
16 |
return;
|
17 |
}
|
18 |
+
|
19 |
// Load admin toolbar feature once WordPress, all plugins, and the theme are fully loaded and instantiated.
|
20 |
add_action( 'wp_loaded', array( $this, 'load_toolbar' ) );
|
21 |
}
|
22 |
|
23 |
public function load_toolbar()
|
24 |
{
|
25 |
+
// Check permissions and that toolbar is not hidden via filter.
|
26 |
if ( current_user_can( 'manage_options' ) && apply_filters( 'autoptimize_filter_toolbar_show', true ) ) {
|
27 |
+
|
28 |
+
// Create a handler for the AJAX toolbar requests.
|
29 |
add_action( 'wp_ajax_autoptimize_delete_cache', array( $this, 'delete_cache' ) );
|
30 |
|
31 |
+
// Load custom styles, scripts and menu only when needed.
|
32 |
if ( is_admin_bar_showing() ) {
|
33 |
if ( is_admin() ) {
|
|
|
34 |
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
35 |
} else {
|
|
|
36 |
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
37 |
}
|
38 |
+
|
39 |
+
// Add the Autoptimize Toolbar to the Admin bar.
|
40 |
add_action( 'admin_bar_menu', array( $this, 'add_toolbar' ), 100 );
|
41 |
}
|
42 |
}
|
46 |
{
|
47 |
global $wp_admin_bar;
|
48 |
|
49 |
+
// Retrieve the Autoptimize Cache Stats information.
|
50 |
$stats = autoptimizeCache::stats();
|
51 |
|
52 |
+
// Set the Max Size recommended for cache files.
|
53 |
$max_size = apply_filters( 'autoptimize_filter_cachecheck_maxsize', 512 * 1024 * 1024 );
|
54 |
|
55 |
+
// Retrieve the current Total Files in cache.
|
56 |
$files = $stats[0];
|
57 |
+
// Retrieve the current Total Size of the cache.
|
58 |
$bytes = $stats[1];
|
59 |
+
$size = $this->format_filesize( $bytes );
|
60 |
|
61 |
+
// Calculate the percentage of cache used.
|
|
|
|
|
62 |
$percentage = ceil( $bytes / $max_size * 100 );
|
63 |
if ( $percentage > 100 ) {
|
64 |
$percentage = 100;
|
65 |
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* We define the type of color indicator for the current state of cache size:
|
69 |
+
* - "green" if the size is less than 80% of the total recommended.
|
70 |
+
* - "orange" if over 80%.
|
71 |
+
* - "red" if over 100%.
|
72 |
+
*/
|
73 |
+
$color = ( 100 == $percentage ) ? 'red' : ( ( $percentage > 80 ) ? 'orange' : 'green' );
|
74 |
|
75 |
// Create or add new items into the Admin Toolbar.
|
76 |
+
// Main "Autoptimize" node.
|
77 |
$wp_admin_bar->add_node( array(
|
78 |
'id' => 'autoptimize',
|
79 |
+
'title' => '<span class="ab-icon"></span><span class="ab-label">' . __( 'Autoptimize', 'autoptimize' ) . '</span>',
|
80 |
'href' => admin_url( 'options-general.php?page=autoptimize' ),
|
81 |
+
'meta' => array( 'class' => 'bullet-' . $color ),
|
82 |
));
|
83 |
|
84 |
+
// "Cache Info" node.
|
85 |
$wp_admin_bar->add_node( array(
|
86 |
'id' => 'autoptimize-cache-info',
|
87 |
+
'title' => '<p>' . __( 'Cache Info', 'autoptimize' ) . '</p>' .
|
88 |
+
'<div class="autoptimize-radial-bar" percentage="' . $percentage . '">' .
|
89 |
+
'<div class="autoptimize-circle">' .
|
90 |
+
'<div class="mask full"><div class="fill bg-' . $color . '"></div></div>' .
|
91 |
+
'<div class="mask half"><div class="fill bg-' . $color . '"></div></div>' .
|
92 |
+
'<div class="shadow"></div>' .
|
93 |
+
'</div>' .
|
94 |
+
'<div class="inset"><div class="percentage"><div class="numbers ' . $color . '">' . $percentage . '%</div></div></div>' .
|
95 |
+
'</div>' .
|
96 |
+
'<table>' .
|
97 |
+
'<tr><td>' . __( 'Size', 'autoptimize' ) . ':</td><td class="size ' . $color . '">' . $size . '</td></tr>' .
|
98 |
+
'<tr><td>' . __( 'Files', 'autoptimize' ) . ':</td><td class="files white">' . $files . '</td></tr>' .
|
99 |
+
'</table>',
|
100 |
+
'parent' => 'autoptimize',
|
101 |
));
|
102 |
|
103 |
+
// "Delete Cache" node.
|
104 |
$wp_admin_bar->add_node( array(
|
105 |
'id' => 'autoptimize-delete-cache',
|
106 |
+
'title' => __( 'Delete Cache', 'autoptimize' ),
|
107 |
+
'parent' => 'autoptimize',
|
108 |
));
|
109 |
}
|
110 |
|
111 |
public function delete_cache()
|
112 |
{
|
113 |
check_ajax_referer( 'ao_delcache_nonce', 'nonce' );
|
114 |
+
|
115 |
$result = false;
|
116 |
+
if ( current_user_can( 'manage_options' ) ) {
|
117 |
+
// We call the function for cleaning the Autoptimize cache.
|
|
|
118 |
$result = autoptimizeCache::clearall();
|
119 |
}
|
120 |
+
|
121 |
wp_send_json( $result );
|
122 |
}
|
123 |
|
124 |
public function enqueue_scripts()
|
125 |
{
|
126 |
+
// Autoptimize Toolbar Styles.
|
127 |
+
wp_enqueue_style( 'autoptimize-toolbar', plugins_url( '/static/toolbar.css', __FILE__ ), array(), AUTOPTIMIZE_PLUGIN_VERSION, 'all' );
|
128 |
+
|
129 |
+
// Autoptimize Toolbar Javascript.
|
130 |
+
wp_enqueue_script( 'autoptimize-toolbar', plugins_url( '/static/toolbar.js', __FILE__ ), array( 'jquery' ), AUTOPTIMIZE_PLUGIN_VERSION, true );
|
131 |
+
|
132 |
+
// Localizes a registered script with data for a JavaScript variable.
|
133 |
+
// Needed for the AJAX to work properly on the frontend.
|
134 |
wp_localize_script( 'autoptimize-toolbar', 'autoptimize_ajax_object', array(
|
135 |
+
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
136 |
+
'error_msg' => sprintf( __( 'Your Autoptimize cache might not have been purged successfully, please check on the <a href=%s>Autoptimize settings page</a>.', 'autoptimize' ), admin_url( 'options-general.php?page=autoptimize' ) . ' style="white-space:nowrap;"' ),
|
137 |
'dismiss_msg' => __( 'Dismiss this notice.' ),
|
138 |
+
'nonce' => wp_create_nonce( 'ao_delcache_nonce' ),
|
139 |
) );
|
140 |
}
|
141 |
|
142 |
+
public function format_filesize( $bytes, $decimals = 2 )
|
143 |
{
|
144 |
$units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' );
|
145 |
+
|
146 |
+
for ( $i = 0; ( $bytes / 1024) > 0.9; $i++, $bytes /= 1024 ) {} // @codingStandardsIgnoreLine
|
147 |
+
|
148 |
+
return sprintf( "%1.{$decimals}f %s", round( $bytes, $decimals ), $units[ $i ] );
|
149 |
}
|
150 |
}
|
classes/autoptimizeUtils.php
ADDED
@@ -0,0 +1,424 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* General helpers.
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
+
class autoptimizeUtils
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Returns true when mbstring is available.
|
14 |
+
*
|
15 |
+
* @param bool|null $override Allows overriding the decision.
|
16 |
+
*
|
17 |
+
* @return bool
|
18 |
+
*/
|
19 |
+
public static function mbstring_available( $override = null )
|
20 |
+
{
|
21 |
+
static $available = null;
|
22 |
+
|
23 |
+
if ( null === $available ) {
|
24 |
+
$available = \extension_loaded( 'mbstring' );
|
25 |
+
}
|
26 |
+
|
27 |
+
if ( null !== $override ) {
|
28 |
+
$available = $override;
|
29 |
+
}
|
30 |
+
|
31 |
+
return $available;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Returns true when iconv is available.
|
36 |
+
*
|
37 |
+
* @param bool|null $override Allows overriding the decision.
|
38 |
+
*
|
39 |
+
* @return bool
|
40 |
+
*/
|
41 |
+
public static function iconv_available( $override = null )
|
42 |
+
{
|
43 |
+
static $available = null;
|
44 |
+
|
45 |
+
if ( null === $available ) {
|
46 |
+
$available = \extension_loaded( 'iconv' );
|
47 |
+
}
|
48 |
+
|
49 |
+
if ( null !== $override ) {
|
50 |
+
$available = $override;
|
51 |
+
}
|
52 |
+
|
53 |
+
return $available;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Multibyte-capable strpos() if support is available on the server.
|
58 |
+
* If not, it falls back to using \strpos().
|
59 |
+
*
|
60 |
+
* @param string $haystack Haystack.
|
61 |
+
* @param string $needle Needle.
|
62 |
+
* @param int $offset Offset.
|
63 |
+
* @param string|null $encoding Encoding. Default null.
|
64 |
+
*
|
65 |
+
* @return int|false
|
66 |
+
*/
|
67 |
+
public static function strpos( $haystack, $needle, $offset = 0, $encoding = null )
|
68 |
+
{
|
69 |
+
if ( self::mbstring_available() ) {
|
70 |
+
return ( null === $encoding ) ? \mb_strpos( $haystack, $needle, $offset ) : \mb_strlen( $haystack, $needle, $offset, $encoding );
|
71 |
+
} elseif ( self::iconv_available() ) {
|
72 |
+
return ( null === $encoding ) ? \iconv_strpos( $haystack, $needle, $offset ) : \iconv_strpos( $haystack, $needle, $offset, $encoding );
|
73 |
+
} else {
|
74 |
+
return \strpos( $haystack, $needle, $offset );
|
75 |
+
}
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Attempts to return the number of characters in the given $string if
|
80 |
+
* mbstring or iconv is available. Returns the number of bytes
|
81 |
+
* (instead of characters) as fallback.
|
82 |
+
*
|
83 |
+
* @param string $string String.
|
84 |
+
* @param string|null $encoding Encoding.
|
85 |
+
*
|
86 |
+
* @return int Number of charcters or bytes in given $string
|
87 |
+
* (characters if/when supported, bytes otherwise).
|
88 |
+
*/
|
89 |
+
public static function strlen( $string, $encoding = null )
|
90 |
+
{
|
91 |
+
if ( self::mbstring_available() ) {
|
92 |
+
return ( null === $encoding ) ? \mb_strlen( $string ) : \mb_strlen( $string, $encoding );
|
93 |
+
} elseif ( self::iconv_available() ) {
|
94 |
+
return ( null === $encoding ) ? @iconv_strlen( $string ) : @iconv_strlen( $string, $encoding );
|
95 |
+
} else {
|
96 |
+
return \strlen( $string );
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Our wrapper around implementations of \substr_replace()
|
102 |
+
* that attempts to not break things horribly if at all possible.
|
103 |
+
* Uses mbstring and/or iconv if available, before falling back to regular
|
104 |
+
* substr_replace() (which works just fine in the majority of cases).
|
105 |
+
*
|
106 |
+
* @param string $string String.
|
107 |
+
* @param string $replacement Replacement.
|
108 |
+
* @param int $start Start offset.
|
109 |
+
* @param int|null $length Length.
|
110 |
+
* @param string|null $encoding Encoding.
|
111 |
+
*
|
112 |
+
* @return string
|
113 |
+
*/
|
114 |
+
public static function substr_replace( $string, $replacement, $start, $length = null, $encoding = null )
|
115 |
+
{
|
116 |
+
if ( self::mbstring_available() ) {
|
117 |
+
$strlen = self::strlen( $string, $encoding );
|
118 |
+
|
119 |
+
if ( $start < 0 ) {
|
120 |
+
if ( -$start < $strlen ) {
|
121 |
+
$start = $strlen + $start;
|
122 |
+
} else {
|
123 |
+
$start = 0;
|
124 |
+
}
|
125 |
+
} elseif ( $start > $strlen ) {
|
126 |
+
$start = $strlen;
|
127 |
+
}
|
128 |
+
|
129 |
+
if ( null === $length || '' === $length ) {
|
130 |
+
$start2 = $strlen;
|
131 |
+
} elseif ( $length < 0 ) {
|
132 |
+
$start2 = $strlen + $length;
|
133 |
+
if ( $start2 < $start ) {
|
134 |
+
$start2 = $start;
|
135 |
+
}
|
136 |
+
} else {
|
137 |
+
$start2 = $start + $length;
|
138 |
+
}
|
139 |
+
|
140 |
+
if ( null === $encoding ) {
|
141 |
+
$leader = $start ? \mb_substr( $string, 0, $start ) : '';
|
142 |
+
$trailer = ( $start2 < $strlen ) ? \mb_substr( $string, $start2, null ) : '';
|
143 |
+
} else {
|
144 |
+
$leader = $start ? \mb_substr( $string, 0, $start, $encoding ) : '';
|
145 |
+
$trailer = ( $start2 < $strlen ) ? \mb_substr( $string, $start2, null, $encoding ) : '';
|
146 |
+
}
|
147 |
+
|
148 |
+
return "{$leader}{$replacement}{$trailer}";
|
149 |
+
} elseif ( self::iconv_available() ) {
|
150 |
+
$strlen = self::strlen( $string, $encoding );
|
151 |
+
|
152 |
+
if ( $start < 0 ) {
|
153 |
+
$start = \max( 0, $strlen + $start );
|
154 |
+
$start = $strlen + $start;
|
155 |
+
if ( $start < 0 ) {
|
156 |
+
$start = 0;
|
157 |
+
}
|
158 |
+
} elseif ( $start > $strlen ) {
|
159 |
+
$start = $strlen;
|
160 |
+
}
|
161 |
+
|
162 |
+
if ( $length < 0 ) {
|
163 |
+
$length = \max( 0, $strlen - $start + $length );
|
164 |
+
} elseif ( null === $length || ( $length > $strlen ) ) {
|
165 |
+
$length = $strlen;
|
166 |
+
}
|
167 |
+
|
168 |
+
if ( ( $start + $length ) > $strlen ) {
|
169 |
+
$length = $strlen - $start;
|
170 |
+
}
|
171 |
+
|
172 |
+
if ( null === $encoding ) {
|
173 |
+
return self::iconv_substr( $string, 0, $start ) . $replacement . self::iconv_substr( $string, $start + $length, $strlen - $start - $length );
|
174 |
+
}
|
175 |
+
|
176 |
+
return self::iconv_substr( $string, 0, $start, $encoding ) . $replacement . self::iconv_substr( $string, $start + $length, $strlen - $start - $length, $encoding );
|
177 |
+
}
|
178 |
+
|
179 |
+
return ( null === $length ) ? \substr_replace( $string, $replacement, $start ) : \substr_replace( $string, $replacement, $start, $length );
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Wrapper around iconv_substr().
|
184 |
+
*
|
185 |
+
* @param string $s String.
|
186 |
+
* @param int $start Start offset.
|
187 |
+
* @param int|null $length Length.
|
188 |
+
* @param string|null $encoding Encoding.
|
189 |
+
*
|
190 |
+
* @return string
|
191 |
+
*/
|
192 |
+
protected static function iconv_substr( $s, $start, $length = null, $encoding = null )
|
193 |
+
{
|
194 |
+
if ( $start < 0 ) {
|
195 |
+
$start = self::strlen( $s, $encoding ) + $start;
|
196 |
+
if ( $start < 0 ) {
|
197 |
+
$start = 0;
|
198 |
+
}
|
199 |
+
}
|
200 |
+
|
201 |
+
if ( null === $length ) {
|
202 |
+
$length = 2147483647;
|
203 |
+
} elseif ( $length < 0 ) {
|
204 |
+
$length = self::strlen( $s, $encoding ) + ( $length - $start );
|
205 |
+
if ( $length < 0 ) {
|
206 |
+
return '';
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
return (string) ( null === $encoding ) ? \iconv_substr( $s, $start, $length ) : \iconv_substr( $s, $start, $length, $encoding );
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Decides whether this is a "subdirectory site" or not.
|
215 |
+
*
|
216 |
+
* @param bool $override Allows overriding the decision when needed.
|
217 |
+
*
|
218 |
+
* @return bool
|
219 |
+
*/
|
220 |
+
public static function siteurl_not_root( $override = null )
|
221 |
+
{
|
222 |
+
static $subdir = null;
|
223 |
+
|
224 |
+
if ( null === $subdir ) {
|
225 |
+
$parts = self::get_ao_wp_site_url_parts();
|
226 |
+
$subdir = ( isset( $parts['path'] ) && ( '/' !== $parts['path'] ) );
|
227 |
+
}
|
228 |
+
|
229 |
+
if ( null !== $override ) {
|
230 |
+
$subdir = $override;
|
231 |
+
}
|
232 |
+
|
233 |
+
return $subdir;
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Parse AUTOPTIMIZE_WP_SITE_URL into components using \parse_url(), but do
|
238 |
+
* so only once per request/lifecycle.
|
239 |
+
*
|
240 |
+
* @return array
|
241 |
+
*/
|
242 |
+
public static function get_ao_wp_site_url_parts()
|
243 |
+
{
|
244 |
+
static $parts = array();
|
245 |
+
|
246 |
+
if ( empty( $parts ) ) {
|
247 |
+
$parts = \parse_url( AUTOPTIMIZE_WP_SITE_URL );
|
248 |
+
}
|
249 |
+
|
250 |
+
return $parts;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Modify given $cdn_url to include the site path when needed.
|
255 |
+
*
|
256 |
+
* @param string $cdn_url CDN URL to tweak.
|
257 |
+
* @param bool $force_cache_miss Force a cache miss in order to be able
|
258 |
+
* to re-run the filter.
|
259 |
+
*
|
260 |
+
* @return string
|
261 |
+
*/
|
262 |
+
public static function tweak_cdn_url_if_needed( $cdn_url, $force_cache_miss = false )
|
263 |
+
{
|
264 |
+
static $results = array();
|
265 |
+
|
266 |
+
if ( ! isset( $results[ $cdn_url ] ) || $force_cache_miss ) {
|
267 |
+
|
268 |
+
// In order to return unmodified input when there's no need to tweak.
|
269 |
+
$results[ $cdn_url ] = $cdn_url;
|
270 |
+
|
271 |
+
// Behind a default true filter for backcompat, and only for sites
|
272 |
+
// in a subfolder/subdirectory, but still easily turned off if
|
273 |
+
// not wanted/needed...
|
274 |
+
if ( autoptimizeUtils::siteurl_not_root() ) {
|
275 |
+
$check = apply_filters( 'autoptimize_filter_cdn_magic_path_check', true, $cdn_url );
|
276 |
+
if ( $check ) {
|
277 |
+
$site_url_parts = autoptimizeUtils::get_ao_wp_site_url_parts();
|
278 |
+
$cdn_url_parts = \parse_url( $cdn_url );
|
279 |
+
$schemeless = self::is_protocol_relative( $cdn_url );
|
280 |
+
$cdn_url_parts = self::maybe_replace_cdn_path( $site_url_parts, $cdn_url_parts );
|
281 |
+
if ( false !== $cdn_url_parts ) {
|
282 |
+
$results[ $cdn_url ] = self::assemble_parsed_url( $cdn_url_parts, $schemeless );
|
283 |
+
}
|
284 |
+
}
|
285 |
+
}
|
286 |
+
}
|
287 |
+
|
288 |
+
return $results[ $cdn_url ];
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* When siteurl contans a path other than '/' and the CDN URL does not have
|
293 |
+
* a path or it's path is '/', this will modify the CDN URL's path component
|
294 |
+
* to match that of the siteurl.
|
295 |
+
* This is to support "magic" CDN urls that worked that way before v2.4...
|
296 |
+
*
|
297 |
+
* @param array $site_url_parts Site URL components array.
|
298 |
+
* @param array $cdn_url_parts CDN URL components array.
|
299 |
+
*
|
300 |
+
* @return array|false
|
301 |
+
*/
|
302 |
+
public static function maybe_replace_cdn_path( array $site_url_parts, array $cdn_url_parts )
|
303 |
+
{
|
304 |
+
if ( isset( $site_url_parts['path'] ) && '/' !== $site_url_parts['path'] ) {
|
305 |
+
if ( ! isset( $cdn_url_parts['path'] ) || '/' === $cdn_url_parts['path'] ) {
|
306 |
+
$cdn_url_parts['path'] = $site_url_parts['path'];
|
307 |
+
return $cdn_url_parts;
|
308 |
+
}
|
309 |
+
}
|
310 |
+
|
311 |
+
return false;
|
312 |
+
}
|
313 |
+
|
314 |
+
/**
|
315 |
+
* Given an array or components returned from \parse_url(), assembles back
|
316 |
+
* the complete URL.
|
317 |
+
* If optional
|
318 |
+
*
|
319 |
+
* @param array $parsed_url URL components array.
|
320 |
+
* @param bool $schemeless Whether the assembled URL should be
|
321 |
+
* protocol-relative (schemeless) or not.
|
322 |
+
*
|
323 |
+
* @return string
|
324 |
+
*/
|
325 |
+
public static function assemble_parsed_url( array $parsed_url, $schemeless = false )
|
326 |
+
{
|
327 |
+
$scheme = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] . '://' : '';
|
328 |
+
if ( $schemeless ) {
|
329 |
+
$scheme = '//';
|
330 |
+
}
|
331 |
+
$host = isset( $parsed_url['host'] ) ? $parsed_url['host'] : '';
|
332 |
+
$port = isset( $parsed_url['port'] ) ? ':' . $parsed_url['port'] : '';
|
333 |
+
$user = isset( $parsed_url['user'] ) ? $parsed_url['user'] : '';
|
334 |
+
$pass = isset( $parsed_url['pass'] ) ? ':' . $parsed_url['pass'] : '';
|
335 |
+
$pass = ( $user || $pass ) ? "$pass@" : '';
|
336 |
+
$path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : '';
|
337 |
+
$query = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : '';
|
338 |
+
$fragment = isset( $parsed_url['fragment'] ) ? '#' . $parsed_url['fragment'] : '';
|
339 |
+
|
340 |
+
return "$scheme$user$pass$host$port$path$query$fragment";
|
341 |
+
}
|
342 |
+
|
343 |
+
/**
|
344 |
+
* Returns true if given $url is protocol-relative.
|
345 |
+
*
|
346 |
+
* @param string $url URL to check.
|
347 |
+
*
|
348 |
+
* @return bool
|
349 |
+
*/
|
350 |
+
public static function is_protocol_relative( $url )
|
351 |
+
{
|
352 |
+
$result = false;
|
353 |
+
|
354 |
+
if ( ! empty( $url ) ) {
|
355 |
+
$result = ( '/' === $url{1} ); // second char is `/`.
|
356 |
+
}
|
357 |
+
|
358 |
+
return $result;
|
359 |
+
}
|
360 |
+
|
361 |
+
/**
|
362 |
+
* Canonicalizes the given path regardless of it existing or not.
|
363 |
+
*
|
364 |
+
* @param string $path Path to normalize.
|
365 |
+
*
|
366 |
+
* @return string
|
367 |
+
*/
|
368 |
+
public static function path_canonicalize( $path )
|
369 |
+
{
|
370 |
+
$patterns = array(
|
371 |
+
'~/{2,}~',
|
372 |
+
'~/(\./)+~',
|
373 |
+
'~([^/\.]+/(?R)*\.{2,}/)~',
|
374 |
+
'~\.\./~',
|
375 |
+
);
|
376 |
+
$replacements = array(
|
377 |
+
'/',
|
378 |
+
'/',
|
379 |
+
'',
|
380 |
+
'',
|
381 |
+
);
|
382 |
+
|
383 |
+
return preg_replace( $patterns, $replacements, $path );
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Checks to see if 3rd party services are available and stores result in option
|
388 |
+
*
|
389 |
+
* @param string $return_result should we return resulting service status array (default no).
|
390 |
+
*
|
391 |
+
* @return none if $return_result is false (default), array if $return_result is true.
|
392 |
+
*/
|
393 |
+
public static function check_service_availability( $return_result = false )
|
394 |
+
{
|
395 |
+
$service_availability_resp = wp_remote_get( 'https://misc.optimizingmatters.com/api/autoptimize_service_availablity.json?from=aomain&ver=' . AUTOPTIMIZE_PLUGIN_VERSION );
|
396 |
+
if ( ! is_wp_error( $service_availability_resp ) ) {
|
397 |
+
if ( '200' == wp_remote_retrieve_response_code( $service_availability_resp ) ) {
|
398 |
+
$availabilities = json_decode( wp_remote_retrieve_body( $service_availability_resp ), true );
|
399 |
+
if ( is_array( $availabilities ) ) {
|
400 |
+
update_option( 'autoptimize_service_availablity', $availabilities );
|
401 |
+
if ( $return_result ) {
|
402 |
+
return $availabilities;
|
403 |
+
}
|
404 |
+
}
|
405 |
+
}
|
406 |
+
}
|
407 |
+
}
|
408 |
+
|
409 |
+
/**
|
410 |
+
* Returns true if the string is a valid regex.
|
411 |
+
*
|
412 |
+
* @param string $string String, duh.
|
413 |
+
*
|
414 |
+
* @return bool
|
415 |
+
*/
|
416 |
+
public static function str_is_valid_regex( $string )
|
417 |
+
{
|
418 |
+
set_error_handler( function() {}, E_WARNING );
|
419 |
+
$is_regex = ( false !== preg_match( $string, '' ) );
|
420 |
+
restore_error_handler();
|
421 |
+
|
422 |
+
return $is_regex;
|
423 |
+
}
|
424 |
+
}
|
classes/autoptimizeVersionUpdatesHandler.php
ADDED
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Handles version updates and should only be instantiated in autoptimize.php if/when needed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
7 |
+
exit;
|
8 |
+
}
|
9 |
+
|
10 |
+
class autoptimizeVersionUpdatesHandler
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* The current major version string.
|
14 |
+
*
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
+
protected $current_major_version = null;
|
18 |
+
|
19 |
+
public function __construct( $current_version )
|
20 |
+
{
|
21 |
+
$this->current_major_version = substr( $current_version, 0, 3 );
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Runs all needed upgrade procedures (depending on the
|
26 |
+
* current major version specified during class instantiation)
|
27 |
+
*/
|
28 |
+
public function run_needed_major_upgrades()
|
29 |
+
{
|
30 |
+
$major_update = false;
|
31 |
+
|
32 |
+
switch ( $this->current_major_version ) {
|
33 |
+
case '1.6':
|
34 |
+
$this->upgrade_from_1_6();
|
35 |
+
$major_update = true;
|
36 |
+
// No break, intentionally, so all upgrades are ran during a single request...
|
37 |
+
case '1.7':
|
38 |
+
$this->upgrade_from_1_7();
|
39 |
+
$major_update = true;
|
40 |
+
// No break, intentionally, so all upgrades are ran during a single request...
|
41 |
+
case '1.9':
|
42 |
+
$this->upgrade_from_1_9();
|
43 |
+
$major_update = true;
|
44 |
+
// No break, intentionally, so all upgrades are ran during a single request...
|
45 |
+
case '2.2':
|
46 |
+
$this->upgrade_from_2_2();
|
47 |
+
$major_update = true;
|
48 |
+
// No break, intentionally, so all upgrades are ran during a single request...
|
49 |
+
}
|
50 |
+
|
51 |
+
if ( true === $major_update ) {
|
52 |
+
$this->on_major_version_update();
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Checks specified version against the one stored in the database under `autoptimize_version` and performs
|
58 |
+
* any major upgrade routines if needed.
|
59 |
+
* Updates the database version to the specified $target if it's different to the one currently stored there.
|
60 |
+
*
|
61 |
+
* @param string $target Target version to check against (ie., the currently running one).
|
62 |
+
*/
|
63 |
+
public static function check_installed_and_update( $target )
|
64 |
+
{
|
65 |
+
$db_version = get_option( 'autoptimize_version', 'none' );
|
66 |
+
if ( $db_version !== $target ) {
|
67 |
+
if ( 'none' === $db_version ) {
|
68 |
+
add_action( 'admin_notices', 'autoptimizeMain::notice_installed' );
|
69 |
+
} else {
|
70 |
+
$updater = new self( $db_version );
|
71 |
+
$updater->run_needed_major_upgrades();
|
72 |
+
}
|
73 |
+
|
74 |
+
// Versions differed, upgrades happened if needed, store the new version.
|
75 |
+
update_option( 'autoptimize_version', $target );
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Called after any major version update (and it's accompanying upgrade procedure)
|
81 |
+
* has happened. Clears cache and sets an admin notice.
|
82 |
+
*/
|
83 |
+
protected function on_major_version_update()
|
84 |
+
{
|
85 |
+
// The transients guard here prevents stale object caches from busting the cache on every request.
|
86 |
+
if ( false == get_transient( 'autoptimize_stale_option_buster' ) ) {
|
87 |
+
set_transient( 'autoptimize_stale_option_buster', 'Mamsie & Liessie zehhe: ZWIJH!', HOUR_IN_SECONDS );
|
88 |
+
autoptimizeCache::clearall();
|
89 |
+
add_action( 'admin_notices', 'autoptimizeMain::notice_updated' );
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* From back in the days when I did not yet consider multisite.
|
95 |
+
*/
|
96 |
+
private function upgrade_from_1_6()
|
97 |
+
{
|
98 |
+
// If user was on version 1.6.x, force advanced options to be shown by default.
|
99 |
+
update_option( 'autoptimize_show_adv', '1' );
|
100 |
+
|
101 |
+
// And remove old options.
|
102 |
+
$to_delete_options = array(
|
103 |
+
'autoptimize_cdn_css',
|
104 |
+
'autoptimize_cdn_css_url',
|
105 |
+
'autoptimize_cdn_js',
|
106 |
+
'autoptimize_cdn_js_url',
|
107 |
+
'autoptimize_cdn_img',
|
108 |
+
'autoptimize_cdn_img_url',
|
109 |
+
'autoptimize_css_yui',
|
110 |
+
);
|
111 |
+
foreach ( $to_delete_options as $del_opt ) {
|
112 |
+
delete_option( $del_opt );
|
113 |
+
}
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Forces WP 3.8 dashicons in CSS exclude options when upgrading from 1.7 to 1.8
|
118 |
+
*
|
119 |
+
* @global $wpdb
|
120 |
+
*/
|
121 |
+
private function upgrade_from_1_7()
|
122 |
+
{
|
123 |
+
if ( ! is_multisite() ) {
|
124 |
+
$css_exclude = get_option( 'autoptimize_css_exclude' );
|
125 |
+
if ( empty( $css_exclude ) ) {
|
126 |
+
$css_exclude = 'admin-bar.min.css, dashicons.min.css';
|
127 |
+
} elseif ( false === strpos( $css_exclude, 'dashicons.min.css' ) ) {
|
128 |
+
$css_exclude .= ', dashicons.min.css';
|
129 |
+
}
|
130 |
+
update_option( 'autoptimize_css_exclude', $css_exclude );
|
131 |
+
} else {
|
132 |
+
global $wpdb;
|
133 |
+
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
134 |
+
$original_blog_id = get_current_blog_id();
|
135 |
+
foreach ( $blog_ids as $blog_id ) {
|
136 |
+
switch_to_blog( $blog_id );
|
137 |
+
$css_exclude = get_option( 'autoptimize_css_exclude' );
|
138 |
+
if ( empty( $css_exclude ) ) {
|
139 |
+
$css_exclude = 'admin-bar.min.css, dashicons.min.css';
|
140 |
+
} elseif ( false === strpos( $css_exclude, 'dashicons.min.css' ) ) {
|
141 |
+
$css_exclude .= ', dashicons.min.css';
|
142 |
+
}
|
143 |
+
update_option( 'autoptimize_css_exclude', $css_exclude );
|
144 |
+
}
|
145 |
+
switch_to_blog( $original_blog_id );
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* 2.0 will not aggregate inline CSS/JS by default, but we want users
|
151 |
+
* upgrading from 1.9 to keep their inline code aggregated by default.
|
152 |
+
*
|
153 |
+
* @global $wpdb
|
154 |
+
*/
|
155 |
+
private function upgrade_from_1_9()
|
156 |
+
{
|
157 |
+
if ( ! is_multisite() ) {
|
158 |
+
update_option( 'autoptimize_css_include_inline', 'on' );
|
159 |
+
update_option( 'autoptimize_js_include_inline', 'on' );
|
160 |
+
} else {
|
161 |
+
global $wpdb;
|
162 |
+
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
163 |
+
$original_blog_id = get_current_blog_id();
|
164 |
+
foreach ( $blog_ids as $blog_id ) {
|
165 |
+
switch_to_blog( $blog_id );
|
166 |
+
update_option( 'autoptimize_css_include_inline', 'on' );
|
167 |
+
update_option( 'autoptimize_js_include_inline', 'on' );
|
168 |
+
}
|
169 |
+
switch_to_blog( $original_blog_id );
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* 2.3 has no "remove google fonts" in main screen, moved to "extra"
|
175 |
+
*
|
176 |
+
* @global $wpdb
|
177 |
+
*/
|
178 |
+
private function upgrade_from_2_2()
|
179 |
+
{
|
180 |
+
if ( ! is_multisite() ) {
|
181 |
+
$this->do_2_2_settings_update();
|
182 |
+
} else {
|
183 |
+
global $wpdb;
|
184 |
+
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
185 |
+
$original_blog_id = get_current_blog_id();
|
186 |
+
foreach ( $blog_ids as $blog_id ) {
|
187 |
+
switch_to_blog( $blog_id );
|
188 |
+
$this->do_2_2_settings_update();
|
189 |
+
}
|
190 |
+
switch_to_blog( $original_blog_id );
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Helper for 2.2 autoptimize_extra_settings upgrade to avoid duplicate code
|
196 |
+
*/
|
197 |
+
private function do_2_2_settings_update()
|
198 |
+
{
|
199 |
+
$nogooglefont = get_option( 'autoptimize_css_nogooglefont', '' );
|
200 |
+
$ao_extrasetting = get_option( 'autoptimize_extra_settings', '' );
|
201 |
+
if ( ( $nogooglefont ) && ( empty( $ao_extrasetting ) ) ) {
|
202 |
+
update_option( 'autoptimize_extra_settings', autoptimizeConfig::get_ao_extra_default_options() );
|
203 |
+
}
|
204 |
+
delete_option( 'autoptimize_css_nogooglefont' );
|
205 |
+
}
|
206 |
+
}
|
classes/external/index.html
CHANGED
@@ -1 +1 @@
|
|
1 |
-
<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
|
1 |
+
<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
|
classes/external/js/index.html
CHANGED
@@ -1 +1 @@
|
|
1 |
-
<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
|
1 |
+
<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
|
classes/external/php/index.html
CHANGED
@@ -1 +1 @@
|
|
1 |
-
<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
|
1 |
+
<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
|
classes/external/php/jsmin-1.1.1.php
DELETED
@@ -1,291 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* jsmin.php - PHP implementation of Douglas Crockford's JSMin.
|
4 |
-
*
|
5 |
-
* This is pretty much a direct port of jsmin.c to PHP with just a few
|
6 |
-
* PHP-specific performance tweaks. Also, whereas jsmin.c reads from stdin and
|
7 |
-
* outputs to stdout, this library accepts a string as input and returns another
|
8 |
-
* string as output.
|
9 |
-
*
|
10 |
-
* PHP 5 or higher is required.
|
11 |
-
*
|
12 |
-
* Permission is hereby granted to use this version of the library under the
|
13 |
-
* same terms as jsmin.c, which has the following license:
|
14 |
-
*
|
15 |
-
* --
|
16 |
-
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
17 |
-
*
|
18 |
-
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
19 |
-
* this software and associated documentation files (the "Software"), to deal in
|
20 |
-
* the Software without restriction, including without limitation the rights to
|
21 |
-
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
22 |
-
* of the Software, and to permit persons to whom the Software is furnished to do
|
23 |
-
* so, subject to the following conditions:
|
24 |
-
*
|
25 |
-
* The above copyright notice and this permission notice shall be included in all
|
26 |
-
* copies or substantial portions of the Software.
|
27 |
-
*
|
28 |
-
* The Software shall be used for Good, not Evil.
|
29 |
-
*
|
30 |
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
31 |
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
32 |
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
33 |
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
34 |
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
35 |
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
36 |
-
* SOFTWARE.
|
37 |
-
* --
|
38 |
-
*
|
39 |
-
* @package JSMin
|
40 |
-
* @author Ryan Grove <ryan@wonko.com>
|
41 |
-
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
|
42 |
-
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
|
43 |
-
* @license http://opensource.org/licenses/mit-license.php MIT License
|
44 |
-
* @version 1.1.1 (2008-03-02)
|
45 |
-
* @link http://code.google.com/p/jsmin-php/
|
46 |
-
*/
|
47 |
-
|
48 |
-
class JSMin {
|
49 |
-
const ORD_LF = 10;
|
50 |
-
const ORD_SPACE = 32;
|
51 |
-
|
52 |
-
protected $a = '';
|
53 |
-
protected $b = '';
|
54 |
-
protected $input = '';
|
55 |
-
protected $inputIndex = 0;
|
56 |
-
protected $inputLength = 0;
|
57 |
-
protected $lookAhead = null;
|
58 |
-
protected $output = '';
|
59 |
-
|
60 |
-
// -- Public Static Methods --------------------------------------------------
|
61 |
-
|
62 |
-
public static function minify($js) {
|
63 |
-
$jsmin = new JSMin($js);
|
64 |
-
return $jsmin->min();
|
65 |
-
}
|
66 |
-
|
67 |
-
// -- Public Instance Methods ------------------------------------------------
|
68 |
-
|
69 |
-
public function __construct($input) {
|
70 |
-
$this->input = str_replace("\r\n", "\n", $input);
|
71 |
-
$this->inputLength = strlen($this->input);
|
72 |
-
}
|
73 |
-
|
74 |
-
// -- Protected Instance Methods ---------------------------------------------
|
75 |
-
|
76 |
-
protected function action($d) {
|
77 |
-
switch($d) {
|
78 |
-
case 1:
|
79 |
-
$this->output .= $this->a;
|
80 |
-
|
81 |
-
case 2:
|
82 |
-
$this->a = $this->b;
|
83 |
-
|
84 |
-
if ($this->a === "'" || $this->a === '"') {
|
85 |
-
for (;;) {
|
86 |
-
$this->output .= $this->a;
|
87 |
-
$this->a = $this->get();
|
88 |
-
|
89 |
-
if ($this->a === $this->b) {
|
90 |
-
break;
|
91 |
-
}
|
92 |
-
|
93 |
-
if (ord($this->a) <= self::ORD_LF) {
|
94 |
-
throw new JSMinException('Unterminated string literal.');
|
95 |
-
}
|
96 |
-
|
97 |
-
if ($this->a === '\\') {
|
98 |
-
$this->output .= $this->a;
|
99 |
-
$this->a = $this->get();
|
100 |
-
}
|
101 |
-
}
|
102 |
-
}
|
103 |
-
|
104 |
-
case 3:
|
105 |
-
$this->b = $this->next();
|
106 |
-
|
107 |
-
if ($this->b === '/' && (
|
108 |
-
$this->a === '(' || $this->a === ',' || $this->a === '=' ||
|
109 |
-
$this->a === ':' || $this->a === '[' || $this->a === '!' ||
|
110 |
-
$this->a === '&' || $this->a === '|' || $this->a === '?')) {
|
111 |
-
|
112 |
-
$this->output .= $this->a . $this->b;
|
113 |
-
|
114 |
-
for (;;) {
|
115 |
-
$this->a = $this->get();
|
116 |
-
|
117 |
-
if ($this->a === '/') {
|
118 |
-
break;
|
119 |
-
} elseif ($this->a === '\\') {
|
120 |
-
$this->output .= $this->a;
|
121 |
-
$this->a = $this->get();
|
122 |
-
} elseif (ord($this->a) <= self::ORD_LF) {
|
123 |
-
throw new JSMinException('Unterminated regular expression '.
|
124 |
-
'literal.');
|
125 |
-
}
|
126 |
-
|
127 |
-
$this->output .= $this->a;
|
128 |
-
}
|
129 |
-
|
130 |
-
$this->b = $this->next();
|
131 |
-
}
|
132 |
-
}
|
133 |
-
}
|
134 |
-
|
135 |
-
protected function get() {
|
136 |
-
$c = $this->lookAhead;
|
137 |
-
$this->lookAhead = null;
|
138 |
-
|
139 |
-
if ($c === null) {
|
140 |
-
if ($this->inputIndex < $this->inputLength) {
|
141 |
-
$c = $this->input[$this->inputIndex];
|
142 |
-
$this->inputIndex += 1;
|
143 |
-
} else {
|
144 |
-
$c = null;
|
145 |
-
}
|
146 |
-
}
|
147 |
-
|
148 |
-
if ($c === "\r") {
|
149 |
-
return "\n";
|
150 |
-
}
|
151 |
-
|
152 |
-
if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
|
153 |
-
return $c;
|
154 |
-
}
|
155 |
-
|
156 |
-
return ' ';
|
157 |
-
}
|
158 |
-
|
159 |
-
protected function isAlphaNum($c) {
|
160 |
-
return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
|
161 |
-
}
|
162 |
-
|
163 |
-
protected function min() {
|
164 |
-
$this->a = "\n";
|
165 |
-
$this->action(3);
|
166 |
-
|
167 |
-
while ($this->a !== null) {
|
168 |
-
switch ($this->a) {
|
169 |
-
case ' ':
|
170 |
-
if ($this->isAlphaNum($this->b)) {
|
171 |
-
$this->action(1);
|
172 |
-
} else {
|
173 |
-
$this->action(2);
|
174 |
-
}
|
175 |
-
break;
|
176 |
-
|
177 |
-
case "\n":
|
178 |
-
switch ($this->b) {
|
179 |
-
case '{':
|
180 |
-
case '[':
|
181 |
-
case '(':
|
182 |
-
case '+':
|
183 |
-
case '-':
|
184 |
-
$this->action(1);
|
185 |
-
break;
|
186 |
-
|
187 |
-
case ' ':
|
188 |
-
$this->action(3);
|
189 |
-
break;
|
190 |
-
|
191 |
-
default:
|
192 |
-
if ($this->isAlphaNum($this->b)) {
|
193 |
-
$this->action(1);
|
194 |
-
}
|
195 |
-
else {
|
196 |
-
$this->action(2);
|
197 |
-
}
|
198 |
-
}
|
199 |
-
break;
|
200 |
-
|
201 |
-
default:
|
202 |
-
switch ($this->b) {
|
203 |
-
case ' ':
|
204 |
-
if ($this->isAlphaNum($this->a)) {
|
205 |
-
$this->action(1);
|
206 |
-
break;
|
207 |
-
}
|
208 |
-
|
209 |
-
$this->action(3);
|
210 |
-
break;
|
211 |
-
|
212 |
-
case "\n":
|
213 |
-
switch ($this->a) {
|
214 |
-
case '}':
|
215 |
-
case ']':
|
216 |
-
case ')':
|
217 |
-
case '+':
|
218 |
-
case '-':
|
219 |
-
case '"':
|
220 |
-
case "'":
|
221 |
-
$this->action(1);
|
222 |
-
break;
|
223 |
-
|
224 |
-
default:
|
225 |
-
if ($this->isAlphaNum($this->a)) {
|
226 |
-
$this->action(1);
|
227 |
-
}
|
228 |
-
else {
|
229 |
-
$this->action(3);
|
230 |
-
}
|
231 |
-
}
|
232 |
-
break;
|
233 |
-
|
234 |
-
default:
|
235 |
-
$this->action(1);
|
236 |
-
break;
|
237 |
-
}
|
238 |
-
}
|
239 |
-
}
|
240 |
-
|
241 |
-
return $this->output;
|
242 |
-
}
|
243 |
-
|
244 |
-
protected function next() {
|
245 |
-
$c = $this->get();
|
246 |
-
|
247 |
-
if ($c === '/') {
|
248 |
-
switch($this->peek()) {
|
249 |
-
case '/':
|
250 |
-
for (;;) {
|
251 |
-
$c = $this->get();
|
252 |
-
|
253 |
-
if (ord($c) <= self::ORD_LF) {
|
254 |
-
return $c;
|
255 |
-
}
|
256 |
-
}
|
257 |
-
|
258 |
-
case '*':
|
259 |
-
$this->get();
|
260 |
-
|
261 |
-
for (;;) {
|
262 |
-
switch($this->get()) {
|
263 |
-
case '*':
|
264 |
-
if ($this->peek() === '/') {
|
265 |
-
$this->get();
|
266 |
-
return ' ';
|
267 |
-
}
|
268 |
-
break;
|
269 |
-
|
270 |
-
case null:
|
271 |
-
throw new JSMinException('Unterminated comment.');
|
272 |
-
}
|
273 |
-
}
|
274 |
-
|
275 |
-
default:
|
276 |
-
return $c;
|
277 |
-
}
|
278 |
-
}
|
279 |
-
|
280 |
-
return $c;
|
281 |
-
}
|
282 |
-
|
283 |
-
protected function peek() {
|
284 |
-
$this->lookAhead = $this->get();
|
285 |
-
return $this->lookAhead;
|
286 |
-
}
|
287 |
-
}
|
288 |
-
|
289 |
-
// -- Exceptions ---------------------------------------------------------------
|
290 |
-
class JSMinException extends Exception {}
|
291 |
-
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/external/php/{minify-2.3.1-jsmin.php → jsmin.php}
RENAMED
@@ -54,6 +54,8 @@
|
|
54 |
* @link http://code.google.com/p/jsmin-php/
|
55 |
*/
|
56 |
|
|
|
|
|
57 |
class JSMin {
|
58 |
const ORD_LF = 10;
|
59 |
const ORD_SPACE = 32;
|
@@ -194,19 +196,17 @@ class JSMin {
|
|
194 |
// fallthrough intentional
|
195 |
case self::ACTION_DELETE_A: // 2
|
196 |
$this->a = $this->b;
|
197 |
-
if ($this->a === "'" || $this->a === '"'
|
198 |
-
$delimiter = $this->a;
|
199 |
$str = $this->a; // in case needed for exception
|
200 |
for(;;) {
|
201 |
$this->output .= $this->a;
|
202 |
$this->lastByteOut = $this->a;
|
|
|
203 |
$this->a = $this->get();
|
204 |
if ($this->a === $this->b) { // end quote
|
205 |
break;
|
206 |
}
|
207 |
-
if ($
|
208 |
-
// leave the newline
|
209 |
-
} elseif ($this->isEOF($this->a)) {
|
210 |
$byte = $this->inputIndex - 1;
|
211 |
throw new JSMin_UnterminatedStringException(
|
212 |
"JSMin: Unterminated String at byte {$byte}: {$str}");
|
@@ -215,7 +215,8 @@ class JSMin {
|
|
215 |
if ($this->a === '\\') {
|
216 |
$this->output .= $this->a;
|
217 |
$this->lastByteOut = $this->a;
|
218 |
-
|
|
|
219 |
$str .= $this->a;
|
220 |
}
|
221 |
}
|
54 |
* @link http://code.google.com/p/jsmin-php/
|
55 |
*/
|
56 |
|
57 |
+
// This is from https://github.com/mrclay/jsmin-php 2.3.2
|
58 |
+
|
59 |
class JSMin {
|
60 |
const ORD_LF = 10;
|
61 |
const ORD_SPACE = 32;
|
196 |
// fallthrough intentional
|
197 |
case self::ACTION_DELETE_A: // 2
|
198 |
$this->a = $this->b;
|
199 |
+
if ($this->a === "'" || $this->a === '"') { // string literal
|
|
|
200 |
$str = $this->a; // in case needed for exception
|
201 |
for(;;) {
|
202 |
$this->output .= $this->a;
|
203 |
$this->lastByteOut = $this->a;
|
204 |
+
|
205 |
$this->a = $this->get();
|
206 |
if ($this->a === $this->b) { // end quote
|
207 |
break;
|
208 |
}
|
209 |
+
if ($this->isEOF($this->a)) {
|
|
|
|
|
210 |
$byte = $this->inputIndex - 1;
|
211 |
throw new JSMin_UnterminatedStringException(
|
212 |
"JSMin: Unterminated String at byte {$byte}: {$str}");
|
215 |
if ($this->a === '\\') {
|
216 |
$this->output .= $this->a;
|
217 |
$this->lastByteOut = $this->a;
|
218 |
+
|
219 |
+
$this->a = $this->get();
|
220 |
$str .= $this->a;
|
221 |
}
|
222 |
}
|
classes/external/php/minify-2.1.7-html.php
DELETED
@@ -1,257 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Class Minify_HTML
|
4 |
-
* @package Minify
|
5 |
-
*/
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Compress HTML
|
9 |
-
*
|
10 |
-
* This is a heavy regex-based removal of whitespace, unnecessary comments and
|
11 |
-
* tokens. IE conditional comments are preserved. There are also options to have
|
12 |
-
* STYLE and SCRIPT blocks compressed by callback functions.
|
13 |
-
*
|
14 |
-
* A test suite is available.
|
15 |
-
*
|
16 |
-
* @package Minify
|
17 |
-
* @author Stephen Clay <steve@mrclay.org>
|
18 |
-
*/
|
19 |
-
class Minify_HTML {
|
20 |
-
/**
|
21 |
-
* @var boolean
|
22 |
-
*/
|
23 |
-
protected $_jsCleanComments = true;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* "Minify" an HTML page
|
27 |
-
*
|
28 |
-
* @param string $html
|
29 |
-
*
|
30 |
-
* @param array $options
|
31 |
-
*
|
32 |
-
* 'cssMinifier' : (optional) callback function to process content of STYLE
|
33 |
-
* elements.
|
34 |
-
*
|
35 |
-
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
|
36 |
-
* elements. Note: the type attribute is ignored.
|
37 |
-
*
|
38 |
-
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
39 |
-
* unset, minify will sniff for an XHTML doctype.
|
40 |
-
*
|
41 |
-
* @return string
|
42 |
-
*/
|
43 |
-
public static function minify($html, $options = array()) {
|
44 |
-
$min = new self($html, $options);
|
45 |
-
return $min->process();
|
46 |
-
}
|
47 |
-
|
48 |
-
|
49 |
-
/**
|
50 |
-
* Create a minifier object
|
51 |
-
*
|
52 |
-
* @param string $html
|
53 |
-
*
|
54 |
-
* @param array $options
|
55 |
-
*
|
56 |
-
* 'cssMinifier' : (optional) callback function to process content of STYLE
|
57 |
-
* elements.
|
58 |
-
*
|
59 |
-
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
|
60 |
-
* elements. Note: the type attribute is ignored.
|
61 |
-
*
|
62 |
-
* 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
|
63 |
-
*
|
64 |
-
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
65 |
-
* unset, minify will sniff for an XHTML doctype.
|
66 |
-
*
|
67 |
-
* @return null
|
68 |
-
*/
|
69 |
-
public function __construct($html, $options = array())
|
70 |
-
{
|
71 |
-
$this->_html = str_replace("\r\n", "\n", trim($html));
|
72 |
-
if (isset($options['xhtml'])) {
|
73 |
-
$this->_isXhtml = (bool)$options['xhtml'];
|
74 |
-
}
|
75 |
-
if (isset($options['cssMinifier'])) {
|
76 |
-
$this->_cssMinifier = $options['cssMinifier'];
|
77 |
-
}
|
78 |
-
if (isset($options['jsMinifier'])) {
|
79 |
-
$this->_jsMinifier = $options['jsMinifier'];
|
80 |
-
}
|
81 |
-
if (isset($options['jsCleanComments'])) {
|
82 |
-
$this->_jsCleanComments = (bool)$options['jsCleanComments'];
|
83 |
-
}
|
84 |
-
}
|
85 |
-
|
86 |
-
|
87 |
-
/**
|
88 |
-
* Minify the markeup given in the constructor
|
89 |
-
*
|
90 |
-
* @return string
|
91 |
-
*/
|
92 |
-
public function process()
|
93 |
-
{
|
94 |
-
if ($this->_isXhtml === null) {
|
95 |
-
$this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
|
96 |
-
}
|
97 |
-
|
98 |
-
$this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
|
99 |
-
$this->_placeholders = array();
|
100 |
-
|
101 |
-
// replace SCRIPTs (and minify) with placeholders
|
102 |
-
$this->_html = preg_replace_callback(
|
103 |
-
'/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
|
104 |
-
,array($this, '_removeScriptCB')
|
105 |
-
,$this->_html);
|
106 |
-
|
107 |
-
// replace STYLEs (and minify) with placeholders
|
108 |
-
$this->_html = preg_replace_callback(
|
109 |
-
'/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
|
110 |
-
,array($this, '_removeStyleCB')
|
111 |
-
,$this->_html);
|
112 |
-
|
113 |
-
// remove HTML comments (not containing IE conditional comments).
|
114 |
-
$this->_html = preg_replace_callback(
|
115 |
-
'/<!--([\\s\\S]*?)-->/'
|
116 |
-
,array($this, '_commentCB')
|
117 |
-
,$this->_html);
|
118 |
-
|
119 |
-
// replace PREs with placeholders
|
120 |
-
$this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
|
121 |
-
,array($this, '_removePreCB')
|
122 |
-
,$this->_html);
|
123 |
-
|
124 |
-
// replace TEXTAREAs with placeholders
|
125 |
-
$this->_html = preg_replace_callback(
|
126 |
-
'/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
|
127 |
-
,array($this, '_removeTextareaCB')
|
128 |
-
,$this->_html);
|
129 |
-
|
130 |
-
// trim each line.
|
131 |
-
// @todo take into account attribute values that span multiple lines.
|
132 |
-
$this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
|
133 |
-
|
134 |
-
// remove ws around block/undisplayed elements
|
135 |
-
$this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
|
136 |
-
.'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
|
137 |
-
.'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
|
138 |
-
.'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
|
139 |
-
.'|ul)\\b[^>]*>)/i', '$1', $this->_html);
|
140 |
-
|
141 |
-
// remove ws outside of all elements
|
142 |
-
$this->_html = preg_replace(
|
143 |
-
'/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
|
144 |
-
,'>$1$2$3<'
|
145 |
-
,$this->_html);
|
146 |
-
|
147 |
-
// use newlines before 1st attribute in open tags (to limit line lengths)
|
148 |
-
$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
|
149 |
-
|
150 |
-
// fill placeholders
|
151 |
-
$this->_html = str_replace(
|
152 |
-
array_keys($this->_placeholders)
|
153 |
-
,array_values($this->_placeholders)
|
154 |
-
,$this->_html
|
155 |
-
);
|
156 |
-
// issue 229: multi-pass to catch scripts that didn't get replaced in textareas
|
157 |
-
$this->_html = str_replace(
|
158 |
-
array_keys($this->_placeholders)
|
159 |
-
,array_values($this->_placeholders)
|
160 |
-
,$this->_html
|
161 |
-
);
|
162 |
-
return $this->_html;
|
163 |
-
}
|
164 |
-
|
165 |
-
protected function _commentCB($m)
|
166 |
-
{
|
167 |
-
return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
|
168 |
-
? $m[0]
|
169 |
-
: '';
|
170 |
-
}
|
171 |
-
|
172 |
-
protected function _reservePlace($content)
|
173 |
-
{
|
174 |
-
$placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
|
175 |
-
$this->_placeholders[$placeholder] = $content;
|
176 |
-
return $placeholder;
|
177 |
-
}
|
178 |
-
|
179 |
-
protected $_isXhtml = null;
|
180 |
-
protected $_replacementHash = null;
|
181 |
-
protected $_placeholders = array();
|
182 |
-
protected $_cssMinifier = null;
|
183 |
-
protected $_jsMinifier = null;
|
184 |
-
|
185 |
-
protected function _removePreCB($m)
|
186 |
-
{
|
187 |
-
return $this->_reservePlace("<pre{$m[1]}");
|
188 |
-
}
|
189 |
-
|
190 |
-
protected function _removeTextareaCB($m)
|
191 |
-
{
|
192 |
-
return $this->_reservePlace("<textarea{$m[1]}");
|
193 |
-
}
|
194 |
-
|
195 |
-
protected function _removeStyleCB($m)
|
196 |
-
{
|
197 |
-
$openStyle = "<style{$m[1]}";
|
198 |
-
$css = $m[2];
|
199 |
-
// remove HTML comments
|
200 |
-
$css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
|
201 |
-
|
202 |
-
// remove CDATA section markers
|
203 |
-
$css = $this->_removeCdata($css);
|
204 |
-
|
205 |
-
// minify
|
206 |
-
$minifier = $this->_cssMinifier
|
207 |
-
? $this->_cssMinifier
|
208 |
-
: 'trim';
|
209 |
-
$css = call_user_func($minifier, $css);
|
210 |
-
|
211 |
-
return $this->_reservePlace($this->_needsCdata($css)
|
212 |
-
? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
|
213 |
-
: "{$openStyle}{$css}</style>"
|
214 |
-
);
|
215 |
-
}
|
216 |
-
|
217 |
-
protected function _removeScriptCB($m)
|
218 |
-
{
|
219 |
-
$openScript = "<script{$m[2]}";
|
220 |
-
$js = $m[3];
|
221 |
-
|
222 |
-
// whitespace surrounding? preserve at least one space
|
223 |
-
$ws1 = ($m[1] === '') ? '' : ' ';
|
224 |
-
$ws2 = ($m[4] === '') ? '' : ' ';
|
225 |
-
|
226 |
-
// remove HTML comments (and ending "//" if present)
|
227 |
-
if ($this->_jsCleanComments) {
|
228 |
-
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
|
229 |
-
}
|
230 |
-
|
231 |
-
// remove CDATA section markers
|
232 |
-
$js = $this->_removeCdata($js);
|
233 |
-
|
234 |
-
// minify
|
235 |
-
$minifier = $this->_jsMinifier
|
236 |
-
? $this->_jsMinifier
|
237 |
-
: 'trim';
|
238 |
-
$js = call_user_func($minifier, $js);
|
239 |
-
|
240 |
-
return $this->_reservePlace($this->_needsCdata($js)
|
241 |
-
? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
|
242 |
-
: "{$ws1}{$openScript}{$js}</script>{$ws2}"
|
243 |
-
);
|
244 |
-
}
|
245 |
-
|
246 |
-
protected function _removeCdata($str)
|
247 |
-
{
|
248 |
-
return (false !== strpos($str, '<![CDATA['))
|
249 |
-
? str_replace(array('<![CDATA[', ']]>'), '', $str)
|
250 |
-
: $str;
|
251 |
-
}
|
252 |
-
|
253 |
-
protected function _needsCdata($str)
|
254 |
-
{
|
255 |
-
return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
|
256 |
-
}
|
257 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/external/php/minify-2.1.7-jsmin.php
DELETED
@@ -1,447 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
|
4 |
-
*
|
5 |
-
* <code>
|
6 |
-
* $minifiedJs = JSMin::minify($js);
|
7 |
-
* </code>
|
8 |
-
*
|
9 |
-
* This is a modified port of jsmin.c. Improvements:
|
10 |
-
*
|
11 |
-
* Does not choke on some regexp literals containing quote characters. E.g. /'/
|
12 |
-
*
|
13 |
-
* Spaces are preserved after some add/sub operators, so they are not mistakenly
|
14 |
-
* converted to post-inc/dec. E.g. a + ++b -> a+ ++b
|
15 |
-
*
|
16 |
-
* Preserves multi-line comments that begin with /*!
|
17 |
-
*
|
18 |
-
* PHP 5 or higher is required.
|
19 |
-
*
|
20 |
-
* Permission is hereby granted to use this version of the library under the
|
21 |
-
* same terms as jsmin.c, which has the following license:
|
22 |
-
*
|
23 |
-
* --
|
24 |
-
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
25 |
-
*
|
26 |
-
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
27 |
-
* this software and associated documentation files (the "Software"), to deal in
|
28 |
-
* the Software without restriction, including without limitation the rights to
|
29 |
-
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
30 |
-
* of the Software, and to permit persons to whom the Software is furnished to do
|
31 |
-
* so, subject to the following conditions:
|
32 |
-
*
|
33 |
-
* The above copyright notice and this permission notice shall be included in all
|
34 |
-
* copies or substantial portions of the Software.
|
35 |
-
*
|
36 |
-
* The Software shall be used for Good, not Evil.
|
37 |
-
*
|
38 |
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
39 |
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
40 |
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
41 |
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
42 |
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
43 |
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
44 |
-
* SOFTWARE.
|
45 |
-
* --
|
46 |
-
*
|
47 |
-
* @package JSMin
|
48 |
-
* @author Ryan Grove <ryan@wonko.com> (PHP port)
|
49 |
-
* @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
|
50 |
-
* @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
|
51 |
-
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
|
52 |
-
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
|
53 |
-
* @license http://opensource.org/licenses/mit-license.php MIT License
|
54 |
-
* @link http://code.google.com/p/jsmin-php/
|
55 |
-
*/
|
56 |
-
|
57 |
-
class JSMin {
|
58 |
-
const ORD_LF = 10;
|
59 |
-
const ORD_SPACE = 32;
|
60 |
-
const ACTION_KEEP_A = 1;
|
61 |
-
const ACTION_DELETE_A = 2;
|
62 |
-
const ACTION_DELETE_A_B = 3;
|
63 |
-
|
64 |
-
protected $a = "\n";
|
65 |
-
protected $b = '';
|
66 |
-
protected $input = '';
|
67 |
-
protected $inputIndex = 0;
|
68 |
-
protected $inputLength = 0;
|
69 |
-
protected $lookAhead = null;
|
70 |
-
protected $output = '';
|
71 |
-
protected $lastByteOut = '';
|
72 |
-
protected $keptComment = '';
|
73 |
-
|
74 |
-
/**
|
75 |
-
* Minify Javascript.
|
76 |
-
*
|
77 |
-
* @param string $js Javascript to be minified
|
78 |
-
*
|
79 |
-
* @return string
|
80 |
-
*/
|
81 |
-
public static function minify($js)
|
82 |
-
{
|
83 |
-
$jsmin = new JSMin($js);
|
84 |
-
return $jsmin->min();
|
85 |
-
}
|
86 |
-
|
87 |
-
/**
|
88 |
-
* @param string $input
|
89 |
-
*/
|
90 |
-
public function __construct($input)
|
91 |
-
{
|
92 |
-
$this->input = $input;
|
93 |
-
}
|
94 |
-
|
95 |
-
/**
|
96 |
-
* Perform minification, return result
|
97 |
-
*
|
98 |
-
* @return string
|
99 |
-
*/
|
100 |
-
public function min()
|
101 |
-
{
|
102 |
-
if ($this->output !== '') { // min already run
|
103 |
-
return $this->output;
|
104 |
-
}
|
105 |
-
|
106 |
-
$mbIntEnc = null;
|
107 |
-
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
|
108 |
-
$mbIntEnc = mb_internal_encoding();
|
109 |
-
mb_internal_encoding('8bit');
|
110 |
-
}
|
111 |
-
$this->input = str_replace("\r\n", "\n", $this->input);
|
112 |
-
$this->inputLength = strlen($this->input);
|
113 |
-
|
114 |
-
$this->action(self::ACTION_DELETE_A_B);
|
115 |
-
|
116 |
-
while ($this->a !== null) {
|
117 |
-
// determine next command
|
118 |
-
$command = self::ACTION_KEEP_A; // default
|
119 |
-
if ($this->a === ' ') {
|
120 |
-
if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
|
121 |
-
&& ($this->b === $this->lastByteOut)) {
|
122 |
-
// Don't delete this space. If we do, the addition/subtraction
|
123 |
-
// could be parsed as a post-increment
|
124 |
-
} elseif (! $this->isAlphaNum($this->b)) {
|
125 |
-
$command = self::ACTION_DELETE_A;
|
126 |
-
}
|
127 |
-
} elseif ($this->a === "\n") {
|
128 |
-
if ($this->b === ' ') {
|
129 |
-
$command = self::ACTION_DELETE_A_B;
|
130 |
-
|
131 |
-
// in case of mbstring.func_overload & 2, must check for null b,
|
132 |
-
// otherwise mb_strpos will give WARNING
|
133 |
-
} elseif ($this->b === null
|
134 |
-
|| (false === strpos('{[(+-!~', $this->b)
|
135 |
-
&& ! $this->isAlphaNum($this->b))) {
|
136 |
-
$command = self::ACTION_DELETE_A;
|
137 |
-
}
|
138 |
-
} elseif (! $this->isAlphaNum($this->a)) {
|
139 |
-
if ($this->b === ' '
|
140 |
-
|| ($this->b === "\n"
|
141 |
-
&& (false === strpos('}])+-"\'', $this->a)))) {
|
142 |
-
$command = self::ACTION_DELETE_A_B;
|
143 |
-
}
|
144 |
-
}
|
145 |
-
$this->action($command);
|
146 |
-
}
|
147 |
-
$this->output = trim($this->output);
|
148 |
-
|
149 |
-
if ($mbIntEnc !== null) {
|
150 |
-
mb_internal_encoding($mbIntEnc);
|
151 |
-
}
|
152 |
-
return $this->output;
|
153 |
-
}
|
154 |
-
|
155 |
-
/**
|
156 |
-
* ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
|
157 |
-
* ACTION_DELETE_A = Copy B to A. Get the next B.
|
158 |
-
* ACTION_DELETE_A_B = Get the next B.
|
159 |
-
*
|
160 |
-
* @param int $command
|
161 |
-
* @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
|
162 |
-
*/
|
163 |
-
protected function action($command)
|
164 |
-
{
|
165 |
-
// make sure we don't compress "a + ++b" to "a+++b", etc.
|
166 |
-
if ($command === self::ACTION_DELETE_A_B
|
167 |
-
&& $this->b === ' '
|
168 |
-
&& ($this->a === '+' || $this->a === '-')) {
|
169 |
-
// Note: we're at an addition/substraction operator; the inputIndex
|
170 |
-
// will certainly be a valid index
|
171 |
-
if ($this->input[$this->inputIndex] === $this->a) {
|
172 |
-
// This is "+ +" or "- -". Don't delete the space.
|
173 |
-
$command = self::ACTION_KEEP_A;
|
174 |
-
}
|
175 |
-
}
|
176 |
-
|
177 |
-
switch ($command) {
|
178 |
-
case self::ACTION_KEEP_A: // 1
|
179 |
-
$this->output .= $this->a;
|
180 |
-
|
181 |
-
if ($this->keptComment) {
|
182 |
-
$this->output = rtrim($this->output, "\n");
|
183 |
-
$this->output .= $this->keptComment;
|
184 |
-
$this->keptComment = '';
|
185 |
-
}
|
186 |
-
|
187 |
-
$this->lastByteOut = $this->a;
|
188 |
-
|
189 |
-
// fallthrough intentional
|
190 |
-
case self::ACTION_DELETE_A: // 2
|
191 |
-
$this->a = $this->b;
|
192 |
-
if ($this->a === "'" || $this->a === '"') { // string literal
|
193 |
-
$str = $this->a; // in case needed for exception
|
194 |
-
for(;;) {
|
195 |
-
$this->output .= $this->a;
|
196 |
-
$this->lastByteOut = $this->a;
|
197 |
-
|
198 |
-
$this->a = $this->get();
|
199 |
-
if ($this->a === $this->b) { // end quote
|
200 |
-
break;
|
201 |
-
}
|
202 |
-
if ($this->isEOF($this->a)) {
|
203 |
-
throw new JSMin_UnterminatedStringException(
|
204 |
-
"JSMin: Unterminated String at byte {$this->inputIndex}: {$str}");
|
205 |
-
}
|
206 |
-
$str .= $this->a;
|
207 |
-
if ($this->a === '\\') {
|
208 |
-
$this->output .= $this->a;
|
209 |
-
$this->lastByteOut = $this->a;
|
210 |
-
|
211 |
-
$this->a = $this->get();
|
212 |
-
$str .= $this->a;
|
213 |
-
}
|
214 |
-
}
|
215 |
-
}
|
216 |
-
|
217 |
-
// fallthrough intentional
|
218 |
-
case self::ACTION_DELETE_A_B: // 3
|
219 |
-
$this->b = $this->next();
|
220 |
-
if ($this->b === '/' && $this->isRegexpLiteral()) {
|
221 |
-
$this->output .= $this->a . $this->b;
|
222 |
-
$pattern = '/'; // keep entire pattern in case we need to report it in the exception
|
223 |
-
for(;;) {
|
224 |
-
$this->a = $this->get();
|
225 |
-
$pattern .= $this->a;
|
226 |
-
if ($this->a === '[') {
|
227 |
-
for(;;) {
|
228 |
-
$this->output .= $this->a;
|
229 |
-
$this->a = $this->get();
|
230 |
-
$pattern .= $this->a;
|
231 |
-
if ($this->a === ']') {
|
232 |
-
break;
|
233 |
-
}
|
234 |
-
if ($this->a === '\\') {
|
235 |
-
$this->output .= $this->a;
|
236 |
-
$this->a = $this->get();
|
237 |
-
$pattern .= $this->a;
|
238 |
-
}
|
239 |
-
if ($this->isEOF($this->a)) {
|
240 |
-
throw new JSMin_UnterminatedRegExpException(
|
241 |
-
"JSMin: Unterminated set in RegExp at byte "
|
242 |
-
. $this->inputIndex .": {$pattern}");
|
243 |
-
}
|
244 |
-
}
|
245 |
-
}
|
246 |
-
|
247 |
-
if ($this->a === '/') { // end pattern
|
248 |
-
break; // while (true)
|
249 |
-
} elseif ($this->a === '\\') {
|
250 |
-
$this->output .= $this->a;
|
251 |
-
$this->a = $this->get();
|
252 |
-
$pattern .= $this->a;
|
253 |
-
} elseif ($this->isEOF($this->a)) {
|
254 |
-
throw new JSMin_UnterminatedRegExpException(
|
255 |
-
"JSMin: Unterminated RegExp at byte {$this->inputIndex}: {$pattern}");
|
256 |
-
}
|
257 |
-
$this->output .= $this->a;
|
258 |
-
$this->lastByteOut = $this->a;
|
259 |
-
}
|
260 |
-
$this->b = $this->next();
|
261 |
-
}
|
262 |
-
// end case ACTION_DELETE_A_B
|
263 |
-
}
|
264 |
-
}
|
265 |
-
|
266 |
-
/**
|
267 |
-
* @return bool
|
268 |
-
*/
|
269 |
-
protected function isRegexpLiteral()
|
270 |
-
{
|
271 |
-
if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
|
272 |
-
// we obviously aren't dividing
|
273 |
-
return true;
|
274 |
-
}
|
275 |
-
|
276 |
-
// we have to check for a preceding keyword, and we don't need to pattern
|
277 |
-
// match over the whole output.
|
278 |
-
$recentOutput = substr($this->output, -10);
|
279 |
-
|
280 |
-
// check if return/typeof directly precede a pattern without a space
|
281 |
-
foreach (array('return', 'typeof') as $keyword) {
|
282 |
-
if ($this->a !== substr($keyword, -1)) {
|
283 |
-
// certainly wasn't keyword
|
284 |
-
continue;
|
285 |
-
}
|
286 |
-
if (preg_match("~(^|[\\s\\S])" . substr($keyword, 0, -1) . "$~", $recentOutput, $m)) {
|
287 |
-
if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
|
288 |
-
return true;
|
289 |
-
}
|
290 |
-
}
|
291 |
-
}
|
292 |
-
|
293 |
-
// check all keywords
|
294 |
-
if ($this->a === ' ' || $this->a === "\n") {
|
295 |
-
if (preg_match('~(^|[\\s\\S])(?:case|else|in|return|typeof)$~', $recentOutput, $m)) {
|
296 |
-
if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
|
297 |
-
return true;
|
298 |
-
}
|
299 |
-
}
|
300 |
-
}
|
301 |
-
|
302 |
-
return false;
|
303 |
-
}
|
304 |
-
|
305 |
-
/**
|
306 |
-
* Return the next character from stdin. Watch out for lookahead. If the character is a control character,
|
307 |
-
* translate it to a space or linefeed.
|
308 |
-
*
|
309 |
-
* @return string
|
310 |
-
*/
|
311 |
-
protected function get()
|
312 |
-
{
|
313 |
-
$c = $this->lookAhead;
|
314 |
-
$this->lookAhead = null;
|
315 |
-
if ($c === null) {
|
316 |
-
// getc(stdin)
|
317 |
-
if ($this->inputIndex < $this->inputLength) {
|
318 |
-
$c = $this->input[$this->inputIndex];
|
319 |
-
$this->inputIndex += 1;
|
320 |
-
} else {
|
321 |
-
$c = null;
|
322 |
-
}
|
323 |
-
}
|
324 |
-
if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
|
325 |
-
return $c;
|
326 |
-
}
|
327 |
-
if ($c === "\r") {
|
328 |
-
return "\n";
|
329 |
-
}
|
330 |
-
return ' ';
|
331 |
-
}
|
332 |
-
|
333 |
-
/**
|
334 |
-
* Does $a indicate end of input?
|
335 |
-
*
|
336 |
-
* @param string $a
|
337 |
-
* @return bool
|
338 |
-
*/
|
339 |
-
protected function isEOF($a)
|
340 |
-
{
|
341 |
-
return ord($a) <= self::ORD_LF;
|
342 |
-
}
|
343 |
-
|
344 |
-
/**
|
345 |
-
* Get next char (without getting it). If is ctrl character, translate to a space or newline.
|
346 |
-
*
|
347 |
-
* @return string
|
348 |
-
*/
|
349 |
-
protected function peek()
|
350 |
-
{
|
351 |
-
$this->lookAhead = $this->get();
|
352 |
-
return $this->lookAhead;
|
353 |
-
}
|
354 |
-
|
355 |
-
/**
|
356 |
-
* Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
|
357 |
-
*
|
358 |
-
* @param string $c
|
359 |
-
*
|
360 |
-
* @return bool
|
361 |
-
*/
|
362 |
-
protected function isAlphaNum($c)
|
363 |
-
{
|
364 |
-
return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
|
365 |
-
}
|
366 |
-
|
367 |
-
/**
|
368 |
-
* Consume a single line comment from input (possibly retaining it)
|
369 |
-
*/
|
370 |
-
protected function consumeSingleLineComment()
|
371 |
-
{
|
372 |
-
$comment = '';
|
373 |
-
while (true) {
|
374 |
-
$get = $this->get();
|
375 |
-
$comment .= $get;
|
376 |
-
if (ord($get) <= self::ORD_LF) { // end of line reached
|
377 |
-
// if IE conditional comment
|
378 |
-
if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
|
379 |
-
$this->keptComment .= "/{$comment}";
|
380 |
-
}
|
381 |
-
return;
|
382 |
-
}
|
383 |
-
}
|
384 |
-
}
|
385 |
-
|
386 |
-
/**
|
387 |
-
* Consume a multiple line comment from input (possibly retaining it)
|
388 |
-
*
|
389 |
-
* @throws JSMin_UnterminatedCommentException
|
390 |
-
*/
|
391 |
-
protected function consumeMultipleLineComment()
|
392 |
-
{
|
393 |
-
$this->get();
|
394 |
-
$comment = '';
|
395 |
-
for(;;) {
|
396 |
-
$get = $this->get();
|
397 |
-
if ($get === '*') {
|
398 |
-
if ($this->peek() === '/') { // end of comment reached
|
399 |
-
$this->get();
|
400 |
-
if (0 === strpos($comment, '!')) {
|
401 |
-
// preserved by YUI Compressor
|
402 |
-
if (!$this->keptComment) {
|
403 |
-
// don't prepend a newline if two comments right after one another
|
404 |
-
$this->keptComment = "\n";
|
405 |
-
}
|
406 |
-
$this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
|
407 |
-
} else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
|
408 |
-
// IE conditional
|
409 |
-
$this->keptComment .= "/*{$comment}*/";
|
410 |
-
}
|
411 |
-
return;
|
412 |
-
}
|
413 |
-
} elseif ($get === null) {
|
414 |
-
throw new JSMin_UnterminatedCommentException(
|
415 |
-
"JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
|
416 |
-
}
|
417 |
-
$comment .= $get;
|
418 |
-
}
|
419 |
-
}
|
420 |
-
|
421 |
-
/**
|
422 |
-
* Get the next character, skipping over comments. Some comments may be preserved.
|
423 |
-
*
|
424 |
-
* @return string
|
425 |
-
*/
|
426 |
-
protected function next()
|
427 |
-
{
|
428 |
-
$get = $this->get();
|
429 |
-
if ($get === '/') {
|
430 |
-
switch ($this->peek()) {
|
431 |
-
case '/':
|
432 |
-
$this->consumeSingleLineComment();
|
433 |
-
$get = "\n";
|
434 |
-
break;
|
435 |
-
case '*':
|
436 |
-
$this->consumeMultipleLineComment();
|
437 |
-
$get = ' ';
|
438 |
-
break;
|
439 |
-
}
|
440 |
-
}
|
441 |
-
return $get;
|
442 |
-
}
|
443 |
-
}
|
444 |
-
|
445 |
-
class JSMin_UnterminatedStringException extends Exception {}
|
446 |
-
class JSMin_UnterminatedCommentException extends Exception {}
|
447 |
-
class JSMin_UnterminatedRegExpException extends Exception {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/external/php/minify-css-compressor.php
DELETED
@@ -1,250 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Class Minify_CSS_Compressor
|
4 |
-
* @package Minify
|
5 |
-
*/
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Compress CSS
|
9 |
-
*
|
10 |
-
* This is a heavy regex-based removal of whitespace, unnecessary
|
11 |
-
* comments and tokens, and some CSS value minimization, where practical.
|
12 |
-
* Many steps have been taken to avoid breaking comment-based hacks,
|
13 |
-
* including the ie5/mac filter (and its inversion), but expect tricky
|
14 |
-
* hacks involving comment tokens in 'content' value strings to break
|
15 |
-
* minimization badly. A test suite is available.
|
16 |
-
*
|
17 |
-
* @package Minify
|
18 |
-
* @author Stephen Clay <steve@mrclay.org>
|
19 |
-
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
20 |
-
*/
|
21 |
-
class Minify_CSS_Compressor {
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Minify a CSS string
|
25 |
-
*
|
26 |
-
* @param string $css
|
27 |
-
*
|
28 |
-
* @param array $options (currently ignored)
|
29 |
-
*
|
30 |
-
* @return string
|
31 |
-
*/
|
32 |
-
public static function process($css, $options = array())
|
33 |
-
{
|
34 |
-
$obj = new Minify_CSS_Compressor($options);
|
35 |
-
return $obj->_process($css);
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* @var array options
|
40 |
-
*/
|
41 |
-
protected $_options = null;
|
42 |
-
|
43 |
-
/**
|
44 |
-
* @var bool Are we "in" a hack?
|
45 |
-
*
|
46 |
-
* I.e. are some browsers targetted until the next comment?
|
47 |
-
*/
|
48 |
-
protected $_inHack = false;
|
49 |
-
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Constructor
|
53 |
-
*
|
54 |
-
* @param array $options (currently ignored)
|
55 |
-
*
|
56 |
-
* @return null
|
57 |
-
*/
|
58 |
-
private function __construct($options) {
|
59 |
-
$this->_options = $options;
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* Minify a CSS string
|
64 |
-
*
|
65 |
-
* @param string $css
|
66 |
-
*
|
67 |
-
* @return string
|
68 |
-
*/
|
69 |
-
protected function _process($css)
|
70 |
-
{
|
71 |
-
$css = str_replace("\r\n", "\n", $css);
|
72 |
-
|
73 |
-
// preserve empty comment after '>'
|
74 |
-
// http://www.webdevout.net/css-hacks#in_css-selectors
|
75 |
-
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
|
76 |
-
|
77 |
-
// preserve empty comment between property and value
|
78 |
-
// http://css-discuss.incutio.com/?page=BoxModelHack
|
79 |
-
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
|
80 |
-
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
|
81 |
-
|
82 |
-
// apply callback to all valid comments (and strip out surrounding ws
|
83 |
-
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
|
84 |
-
,array($this, '_commentCB'), $css);
|
85 |
-
|
86 |
-
// remove ws around { } and last semicolon in declaration block
|
87 |
-
$css = preg_replace('/\\s*{\\s*/', '{', $css);
|
88 |
-
$css = preg_replace('/;?\\s*}\\s*/', '}', $css);
|
89 |
-
|
90 |
-
// remove ws surrounding semicolons
|
91 |
-
$css = preg_replace('/\\s*;\\s*/', ';', $css);
|
92 |
-
|
93 |
-
// remove ws around urls
|
94 |
-
$css = preg_replace('/
|
95 |
-
url\\( # url(
|
96 |
-
\\s*
|
97 |
-
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
|
98 |
-
\\s*
|
99 |
-
\\) # )
|
100 |
-
/x', 'url($1)', $css);
|
101 |
-
|
102 |
-
// remove ws between rules and colons
|
103 |
-
$css = preg_replace('/
|
104 |
-
\\s*
|
105 |
-
([{;]) # 1 = beginning of block or rule separator
|
106 |
-
\\s*
|
107 |
-
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
|
108 |
-
\\s*
|
109 |
-
:
|
110 |
-
\\s*
|
111 |
-
(\\b|[#\'"]) # 3 = first character of a value
|
112 |
-
/x', '$1$2:$3', $css);
|
113 |
-
|
114 |
-
// remove ws in selectors
|
115 |
-
$css = preg_replace_callback('/
|
116 |
-
(?: # non-capture
|
117 |
-
\\s*
|
118 |
-
[^~>+,\\s]+ # selector part
|
119 |
-
\\s*
|
120 |
-
[,>+~] # combinators
|
121 |
-
)+
|
122 |
-
\\s*
|
123 |
-
[^~>+,\\s]+ # selector part
|
124 |
-
{ # open declaration block
|
125 |
-
/x'
|
126 |
-
,array($this, '_selectorsCB'), $css);
|
127 |
-
|
128 |
-
// minimize hex colors
|
129 |
-
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
|
130 |
-
, '$1#$2$3$4$5', $css);
|
131 |
-
|
132 |
-
// remove spaces between font families
|
133 |
-
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
|
134 |
-
,array($this, '_fontFamilyCB'), $css);
|
135 |
-
|
136 |
-
$css = preg_replace('/@import\\s+url/', '@import url', $css);
|
137 |
-
|
138 |
-
// replace any ws involving newlines with a single newline
|
139 |
-
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
|
140 |
-
|
141 |
-
// separate common descendent selectors w/ newlines (to limit line lengths)
|
142 |
-
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
|
143 |
-
|
144 |
-
// Use newline after 1st numeric value (to limit line lengths).
|
145 |
-
$css = preg_replace('/
|
146 |
-
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
|
147 |
-
\\s+
|
148 |
-
/x'
|
149 |
-
,"$1\n", $css);
|
150 |
-
|
151 |
-
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
|
152 |
-
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
|
153 |
-
|
154 |
-
return trim($css);
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Replace what looks like a set of selectors
|
159 |
-
*
|
160 |
-
* @param array $m regex matches
|
161 |
-
*
|
162 |
-
* @return string
|
163 |
-
*/
|
164 |
-
protected function _selectorsCB($m)
|
165 |
-
{
|
166 |
-
// remove ws around the combinators
|
167 |
-
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
|
168 |
-
}
|
169 |
-
|
170 |
-
/**
|
171 |
-
* Process a comment and return a replacement
|
172 |
-
*
|
173 |
-
* @param array $m regex matches
|
174 |
-
*
|
175 |
-
* @return string
|
176 |
-
*/
|
177 |
-
protected function _commentCB($m)
|
178 |
-
{
|
179 |
-
$hasSurroundingWs = (trim($m[0]) !== $m[1]);
|
180 |
-
$m = $m[1];
|
181 |
-
// $m is the comment content w/o the surrounding tokens,
|
182 |
-
// but the return value will replace the entire comment.
|
183 |
-
if ($m === 'keep') {
|
184 |
-
return '/**/';
|
185 |
-
}
|
186 |
-
if ($m === '" "') {
|
187 |
-
// component of http://tantek.com/CSS/Examples/midpass.html
|
188 |
-
return '/*" "*/';
|
189 |
-
}
|
190 |
-
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
|
191 |
-
// component of http://tantek.com/CSS/Examples/midpass.html
|
192 |
-
return '/*";}}/* */';
|
193 |
-
}
|
194 |
-
if ($this->_inHack) {
|
195 |
-
// inversion: feeding only to one browser
|
196 |
-
if (preg_match('@
|
197 |
-
^/ # comment started like /*/
|
198 |
-
\\s*
|
199 |
-
(\\S[\\s\\S]+?) # has at least some non-ws content
|
200 |
-
\\s*
|
201 |
-
/\\* # ends like /*/ or /**/
|
202 |
-
@x', $m, $n)) {
|
203 |
-
// end hack mode after this comment, but preserve the hack and comment content
|
204 |
-
$this->_inHack = false;
|
205 |
-
return "/*/{$n[1]}/**/";
|
206 |
-
}
|
207 |
-
}
|
208 |
-
if (substr($m, -1) === '\\') { // comment ends like \*/
|
209 |
-
// begin hack mode and preserve hack
|
210 |
-
$this->_inHack = true;
|
211 |
-
return '/*\\*/';
|
212 |
-
}
|
213 |
-
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
|
214 |
-
// begin hack mode and preserve hack
|
215 |
-
$this->_inHack = true;
|
216 |
-
return '/*/*/';
|
217 |
-
}
|
218 |
-
if ($this->_inHack) {
|
219 |
-
// a regular comment ends hack mode but should be preserved
|
220 |
-
$this->_inHack = false;
|
221 |
-
return '/**/';
|
222 |
-
}
|
223 |
-
// Issue 107: if there's any surrounding whitespace, it may be important, so
|
224 |
-
// replace the comment with a single space
|
225 |
-
return $hasSurroundingWs // remove all other comments
|
226 |
-
? ' '
|
227 |
-
: '';
|
228 |
-
}
|
229 |
-
|
230 |
-
/**
|
231 |
-
* Process a font-family listing and return a replacement
|
232 |
-
*
|
233 |
-
* @param array $m regex matches
|
234 |
-
*
|
235 |
-
* @return string
|
236 |
-
*/
|
237 |
-
protected function _fontFamilyCB($m)
|
238 |
-
{
|
239 |
-
$m[1] = preg_replace('/
|
240 |
-
\\s*
|
241 |
-
(
|
242 |
-
"[^"]+" # 1 = family in double qutoes
|
243 |
-
|\'[^\']+\' # or 1 = family in single quotes
|
244 |
-
|[\\w\\-]+ # or 1 = unquoted family
|
245 |
-
)
|
246 |
-
\\s*
|
247 |
-
/x', '$1', $m[1]);
|
248 |
-
return 'font-family:' . $m[1] . $m[2];
|
249 |
-
}
|
250 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/external/php/minify-html.php
CHANGED
@@ -1,18 +1,18 @@
|
|
1 |
<?php
|
2 |
/**
|
3 |
-
* Class Minify_HTML
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Compress HTML
|
9 |
*
|
10 |
-
* This is a heavy regex-based removal of whitespace, unnecessary comments and
|
11 |
* tokens. IE conditional comments are preserved. There are also options to have
|
12 |
-
* STYLE and SCRIPT blocks compressed by callback functions.
|
13 |
-
*
|
14 |
* A test suite is available.
|
15 |
-
*
|
16 |
* @package Minify
|
17 |
* @author Stephen Clay <steve@mrclay.org>
|
18 |
*/
|
@@ -27,24 +27,24 @@ class Minify_HTML {
|
|
27 |
*
|
28 |
* 'cssMinifier' : (optional) callback function to process content of STYLE
|
29 |
* elements.
|
30 |
-
*
|
31 |
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
|
32 |
* elements. Note: the type attribute is ignored.
|
33 |
-
*
|
34 |
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
35 |
* unset, minify will sniff for an XHTML doctype.
|
36 |
-
|
37 |
* 'keepComments' : (optional boolean) should the HTML comments be kept
|
38 |
* in the HTML Code?
|
39 |
-
*
|
40 |
* @return string
|
41 |
*/
|
42 |
public static function minify($html, $options = array()) {
|
43 |
$min = new Minify_HTML($html, $options);
|
44 |
return $min->process();
|
45 |
}
|
46 |
-
|
47 |
-
|
48 |
/**
|
49 |
* Create a minifier object
|
50 |
*
|
@@ -54,16 +54,16 @@ class Minify_HTML {
|
|
54 |
*
|
55 |
* 'cssMinifier' : (optional) callback function to process content of STYLE
|
56 |
* elements.
|
57 |
-
*
|
58 |
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
|
59 |
* elements. Note: the type attribute is ignored.
|
60 |
-
*
|
61 |
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
62 |
* unset, minify will sniff for an XHTML doctype.
|
63 |
-
|
64 |
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
65 |
* unset, minify will sniff for an XHTML doctype.
|
66 |
-
*
|
67 |
* @return null
|
68 |
*/
|
69 |
public function __construct($html, $options = array())
|
@@ -82,11 +82,11 @@ class Minify_HTML {
|
|
82 |
$this->_keepComments = $options['keepComments'];
|
83 |
}
|
84 |
}
|
85 |
-
|
86 |
-
|
87 |
/**
|
88 |
* Minify the markeup given in the constructor
|
89 |
-
*
|
90 |
* @return string
|
91 |
*/
|
92 |
public function process()
|
@@ -94,67 +94,67 @@ class Minify_HTML {
|
|
94 |
if ($this->_isXhtml === null) {
|
95 |
$this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
|
96 |
}
|
97 |
-
|
98 |
$this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
|
99 |
$this->_placeholders = array();
|
100 |
-
|
101 |
// replace SCRIPTs (and minify) with placeholders
|
102 |
$this->_html = preg_replace_callback(
|
103 |
'/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
|
104 |
,array($this, '_removeScriptCB')
|
105 |
,$this->_html);
|
106 |
-
|
107 |
// replace STYLEs (and minify) with placeholders
|
108 |
$this->_html = preg_replace_callback(
|
109 |
'/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i'
|
110 |
,array($this, '_removeStyleCB')
|
111 |
,$this->_html);
|
112 |
-
|
113 |
// remove HTML comments (not containing IE conditional comments).
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
}
|
120 |
-
|
121 |
// replace PREs with placeholders
|
122 |
$this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
|
123 |
,array($this, '_removePreCB')
|
124 |
,$this->_html);
|
125 |
-
|
126 |
// replace TEXTAREAs with placeholders
|
127 |
$this->_html = preg_replace_callback(
|
128 |
'/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
|
129 |
,array($this, '_removeTextareaCB')
|
130 |
,$this->_html);
|
131 |
-
|
132 |
-
|
133 |
$this->_html = preg_replace_callback(
|
134 |
'/(=("|\')data:.*\\2)/Ui'
|
135 |
,array($this, '_removeDataURICB')
|
136 |
,$this->_html);
|
137 |
-
|
138 |
// trim each line.
|
139 |
// replace by space instead of '' to avoid newline after opening tag getting zapped
|
140 |
$this->_html = preg_replace('/^\s+|\s+$/m', ' ', $this->_html);
|
141 |
-
|
142 |
// remove ws around block/undisplayed elements
|
143 |
$this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body'
|
144 |
.'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form'
|
145 |
.'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav'
|
146 |
.'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)'
|
147 |
.'|ul|video)\\b[^>]*>)/i', '$1', $this->_html);
|
148 |
-
|
149 |
// remove ws outside of all elements
|
150 |
$this->_html = preg_replace_callback(
|
151 |
'/>([^<]+)</'
|
152 |
,array($this, '_outsideTagCB')
|
153 |
,$this->_html);
|
154 |
-
|
155 |
// use newlines before 1st attribute in open tags (to limit line lengths)
|
156 |
//$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
|
157 |
-
|
158 |
// fill placeholders
|
159 |
$this->_html = str_replace(
|
160 |
array_keys($this->_placeholders)
|
@@ -163,14 +163,14 @@ class Minify_HTML {
|
|
163 |
);
|
164 |
return $this->_html;
|
165 |
}
|
166 |
-
|
167 |
protected function _commentCB($m)
|
168 |
{
|
169 |
return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
|
170 |
? $m[0]
|
171 |
: '';
|
172 |
}
|
173 |
-
|
174 |
protected function _reservePlace($content)
|
175 |
{
|
176 |
$placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
|
@@ -183,24 +183,24 @@ class Minify_HTML {
|
|
183 |
protected $_placeholders = array();
|
184 |
protected $_cssMinifier = null;
|
185 |
protected $_jsMinifier = null;
|
186 |
-
|
187 |
|
188 |
protected function _outsideTagCB($m)
|
189 |
{
|
190 |
return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
|
191 |
}
|
192 |
-
|
193 |
protected function _removePreCB($m)
|
194 |
{
|
195 |
return $this->_reservePlace($m[1]);
|
196 |
}
|
197 |
-
|
198 |
protected function _removeTextareaCB($m)
|
199 |
{
|
200 |
return $this->_reservePlace($m[1]);
|
201 |
}
|
202 |
-
|
203 |
-
|
204 |
{
|
205 |
return $this->_reservePlace($m[1]);
|
206 |
}
|
@@ -211,16 +211,16 @@ class Minify_HTML {
|
|
211 |
$css = $m[2];
|
212 |
// remove HTML comments
|
213 |
$css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
|
214 |
-
|
215 |
// remove CDATA section markers
|
216 |
$css = $this->_removeCdata($css);
|
217 |
-
|
218 |
// minify
|
219 |
$minifier = $this->_cssMinifier
|
220 |
? $this->_cssMinifier
|
221 |
: 'trim';
|
222 |
$css = call_user_func($minifier, $css);
|
223 |
-
|
224 |
return $this->_reservePlace($this->_needsCdata($css)
|
225 |
? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
|
226 |
: "{$openStyle}{$css}</style>"
|
@@ -231,23 +231,25 @@ class Minify_HTML {
|
|
231 |
{
|
232 |
$openScript = $m[2];
|
233 |
$js = $m[3];
|
234 |
-
|
235 |
// whitespace surrounding? preserve at least one space
|
236 |
$ws1 = ($m[1] === '') ? '' : ' ';
|
237 |
$ws2 = ($m[4] === '') ? '' : ' ';
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
|
|
|
|
245 |
// minify
|
246 |
$minifier = $this->_jsMinifier
|
247 |
? $this->_jsMinifier
|
248 |
-
: 'trim';
|
249 |
$js = call_user_func($minifier, $js);
|
250 |
-
|
251 |
return $this->_reservePlace($this->_needsCdata($js)
|
252 |
? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
|
253 |
: "{$ws1}{$openScript}{$js}</script>{$ws2}"
|
@@ -260,7 +262,7 @@ class Minify_HTML {
|
|
260 |
? str_replace(array('/* <![CDATA[ */','/* ]]> */','/*<![CDATA[*/','/*]]>*/','<![CDATA[', ']]>'), '', $str)
|
261 |
: $str;
|
262 |
}
|
263 |
-
|
264 |
protected function _needsCdata($str)
|
265 |
{
|
266 |
return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
|
1 |
<?php
|
2 |
/**
|
3 |
+
* Class Minify_HTML
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Compress HTML
|
9 |
*
|
10 |
+
* This is a heavy regex-based removal of whitespace, unnecessary comments and
|
11 |
* tokens. IE conditional comments are preserved. There are also options to have
|
12 |
+
* STYLE and SCRIPT blocks compressed by callback functions.
|
13 |
+
*
|
14 |
* A test suite is available.
|
15 |
+
*
|
16 |
* @package Minify
|
17 |
* @author Stephen Clay <steve@mrclay.org>
|
18 |
*/
|
27 |
*
|
28 |
* 'cssMinifier' : (optional) callback function to process content of STYLE
|
29 |
* elements.
|
30 |
+
*
|
31 |
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
|
32 |
* elements. Note: the type attribute is ignored.
|
33 |
+
*
|
34 |
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
35 |
* unset, minify will sniff for an XHTML doctype.
|
36 |
+
*
|
37 |
* 'keepComments' : (optional boolean) should the HTML comments be kept
|
38 |
* in the HTML Code?
|
39 |
+
*
|
40 |
* @return string
|
41 |
*/
|
42 |
public static function minify($html, $options = array()) {
|
43 |
$min = new Minify_HTML($html, $options);
|
44 |
return $min->process();
|
45 |
}
|
46 |
+
|
47 |
+
|
48 |
/**
|
49 |
* Create a minifier object
|
50 |
*
|
54 |
*
|
55 |
* 'cssMinifier' : (optional) callback function to process content of STYLE
|
56 |
* elements.
|
57 |
+
*
|
58 |
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
|
59 |
* elements. Note: the type attribute is ignored.
|
60 |
+
*
|
61 |
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
62 |
* unset, minify will sniff for an XHTML doctype.
|
63 |
+
*
|
64 |
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
65 |
* unset, minify will sniff for an XHTML doctype.
|
66 |
+
*
|
67 |
* @return null
|
68 |
*/
|
69 |
public function __construct($html, $options = array())
|
82 |
$this->_keepComments = $options['keepComments'];
|
83 |
}
|
84 |
}
|
85 |
+
|
86 |
+
|
87 |
/**
|
88 |
* Minify the markeup given in the constructor
|
89 |
+
*
|
90 |
* @return string
|
91 |
*/
|
92 |
public function process()
|
94 |
if ($this->_isXhtml === null) {
|
95 |
$this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
|
96 |
}
|
97 |
+
|
98 |
$this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
|
99 |
$this->_placeholders = array();
|
100 |
+
|
101 |
// replace SCRIPTs (and minify) with placeholders
|
102 |
$this->_html = preg_replace_callback(
|
103 |
'/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
|
104 |
,array($this, '_removeScriptCB')
|
105 |
,$this->_html);
|
106 |
+
|
107 |
// replace STYLEs (and minify) with placeholders
|
108 |
$this->_html = preg_replace_callback(
|
109 |
'/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i'
|
110 |
,array($this, '_removeStyleCB')
|
111 |
,$this->_html);
|
112 |
+
|
113 |
// remove HTML comments (not containing IE conditional comments).
|
114 |
+
if ($this->_keepComments == false) {
|
115 |
+
$this->_html = preg_replace_callback(
|
116 |
+
'/<!--([\\s\\S]*?)-->/'
|
117 |
+
,array($this, '_commentCB')
|
118 |
+
,$this->_html);
|
119 |
}
|
120 |
+
|
121 |
// replace PREs with placeholders
|
122 |
$this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
|
123 |
,array($this, '_removePreCB')
|
124 |
,$this->_html);
|
125 |
+
|
126 |
// replace TEXTAREAs with placeholders
|
127 |
$this->_html = preg_replace_callback(
|
128 |
'/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
|
129 |
,array($this, '_removeTextareaCB')
|
130 |
,$this->_html);
|
131 |
+
|
132 |
+
// replace data: URIs with placeholders
|
133 |
$this->_html = preg_replace_callback(
|
134 |
'/(=("|\')data:.*\\2)/Ui'
|
135 |
,array($this, '_removeDataURICB')
|
136 |
,$this->_html);
|
137 |
+
|
138 |
// trim each line.
|
139 |
// replace by space instead of '' to avoid newline after opening tag getting zapped
|
140 |
$this->_html = preg_replace('/^\s+|\s+$/m', ' ', $this->_html);
|
141 |
+
|
142 |
// remove ws around block/undisplayed elements
|
143 |
$this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body'
|
144 |
.'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form'
|
145 |
.'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav'
|
146 |
.'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)'
|
147 |
.'|ul|video)\\b[^>]*>)/i', '$1', $this->_html);
|
148 |
+
|
149 |
// remove ws outside of all elements
|
150 |
$this->_html = preg_replace_callback(
|
151 |
'/>([^<]+)</'
|
152 |
,array($this, '_outsideTagCB')
|
153 |
,$this->_html);
|
154 |
+
|
155 |
// use newlines before 1st attribute in open tags (to limit line lengths)
|
156 |
//$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
|
157 |
+
|
158 |
// fill placeholders
|
159 |
$this->_html = str_replace(
|
160 |
array_keys($this->_placeholders)
|
163 |
);
|
164 |
return $this->_html;
|
165 |
}
|
166 |
+
|
167 |
protected function _commentCB($m)
|
168 |
{
|
169 |
return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
|
170 |
? $m[0]
|
171 |
: '';
|
172 |
}
|
173 |
+
|
174 |
protected function _reservePlace($content)
|
175 |
{
|
176 |
$placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
|
183 |
protected $_placeholders = array();
|
184 |
protected $_cssMinifier = null;
|
185 |
protected $_jsMinifier = null;
|
186 |
+
protected $_keepComments = false;
|
187 |
|
188 |
protected function _outsideTagCB($m)
|
189 |
{
|
190 |
return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
|
191 |
}
|
192 |
+
|
193 |
protected function _removePreCB($m)
|
194 |
{
|
195 |
return $this->_reservePlace($m[1]);
|
196 |
}
|
197 |
+
|
198 |
protected function _removeTextareaCB($m)
|
199 |
{
|
200 |
return $this->_reservePlace($m[1]);
|
201 |
}
|
202 |
+
|
203 |
+
protected function _removeDataURICB($m)
|
204 |
{
|
205 |
return $this->_reservePlace($m[1]);
|
206 |
}
|
211 |
$css = $m[2];
|
212 |
// remove HTML comments
|
213 |
$css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
|
214 |
+
|
215 |
// remove CDATA section markers
|
216 |
$css = $this->_removeCdata($css);
|
217 |
+
|
218 |
// minify
|
219 |
$minifier = $this->_cssMinifier
|
220 |
? $this->_cssMinifier
|
221 |
: 'trim';
|
222 |
$css = call_user_func($minifier, $css);
|
223 |
+
|
224 |
return $this->_reservePlace($this->_needsCdata($css)
|
225 |
? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
|
226 |
: "{$openStyle}{$css}</style>"
|
231 |
{
|
232 |
$openScript = $m[2];
|
233 |
$js = $m[3];
|
234 |
+
|
235 |
// whitespace surrounding? preserve at least one space
|
236 |
$ws1 = ($m[1] === '') ? '' : ' ';
|
237 |
$ws2 = ($m[4] === '') ? '' : ' ';
|
238 |
+
|
239 |
+
if ($this->_keepComments == false) {
|
240 |
+
// remove HTML comments (and ending "//" if present)
|
241 |
+
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
|
242 |
+
|
243 |
+
// remove CDATA section markers
|
244 |
+
$js = $this->_removeCdata($js);
|
245 |
+
}
|
246 |
+
|
247 |
// minify
|
248 |
$minifier = $this->_jsMinifier
|
249 |
? $this->_jsMinifier
|
250 |
+
: 'trim';
|
251 |
$js = call_user_func($minifier, $js);
|
252 |
+
|
253 |
return $this->_reservePlace($this->_needsCdata($js)
|
254 |
? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
|
255 |
: "{$ws1}{$openScript}{$js}</script>{$ws2}"
|
262 |
? str_replace(array('/* <![CDATA[ */','/* ]]> */','/*<![CDATA[*/','/*]]>*/','<![CDATA[', ']]>'), '', $str)
|
263 |
: $str;
|
264 |
}
|
265 |
+
|
266 |
protected function _needsCdata($str)
|
267 |
{
|
268 |
return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
|
classes/external/php/persist-admin-notices-dismissal/README.md
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Persist Admin notice Dismissals
|
2 |
+
[![Latest Stable Version](https://poser.pugx.org/collizo4sky/persist-admin-notices-dismissal/v/stable)](https://packagist.org/packages/collizo4sky/persist-admin-notices-dismissal)
|
3 |
+
[![Total Downloads](https://poser.pugx.org/collizo4sky/persist-admin-notices-dismissal/downloads)](https://packagist.org/packages/collizo4sky/persist-admin-notices-dismissal)
|
4 |
+
|
5 |
+
Simple framework library that persists the dismissal of admin notices across pages in WordPress dashboard.
|
6 |
+
|
7 |
+
## Installation
|
8 |
+
|
9 |
+
Run `composer require collizo4sky/persist-admin-notices-dismissal`
|
10 |
+
|
11 |
+
Alternatively, clone or download this repo into the `vendor/` folder in your plugin, and include/require the `persist-admin-notices-dismissal.php` file like so
|
12 |
+
|
13 |
+
```php
|
14 |
+
require __DIR__ . '/vendor/persist-admin-notices-dismissal/persist-admin-notices-dismissal.php';
|
15 |
+
add_action( 'admin_init', array( 'PAnD', 'init' ) );
|
16 |
+
```
|
17 |
+
|
18 |
+
or let Composer's autoloader do the work.
|
19 |
+
|
20 |
+
## How to Use
|
21 |
+
Firstly, install and activate this library within a plugin.
|
22 |
+
|
23 |
+
Say you have the following markup as your admin notice,
|
24 |
+
|
25 |
+
|
26 |
+
```php
|
27 |
+
function sample_admin_notice__success() {
|
28 |
+
?>
|
29 |
+
<div class="updated notice notice-success is-dismissible">
|
30 |
+
<p><?php _e( 'Done!', 'sample-text-domain' ); ?></p>
|
31 |
+
</div>
|
32 |
+
<?php
|
33 |
+
}
|
34 |
+
add_action( 'admin_notices', 'sample_admin_notice__success' );
|
35 |
+
```
|
36 |
+
|
37 |
+
To make it hidden forever when dismissed, add the following data attribute `data-dismissible="disable-done-notice-forever"` to the div markup like so:
|
38 |
+
|
39 |
+
|
40 |
+
```php
|
41 |
+
function sample_admin_notice__success() {
|
42 |
+
if ( ! PAnD::is_admin_notice_active( 'disable-done-notice-forever' ) ) {
|
43 |
+
return;
|
44 |
+
}
|
45 |
+
|
46 |
+
?>
|
47 |
+
<div data-dismissible="disable-done-notice-forever" class="updated notice notice-success is-dismissible">
|
48 |
+
<p><?php _e( 'Done!', 'sample-text-domain' ); ?></p>
|
49 |
+
</div>
|
50 |
+
<?php
|
51 |
+
}
|
52 |
+
add_action( 'admin_init', array( 'PAnD', 'init' ) );
|
53 |
+
add_action( 'admin_notices', 'sample_admin_notice__success' );
|
54 |
+
```
|
55 |
+
|
56 |
+
## Autoloaders
|
57 |
+
When using the framework with an autoloader you **must** also load the class outside of the `admin_notices` or `network_admin_notices` hooks. The reason is that these hooks come after the `admin_enqueue_script` hook that loads the javascript.
|
58 |
+
|
59 |
+
Just add the following in your main plugin file.
|
60 |
+
|
61 |
+
```php
|
62 |
+
add_action( 'admin_init', array( 'PAnD', 'init' ) );
|
63 |
+
```
|
64 |
+
|
65 |
+
#### Usage Instructions and Examples
|
66 |
+
If you have two notices displayed when certain actions are triggered; firstly, choose a string to uniquely identify them, e.g. `notice-one` and `notice-two`
|
67 |
+
|
68 |
+
To make the first notice never appear once dismissed, its `data-dismissible` attribute will be `data-dismissible="notice-one-forever"` where `notice-one` is its unique identifier and `forever` is the dismissal time period.
|
69 |
+
|
70 |
+
To make the second notice only hidden for 2 days, its `data-dismissible` attribute will be `data-dismissible="notice-two-2"` where `notice-two` is its unique identifier and the `2`, the number of days it will be hidden is the dismissal time period.
|
71 |
+
|
72 |
+
You **must** append the dismissal time period to the end of your unique identifier with a hyphen (`-`) and this value must be an integer. The only exception is the string `forever`.
|
73 |
+
|
74 |
+
To actually make the dismissed admin notice not to appear, use the `is_admin_notice_active()` function like so:
|
75 |
+
|
76 |
+
|
77 |
+
```php
|
78 |
+
function sample_admin_notice__success1() {
|
79 |
+
if ( ! PAnD::is_admin_notice_active( 'notice-one-forever' ) ) {
|
80 |
+
return;
|
81 |
+
}
|
82 |
+
|
83 |
+
?>
|
84 |
+
<div data-dismissible="notice-one-forever" class="updated notice notice-success is-dismissible">
|
85 |
+
<p><?php _e( 'Done 1!', 'sample-text-domain' ); ?></p>
|
86 |
+
</div>
|
87 |
+
<?php
|
88 |
+
}
|
89 |
+
|
90 |
+
function sample_admin_notice__success2() {
|
91 |
+
if ( ! PAnD::is_admin_notice_active( 'notice-two-2' ) ) {
|
92 |
+
return;
|
93 |
+
}
|
94 |
+
|
95 |
+
?>
|
96 |
+
<div data-dismissible="notice-two-2" class="updated notice notice-success is-dismissible">
|
97 |
+
<p><?php _e( 'Done 2!', 'sample-text-domain' ); ?></p>
|
98 |
+
</div>
|
99 |
+
<?php
|
100 |
+
}
|
101 |
+
|
102 |
+
add_action( 'admin_init', array( 'PAnD', 'init' ) );
|
103 |
+
add_action( 'admin_notices', 'sample_admin_notice__success1' );
|
104 |
+
add_action( 'admin_notices', 'sample_admin_notice__success2' );
|
105 |
+
```
|
106 |
+
|
107 |
+
|
108 |
+
Cool beans. Isn't it?
|
classes/external/php/persist-admin-notices-dismissal/dismiss-notice.js
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function ($) {
|
2 |
+
//shorthand for ready event.
|
3 |
+
$(function () {
|
4 |
+
$('div[data-dismissible] button.notice-dismiss').click(function (event) {
|
5 |
+
event.preventDefault();
|
6 |
+
var $this = $(this);
|
7 |
+
|
8 |
+
var attr_value, option_name, dismissible_length, data;
|
9 |
+
|
10 |
+
attr_value = $this.parent().attr('data-dismissible').split('-');
|
11 |
+
|
12 |
+
// remove the dismissible length from the attribute value and rejoin the array.
|
13 |
+
dismissible_length = attr_value.pop();
|
14 |
+
|
15 |
+
option_name = attr_value.join('-');
|
16 |
+
|
17 |
+
data = {
|
18 |
+
'action': 'dismiss_admin_notice',
|
19 |
+
'option_name': option_name,
|
20 |
+
'dismissible_length': dismissible_length,
|
21 |
+
'nonce': dismissible_notice.nonce
|
22 |
+
};
|
23 |
+
|
24 |
+
// We can also pass the url value separately from ajaxurl for front end AJAX implementations
|
25 |
+
$.post(ajaxurl, data);
|
26 |
+
});
|
27 |
+
})
|
28 |
+
|
29 |
+
}(jQuery));
|
classes/external/php/persist-admin-notices-dismissal/persist-admin-notices-dismissal.php
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Persist Admin notices Dismissal
|
5 |
+
*
|
6 |
+
* Copyright (C) 2016 Agbonghama Collins <http://w3guy.com>
|
7 |
+
*
|
8 |
+
* This program is free software: you can redistribute it and/or modify
|
9 |
+
* it under the terms of the GNU General Public License as published by
|
10 |
+
* the Free Software Foundation, either version 3 of the License, or
|
11 |
+
* (at your option) any later version.
|
12 |
+
*
|
13 |
+
* This program is distributed in the hope that it will be useful,
|
14 |
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15 |
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16 |
+
* GNU General Public License for more details.
|
17 |
+
*
|
18 |
+
* You should have received a copy of the GNU General Public License
|
19 |
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
20 |
+
*
|
21 |
+
* @package Persist Admin notices Dismissal
|
22 |
+
* @author Agbonghama Collins
|
23 |
+
* @author Andy Fragen
|
24 |
+
* @license http://www.gnu.org/licenses GNU General Public License
|
25 |
+
* @version 1.3.2
|
26 |
+
*/
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Exit if called directly.
|
30 |
+
*/
|
31 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
32 |
+
die;
|
33 |
+
}
|
34 |
+
|
35 |
+
if ( ! class_exists( 'PAnD' ) ) {
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Class PAnD
|
39 |
+
*/
|
40 |
+
class PAnD {
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Init hooks.
|
44 |
+
*/
|
45 |
+
public static function init() {
|
46 |
+
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'load_script' ) );
|
47 |
+
add_action( 'wp_ajax_dismiss_admin_notice', array( __CLASS__, 'dismiss_admin_notice' ) );
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Enqueue javascript and variables.
|
52 |
+
*/
|
53 |
+
public static function load_script() {
|
54 |
+
|
55 |
+
if(is_customize_preview()) return;
|
56 |
+
|
57 |
+
wp_enqueue_script(
|
58 |
+
'dismissible-notices',
|
59 |
+
plugins_url( 'dismiss-notice.js', __FILE__ ),
|
60 |
+
array( 'jquery', 'common' ),
|
61 |
+
false,
|
62 |
+
true
|
63 |
+
);
|
64 |
+
|
65 |
+
wp_localize_script(
|
66 |
+
'dismissible-notices',
|
67 |
+
'dismissible_notice',
|
68 |
+
array(
|
69 |
+
'nonce' => wp_create_nonce( 'dismissible-notice' ),
|
70 |
+
)
|
71 |
+
);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Handles Ajax request to persist notices dismissal.
|
76 |
+
* Uses check_ajax_referer to verify nonce.
|
77 |
+
*/
|
78 |
+
public static function dismiss_admin_notice() {
|
79 |
+
$option_name = sanitize_text_field( $_POST['option_name'] );
|
80 |
+
$dismissible_length = sanitize_text_field( $_POST['dismissible_length'] );
|
81 |
+
$transient = 0;
|
82 |
+
|
83 |
+
if ( 'forever' != $dismissible_length ) {
|
84 |
+
// If $dismissible_length is not an integer default to 1
|
85 |
+
$dismissible_length = ( 0 == absint( $dismissible_length ) ) ? 1 : $dismissible_length;
|
86 |
+
$transient = absint( $dismissible_length ) * DAY_IN_SECONDS;
|
87 |
+
$dismissible_length = strtotime( absint( $dismissible_length ) . ' days' );
|
88 |
+
}
|
89 |
+
|
90 |
+
check_ajax_referer( 'dismissible-notice', 'nonce' );
|
91 |
+
set_site_transient( $option_name, $dismissible_length, $transient );
|
92 |
+
wp_die();
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Is admin notice active?
|
97 |
+
*
|
98 |
+
* @param string $arg data-dismissible content of notice.
|
99 |
+
*
|
100 |
+
* @return bool
|
101 |
+
*/
|
102 |
+
public static function is_admin_notice_active( $arg ) {
|
103 |
+
$array = explode( '-', $arg );
|
104 |
+
$length = array_pop( $array );
|
105 |
+
$option_name = implode( '-', $array );
|
106 |
+
$db_record = get_site_transient( $option_name );
|
107 |
+
|
108 |
+
if ( 'forever' == $db_record ) {
|
109 |
+
return false;
|
110 |
+
} elseif ( absint( $db_record ) >= time() ) {
|
111 |
+
return false;
|
112 |
+
} else {
|
113 |
+
return true;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
}
|
118 |
+
|
119 |
+
}
|
classes/external/php/yui-php-cssmin-2.4.8-4_fgo.php
DELETED
@@ -1,790 +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 |
-
// preserve flex, keeping percentage even if 0
|
268 |
-
$css = preg_replace_callback('/flex\s?:\s?((?:[0-9 ]*)\s?(?:px|em|auto|%)?(?:calc\(.*\))?)/i',array($this, 'replace_flex'),$css);
|
269 |
-
|
270 |
-
// Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
|
271 |
-
$css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);
|
272 |
-
|
273 |
-
// Shorten & preserve calculations calc(...) since spaces are important
|
274 |
-
$css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
|
275 |
-
|
276 |
-
// Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
|
277 |
-
// +1.2em to 1.2em, +.8px to .8px, +2% to 2%
|
278 |
-
$css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
|
279 |
-
|
280 |
-
// Remove leading zeros from integer and float numbers preceded by : or a white-space
|
281 |
-
// 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
|
282 |
-
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
|
283 |
-
|
284 |
-
// Remove trailing zeros from float numbers preceded by : or a white-space
|
285 |
-
// -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
|
286 |
-
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
|
287 |
-
|
288 |
-
// Remove trailing .0 -> -9.0 to -9
|
289 |
-
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
|
290 |
-
|
291 |
-
// Replace 0 length numbers with 0
|
292 |
-
$css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
|
293 |
-
|
294 |
-
// Remove the spaces before the things that should not have spaces before them.
|
295 |
-
// But, be careful not to turn "p :link {...}" into "p:link{...}"
|
296 |
-
// Swap out any pseudo-class colons with the token, and then swap back.
|
297 |
-
$css = preg_replace_callback('/(?:^|\})[^\{]*\s+\:/', array($this, 'replace_colon'), $css);
|
298 |
-
|
299 |
-
// Remove spaces before the things that should not have spaces before them.
|
300 |
-
$css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
|
301 |
-
|
302 |
-
// Restore spaces for !important
|
303 |
-
$css = preg_replace('/\!important/i', ' !important', $css);
|
304 |
-
|
305 |
-
// bring back the colon
|
306 |
-
$css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
|
307 |
-
|
308 |
-
// retain space for special IE6 cases
|
309 |
-
$css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
|
310 |
-
|
311 |
-
// no space after the end of a preserved comment
|
312 |
-
$css = preg_replace('/\*\/ /', '*/', $css);
|
313 |
-
|
314 |
-
// lowercase some popular @directives
|
315 |
-
$css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
|
316 |
-
|
317 |
-
// lowercase some more common pseudo-elements
|
318 |
-
$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);
|
319 |
-
|
320 |
-
// lowercase some more common functions
|
321 |
-
$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);
|
322 |
-
|
323 |
-
// lower case some common function that can be values
|
324 |
-
// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
|
325 |
-
$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);
|
326 |
-
|
327 |
-
// Put the space back in some cases, to support stuff like
|
328 |
-
// @media screen and (-webkit-min-device-pixel-ratio:0){
|
329 |
-
// based on regex from https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port/blob/v3.2.0/src/Minifier.php#L479
|
330 |
-
$css = preg_replace('/(\s|\)\s)(and|not|or)\(/i', '$1$2 (', $css);
|
331 |
-
|
332 |
-
// Remove the spaces after the things that should not have spaces after them.
|
333 |
-
$css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
|
334 |
-
|
335 |
-
// remove unnecessary semicolons
|
336 |
-
$css = preg_replace('/;+\}/', '}', $css);
|
337 |
-
|
338 |
-
// Fix for issue: #2528146
|
339 |
-
// Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
|
340 |
-
// to avoid issues on Symbian S60 3.x browsers.
|
341 |
-
$css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
|
342 |
-
|
343 |
-
// Replace 0 <length> and 0 <percentage> values with 0.
|
344 |
-
// <length> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/length
|
345 |
-
// <percentage> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
|
346 |
-
$css = preg_replace('/([^\\\\]\:|\s)0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%)/iS', '${1}0', $css);
|
347 |
-
|
348 |
-
// 0% step in a keyframe? restore the % unit
|
349 |
-
$css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]+\{)(.*?)(\}\})/iS', array($this, 'replace_keyframe_zero'), $css);
|
350 |
-
|
351 |
-
// Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
|
352 |
-
$css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
|
353 |
-
|
354 |
-
// Fix for issue: #2528142
|
355 |
-
// Replace text-shadow:0; with text-shadow:0 0 0;
|
356 |
-
$css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
|
357 |
-
|
358 |
-
// Replace background-position:0; with background-position:0 0;
|
359 |
-
// same for transform-origin
|
360 |
-
// Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
|
361 |
-
$css = preg_replace('/(background|background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
|
362 |
-
|
363 |
-
// Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
|
364 |
-
// Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
|
365 |
-
// This makes it more likely that it'll get further compressed in the next step.
|
366 |
-
$css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
|
367 |
-
$css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
|
368 |
-
|
369 |
-
// Shorten colors from #AABBCC to #ABC or short color name.
|
370 |
-
$css = $this->compress_hex_colors($css);
|
371 |
-
|
372 |
-
// border: none to border:0, outline: none to outline:0
|
373 |
-
$css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
|
374 |
-
|
375 |
-
// shorter opacity IE filter
|
376 |
-
$css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
|
377 |
-
|
378 |
-
// Find a fraction that is used for Opera's -o-device-pixel-ratio query
|
379 |
-
// Add token to add the "\" back in later
|
380 |
-
$css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
|
381 |
-
|
382 |
-
// Remove empty rules.
|
383 |
-
$css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
|
384 |
-
|
385 |
-
// Add "/" back to fix Opera -o-device-pixel-ratio query
|
386 |
-
$css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
|
387 |
-
|
388 |
-
// Replace multiple semi-colons in a row by a single one
|
389 |
-
// See SF bug #1980989
|
390 |
-
$css = preg_replace('/;;+/', ';', $css);
|
391 |
-
|
392 |
-
// Restore new lines for /*! important comments
|
393 |
-
$css = preg_replace('/'. self::NL .'/', "\n", $css);
|
394 |
-
|
395 |
-
// Lowercase all uppercase properties
|
396 |
-
$css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
|
397 |
-
|
398 |
-
// Some source control tools don't like it when files containing lines longer
|
399 |
-
// than, say 8000 characters, are checked in. The linebreak option is used in
|
400 |
-
// that case to split long lines after a specific column.
|
401 |
-
if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
|
402 |
-
$linebreak_pos = (int) $linebreak_pos;
|
403 |
-
$start_index = $i = 0;
|
404 |
-
while ($i < strlen($css)) {
|
405 |
-
$i++;
|
406 |
-
if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
|
407 |
-
$css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
|
408 |
-
$start_index = $i;
|
409 |
-
}
|
410 |
-
}
|
411 |
-
}
|
412 |
-
|
413 |
-
// restore preserved comments and strings in reverse order
|
414 |
-
for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
|
415 |
-
$css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
|
416 |
-
// $css.=$this->preserved_tokens[$i];
|
417 |
-
}
|
418 |
-
|
419 |
-
// Trim the final string (for any leading or trailing white spaces)
|
420 |
-
return trim($css);
|
421 |
-
}
|
422 |
-
|
423 |
-
/**
|
424 |
-
* Utility method to replace all data urls with tokens before we start
|
425 |
-
* compressing, to avoid performance issues running some of the subsequent
|
426 |
-
* regexes against large strings chunks.
|
427 |
-
*
|
428 |
-
* @param string $css
|
429 |
-
* @return string
|
430 |
-
*/
|
431 |
-
private function extract_data_urls($css)
|
432 |
-
{
|
433 |
-
// Leave data urls alone to increase parse performance.
|
434 |
-
$max_index = strlen($css) - 1;
|
435 |
-
$append_index = $index = $last_index = $offset = 0;
|
436 |
-
$sb = array();
|
437 |
-
$pattern = '/url\(\s*(["\']?)data\:/i';
|
438 |
-
|
439 |
-
// Since we need to account for non-base64 data urls, we need to handle
|
440 |
-
// ' and ) being part of the data string. Hence switching to indexOf,
|
441 |
-
// to determine whether or not we have matching string terminators and
|
442 |
-
// handling sb appends directly, instead of using matcher.append* methods.
|
443 |
-
|
444 |
-
while (preg_match($pattern, $css, $m, 0, $offset)) {
|
445 |
-
$index = $this->index_of($css, $m[0], $offset);
|
446 |
-
$last_index = $index + strlen($m[0]);
|
447 |
-
$start_index = $index + 4; // "url(".length()
|
448 |
-
$end_index = $last_index - 1;
|
449 |
-
$terminator = $m[1]; // ', " or empty (not quoted)
|
450 |
-
$found_terminator = FALSE;
|
451 |
-
|
452 |
-
if (strlen($terminator) === 0) {
|
453 |
-
$terminator = ')';
|
454 |
-
}
|
455 |
-
|
456 |
-
while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
|
457 |
-
$end_index = $this->index_of($css, $terminator, $end_index + 1);
|
458 |
-
|
459 |
-
// endIndex == 0 doesn't really apply here
|
460 |
-
if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
|
461 |
-
$found_terminator = TRUE;
|
462 |
-
if (')' != $terminator) {
|
463 |
-
$end_index = $this->index_of($css, ')', $end_index);
|
464 |
-
}
|
465 |
-
}
|
466 |
-
}
|
467 |
-
|
468 |
-
// Enough searching, start moving stuff over to the buffer
|
469 |
-
$sb[] = $this->str_slice($css, $append_index, $index);
|
470 |
-
|
471 |
-
if ($found_terminator) {
|
472 |
-
$token = $this->str_slice($css, $start_index, $end_index);
|
473 |
-
if (strpos($token,"<svg")===false && strpos($token,'svg+xml')===false) {
|
474 |
-
$token = preg_replace('/\s+/', '', $token);
|
475 |
-
}
|
476 |
-
$this->preserved_tokens[] = $token;
|
477 |
-
|
478 |
-
$preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
|
479 |
-
$sb[] = $preserver;
|
480 |
-
|
481 |
-
$append_index = $end_index + 1;
|
482 |
-
} else {
|
483 |
-
// No end terminator found, re-add the whole match. Should we throw/warn here?
|
484 |
-
$sb[] = $this->str_slice($css, $index, $last_index);
|
485 |
-
$append_index = $last_index;
|
486 |
-
}
|
487 |
-
|
488 |
-
$offset = $last_index;
|
489 |
-
}
|
490 |
-
|
491 |
-
$sb[] = $this->str_slice($css, $append_index);
|
492 |
-
|
493 |
-
return implode('', $sb);
|
494 |
-
}
|
495 |
-
|
496 |
-
/**
|
497 |
-
* Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
|
498 |
-
*
|
499 |
-
* DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
|
500 |
-
* e.g. #AddressForm { ... }
|
501 |
-
*
|
502 |
-
* DOES NOT compress IE filters, which have hex color values (which would break things).
|
503 |
-
* e.g. filter: chroma(color="#FFFFFF");
|
504 |
-
*
|
505 |
-
* DOES NOT compress invalid hex values.
|
506 |
-
* e.g. background-color: #aabbccdd
|
507 |
-
*
|
508 |
-
* @param string $css
|
509 |
-
* @return string
|
510 |
-
*/
|
511 |
-
private function compress_hex_colors($css)
|
512 |
-
{
|
513 |
-
// Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
|
514 |
-
$pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
|
515 |
-
$_index = $index = $last_index = $offset = 0;
|
516 |
-
$sb = array();
|
517 |
-
// See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
|
518 |
-
$short_safe = array(
|
519 |
-
'#808080' => 'gray',
|
520 |
-
'#008000' => 'green',
|
521 |
-
'#800000' => 'maroon',
|
522 |
-
'#000080' => 'navy',
|
523 |
-
'#808000' => 'olive',
|
524 |
-
'#ffa500' => 'orange',
|
525 |
-
'#800080' => 'purple',
|
526 |
-
'#c0c0c0' => 'silver',
|
527 |
-
'#008080' => 'teal',
|
528 |
-
'#f00' => 'red'
|
529 |
-
);
|
530 |
-
|
531 |
-
while (preg_match($pattern, $css, $m, 0, $offset)) {
|
532 |
-
$index = $this->index_of($css, $m[0], $offset);
|
533 |
-
$last_index = $index + strlen($m[0]);
|
534 |
-
$is_filter = $m[1] !== null && $m[1] !== '';
|
535 |
-
|
536 |
-
$sb[] = $this->str_slice($css, $_index, $index);
|
537 |
-
|
538 |
-
if ($is_filter) {
|
539 |
-
// Restore, maintain case, otherwise filter will break
|
540 |
-
$sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
|
541 |
-
} else {
|
542 |
-
if (strtolower($m[2]) == strtolower($m[3]) &&
|
543 |
-
strtolower($m[4]) == strtolower($m[5]) &&
|
544 |
-
strtolower($m[6]) == strtolower($m[7])) {
|
545 |
-
// Compress.
|
546 |
-
$hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
|
547 |
-
} else {
|
548 |
-
// Non compressible color, restore but lower case.
|
549 |
-
$hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
|
550 |
-
}
|
551 |
-
// replace Hex colors to short safe color names
|
552 |
-
$sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
|
553 |
-
}
|
554 |
-
|
555 |
-
$_index = $offset = $last_index - strlen($m[8]);
|
556 |
-
}
|
557 |
-
|
558 |
-
$sb[] = $this->str_slice($css, $_index);
|
559 |
-
|
560 |
-
return implode('', $sb);
|
561 |
-
}
|
562 |
-
|
563 |
-
/* CALLBACKS
|
564 |
-
* ---------------------------------------------------------------------------------------------
|
565 |
-
*/
|
566 |
-
|
567 |
-
private function replace_string($matches)
|
568 |
-
{
|
569 |
-
$match = $matches[0];
|
570 |
-
$quote = substr($match, 0, 1);
|
571 |
-
// Must use addcslashes in PHP to avoid parsing of backslashes
|
572 |
-
$match = addcslashes($this->str_slice($match, 1, -1), '\\');
|
573 |
-
|
574 |
-
// maybe the string contains a comment-like substring?
|
575 |
-
// one, maybe more? put'em back then
|
576 |
-
if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
|
577 |
-
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
|
578 |
-
$match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
|
579 |
-
}
|
580 |
-
}
|
581 |
-
|
582 |
-
// minify alpha opacity in filter strings
|
583 |
-
$match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
|
584 |
-
|
585 |
-
$this->preserved_tokens[] = $match;
|
586 |
-
return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
|
587 |
-
}
|
588 |
-
|
589 |
-
private function replace_colon($matches)
|
590 |
-
{
|
591 |
-
return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
|
592 |
-
}
|
593 |
-
|
594 |
-
private function replace_calc($matches)
|
595 |
-
{
|
596 |
-
$this->preserved_tokens[] = preg_replace('/\)([\+\-]{1})/',') $1',preg_replace('/([\+\-]{1})\(/','$1 (',trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]))));
|
597 |
-
return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
|
598 |
-
}
|
599 |
-
|
600 |
-
private function replace_flex($matches)
|
601 |
-
{
|
602 |
-
$this->preserved_tokens[] = trim($matches[1]);
|
603 |
-
return 'flex:'.self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
|
604 |
-
}
|
605 |
-
|
606 |
-
private function preserve_old_IE_specific_matrix_definition($matches)
|
607 |
-
{
|
608 |
-
$this->preserved_tokens[] = $matches[1];
|
609 |
-
return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
|
610 |
-
}
|
611 |
-
|
612 |
-
private function replace_keyframe_zero($matches)
|
613 |
-
{
|
614 |
-
return $matches[1] . preg_replace('/0(\{|,[^\)\{]+\{)/', '0%$1', $matches[2]) . $matches[3];
|
615 |
-
}
|
616 |
-
|
617 |
-
private function rgb_to_hex($matches)
|
618 |
-
{
|
619 |
-
// Support for percentage values rgb(100%, 0%, 45%);
|
620 |
-
if ($this->index_of($matches[1], '%') >= 0){
|
621 |
-
$rgbcolors = explode(',', str_replace('%', '', $matches[1]));
|
622 |
-
for ($i = 0; $i < count($rgbcolors); $i++) {
|
623 |
-
$rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
|
624 |
-
}
|
625 |
-
} else {
|
626 |
-
$rgbcolors = explode(',', $matches[1]);
|
627 |
-
}
|
628 |
-
|
629 |
-
// Values outside the sRGB color space should be clipped (0-255)
|
630 |
-
for ($i = 0; $i < count($rgbcolors); $i++) {
|
631 |
-
$rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
|
632 |
-
$rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
|
633 |
-
}
|
634 |
-
|
635 |
-
// Fix for issue #2528093
|
636 |
-
if (!preg_match('/[\s\,\);\}]/', $matches[2])){
|
637 |
-
$matches[2] = ' ' . $matches[2];
|
638 |
-
}
|
639 |
-
|
640 |
-
return '#' . implode('', $rgbcolors) . $matches[2];
|
641 |
-
}
|
642 |
-
|
643 |
-
private function hsl_to_hex($matches)
|
644 |
-
{
|
645 |
-
$values = explode(',', str_replace('%', '', $matches[1]));
|
646 |
-
$h = floatval($values[0]);
|
647 |
-
$s = floatval($values[1]);
|
648 |
-
$l = floatval($values[2]);
|
649 |
-
|
650 |
-
// Wrap and clamp, then fraction!
|
651 |
-
$h = ((($h % 360) + 360) % 360) / 360;
|
652 |
-
$s = $this->clamp_number($s, 0, 100) / 100;
|
653 |
-
$l = $this->clamp_number($l, 0, 100) / 100;
|
654 |
-
|
655 |
-
if ($s == 0) {
|
656 |
-
$r = $g = $b = $this->round_number(255 * $l);
|
657 |
-
} else {
|
658 |
-
$v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
|
659 |
-
$v1 = (2 * $l) - $v2;
|
660 |
-
$r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
|
661 |
-
$g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
|
662 |
-
$b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
|
663 |
-
}
|
664 |
-
|
665 |
-
return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
|
666 |
-
}
|
667 |
-
|
668 |
-
private function lowercase_pseudo_first($matches)
|
669 |
-
{
|
670 |
-
return ':first-'. strtolower($matches[1]) .' '. $matches[2];
|
671 |
-
}
|
672 |
-
|
673 |
-
private function lowercase_directives($matches)
|
674 |
-
{
|
675 |
-
return '@'. strtolower($matches[1]);
|
676 |
-
}
|
677 |
-
|
678 |
-
private function lowercase_pseudo_elements($matches)
|
679 |
-
{
|
680 |
-
return ':'. strtolower($matches[1]);
|
681 |
-
}
|
682 |
-
|
683 |
-
private function lowercase_common_functions($matches)
|
684 |
-
{
|
685 |
-
return ':'. strtolower($matches[1]) .'(';
|
686 |
-
}
|
687 |
-
|
688 |
-
private function lowercase_common_functions_values($matches)
|
689 |
-
{
|
690 |
-
return $matches[1] . strtolower($matches[2]);
|
691 |
-
}
|
692 |
-
|
693 |
-
private function lowercase_properties($matches)
|
694 |
-
{
|
695 |
-
return $matches[1].strtolower($matches[2]).$matches[3];
|
696 |
-
}
|
697 |
-
|
698 |
-
/* HELPERS
|
699 |
-
* ---------------------------------------------------------------------------------------------
|
700 |
-
*/
|
701 |
-
|
702 |
-
private function hue_to_rgb($v1, $v2, $vh)
|
703 |
-
{
|
704 |
-
$vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
|
705 |
-
if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
|
706 |
-
if ($vh * 2 < 1) return $v2;
|
707 |
-
if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
|
708 |
-
return $v1;
|
709 |
-
}
|
710 |
-
|
711 |
-
private function round_number($n)
|
712 |
-
{
|
713 |
-
return intval(floor(floatval($n) + 0.5), 10);
|
714 |
-
}
|
715 |
-
|
716 |
-
private function clamp_number($n, $min, $max)
|
717 |
-
{
|
718 |
-
return min(max($n, $min), $max);
|
719 |
-
}
|
720 |
-
|
721 |
-
/**
|
722 |
-
* PHP port of Javascript's "indexOf" function for strings only
|
723 |
-
* Author: Tubal Martin http://blog.margenn.com
|
724 |
-
*
|
725 |
-
* @param string $haystack
|
726 |
-
* @param string $needle
|
727 |
-
* @param int $offset index (optional)
|
728 |
-
* @return int
|
729 |
-
*/
|
730 |
-
private function index_of($haystack, $needle, $offset = 0)
|
731 |
-
{
|
732 |
-
$index = strpos($haystack, $needle, $offset);
|
733 |
-
|
734 |
-
return ($index !== FALSE) ? $index : -1;
|
735 |
-
}
|
736 |
-
|
737 |
-
/**
|
738 |
-
* PHP port of Javascript's "slice" function for strings only
|
739 |
-
* Author: Tubal Martin http://blog.margenn.com
|
740 |
-
* Tests: http://margenn.com/tubal/str_slice/
|
741 |
-
*
|
742 |
-
* @param string $str
|
743 |
-
* @param int $start index
|
744 |
-
* @param int|bool $end index (optional)
|
745 |
-
* @return string
|
746 |
-
*/
|
747 |
-
private function str_slice($str, $start = 0, $end = FALSE)
|
748 |
-
{
|
749 |
-
if ($end !== FALSE && ($start < 0 || $end <= 0)) {
|
750 |
-
$max = strlen($str);
|
751 |
-
|
752 |
-
if ($start < 0) {
|
753 |
-
if (($start = $max + $start) < 0) {
|
754 |
-
return '';
|
755 |
-
}
|
756 |
-
}
|
757 |
-
|
758 |
-
if ($end < 0) {
|
759 |
-
if (($end = $max + $end) < 0) {
|
760 |
-
return '';
|
761 |
-
}
|
762 |
-
}
|
763 |
-
|
764 |
-
if ($end <= $start) {
|
765 |
-
return '';
|
766 |
-
}
|
767 |
-
}
|
768 |
-
|
769 |
-
$slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
|
770 |
-
return ($slice === FALSE) ? '' : $slice;
|
771 |
-
}
|
772 |
-
|
773 |
-
/**
|
774 |
-
* Convert strings like "64M" or "30" to int values
|
775 |
-
* @param mixed $size
|
776 |
-
* @return int
|
777 |
-
*/
|
778 |
-
private function normalize_int($size)
|
779 |
-
{
|
780 |
-
if (is_string($size)) {
|
781 |
-
switch (substr($size, -1)) {
|
782 |
-
case 'M': case 'm': return (int) $size * 1048576;
|
783 |
-
case 'K': case 'k': return (int) $size * 1024;
|
784 |
-
case 'G': case 'g': return (int) $size * 1073741824;
|
785 |
-
}
|
786 |
-
}
|
787 |
-
|
788 |
-
return (int) $size;
|
789 |
-
}
|
790 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/external/php/yui-php-cssmin-2.4.8-p10/cssmin.php
DELETED
@@ -1,1112 +0,0 @@
|
|
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
DELETED
@@ -1,37 +0,0 @@
|
|
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
DELETED
@@ -1,108 +0,0 @@
|
|
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/external/php/yui-php-cssmin-bundled/Colors.php
ADDED
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Autoptimize\tubalmartin\CssMin;
|
4 |
+
|
5 |
+
class Colors
|
6 |
+
{
|
7 |
+
public static function getHexToNamedMap()
|
8 |
+
{
|
9 |
+
// Hex colors longer than named counterpart
|
10 |
+
return array(
|
11 |
+
'#f0ffff' => 'azure',
|
12 |
+
'#f5f5dc' => 'beige',
|
13 |
+
'#ffe4c4' => 'bisque',
|
14 |
+
'#a52a2a' => 'brown',
|
15 |
+
'#ff7f50' => 'coral',
|
16 |
+
'#ffd700' => 'gold',
|
17 |
+
'#808080' => 'gray',
|
18 |
+
'#008000' => 'green',
|
19 |
+
'#4b0082' => 'indigo',
|
20 |
+
'#fffff0' => 'ivory',
|
21 |
+
'#f0e68c' => 'khaki',
|
22 |
+
'#faf0e6' => 'linen',
|
23 |
+
'#800000' => 'maroon',
|
24 |
+
'#000080' => 'navy',
|
25 |
+
'#fdf5e6' => 'oldlace',
|
26 |
+
'#808000' => 'olive',
|
27 |
+
'#ffa500' => 'orange',
|
28 |
+
'#da70d6' => 'orchid',
|
29 |
+
'#cd853f' => 'peru',
|
30 |
+
'#ffc0cb' => 'pink',
|
31 |
+
'#dda0dd' => 'plum',
|
32 |
+
'#800080' => 'purple',
|
33 |
+
'#f00' => 'red',
|
34 |
+
'#fa8072' => 'salmon',
|
35 |
+
'#a0522d' => 'sienna',
|
36 |
+
'#c0c0c0' => 'silver',
|
37 |
+
'#fffafa' => 'snow',
|
38 |
+
'#d2b48c' => 'tan',
|
39 |
+
'#008080' => 'teal',
|
40 |
+
'#ff6347' => 'tomato',
|
41 |
+
'#ee82ee' => 'violet',
|
42 |
+
'#f5deb3' => 'wheat'
|
43 |
+
);
|
44 |
+
}
|
45 |
+
|
46 |
+
public static function getNamedToHexMap()
|
47 |
+
{
|
48 |
+
// Named colors longer than hex counterpart
|
49 |
+
return array(
|
50 |
+
'aliceblue' => '#f0f8ff',
|
51 |
+
'antiquewhite' => '#faebd7',
|
52 |
+
'aquamarine' => '#7fffd4',
|
53 |
+
'black' => '#000',
|
54 |
+
'blanchedalmond' => '#ffebcd',
|
55 |
+
'blueviolet' => '#8a2be2',
|
56 |
+
'burlywood' => '#deb887',
|
57 |
+
'cadetblue' => '#5f9ea0',
|
58 |
+
'chartreuse' => '#7fff00',
|
59 |
+
'chocolate' => '#d2691e',
|
60 |
+
'cornflowerblue' => '#6495ed',
|
61 |
+
'cornsilk' => '#fff8dc',
|
62 |
+
'darkblue' => '#00008b',
|
63 |
+
'darkcyan' => '#008b8b',
|
64 |
+
'darkgoldenrod' => '#b8860b',
|
65 |
+
'darkgray' => '#a9a9a9',
|
66 |
+
'darkgreen' => '#006400',
|
67 |
+
'darkgrey' => '#a9a9a9',
|
68 |
+
'darkkhaki' => '#bdb76b',
|
69 |
+
'darkmagenta' => '#8b008b',
|
70 |
+
'darkolivegreen' => '#556b2f',
|
71 |
+
'darkorange' => '#ff8c00',
|
72 |
+
'darkorchid' => '#9932cc',
|
73 |
+
'darksalmon' => '#e9967a',
|
74 |
+
'darkseagreen' => '#8fbc8f',
|
75 |
+
'darkslateblue' => '#483d8b',
|
76 |
+
'darkslategray' => '#2f4f4f',
|
77 |
+
'darkslategrey' => '#2f4f4f',
|
78 |
+
'darkturquoise' => '#00ced1',
|
79 |
+
'darkviolet' => '#9400d3',
|
80 |
+
'deeppink' => '#ff1493',
|
81 |
+
'deepskyblue' => '#00bfff',
|
82 |
+
'dodgerblue' => '#1e90ff',
|
83 |
+
'firebrick' => '#b22222',
|
84 |
+
'floralwhite' => '#fffaf0',
|
85 |
+
'forestgreen' => '#228b22',
|
86 |
+
'fuchsia' => '#f0f',
|
87 |
+
'gainsboro' => '#dcdcdc',
|
88 |
+
'ghostwhite' => '#f8f8ff',
|
89 |
+
'goldenrod' => '#daa520',
|
90 |
+
'greenyellow' => '#adff2f',
|
91 |
+
'honeydew' => '#f0fff0',
|
92 |
+
'indianred' => '#cd5c5c',
|
93 |
+
'lavender' => '#e6e6fa',
|
94 |
+
'lavenderblush' => '#fff0f5',
|
95 |
+
'lawngreen' => '#7cfc00',
|
96 |
+
'lemonchiffon' => '#fffacd',
|
97 |
+
'lightblue' => '#add8e6',
|
98 |
+
'lightcoral' => '#f08080',
|
99 |
+
'lightcyan' => '#e0ffff',
|
100 |
+
'lightgoldenrodyellow' => '#fafad2',
|
101 |
+
'lightgray' => '#d3d3d3',
|
102 |
+
'lightgreen' => '#90ee90',
|
103 |
+
'lightgrey' => '#d3d3d3',
|
104 |
+
'lightpink' => '#ffb6c1',
|
105 |
+
'lightsalmon' => '#ffa07a',
|
106 |
+
'lightseagreen' => '#20b2aa',
|
107 |
+
'lightskyblue' => '#87cefa',
|
108 |
+
'lightslategray' => '#778899',
|
109 |
+
'lightslategrey' => '#778899',
|
110 |
+
'lightsteelblue' => '#b0c4de',
|
111 |
+
'lightyellow' => '#ffffe0',
|
112 |
+
'limegreen' => '#32cd32',
|
113 |
+
'mediumaquamarine' => '#66cdaa',
|
114 |
+
'mediumblue' => '#0000cd',
|
115 |
+
'mediumorchid' => '#ba55d3',
|
116 |
+
'mediumpurple' => '#9370db',
|
117 |
+
'mediumseagreen' => '#3cb371',
|
118 |
+
'mediumslateblue' => '#7b68ee',
|
119 |
+
'mediumspringgreen' => '#00fa9a',
|
120 |
+
'mediumturquoise' => '#48d1cc',
|
121 |
+
'mediumvioletred' => '#c71585',
|
122 |
+
'midnightblue' => '#191970',
|
123 |
+
'mintcream' => '#f5fffa',
|
124 |
+
'mistyrose' => '#ffe4e1',
|
125 |
+
'moccasin' => '#ffe4b5',
|
126 |
+
'navajowhite' => '#ffdead',
|
127 |
+
'olivedrab' => '#6b8e23',
|
128 |
+
'orangered' => '#ff4500',
|
129 |
+
'palegoldenrod' => '#eee8aa',
|
130 |
+
'palegreen' => '#98fb98',
|
131 |
+
'paleturquoise' => '#afeeee',
|
132 |
+
'palevioletred' => '#db7093',
|
133 |
+
'papayawhip' => '#ffefd5',
|
134 |
+
'peachpuff' => '#ffdab9',
|
135 |
+
'powderblue' => '#b0e0e6',
|
136 |
+
'rebeccapurple' => '#663399',
|
137 |
+
'rosybrown' => '#bc8f8f',
|
138 |
+
'royalblue' => '#4169e1',
|
139 |
+
'saddlebrown' => '#8b4513',
|
140 |
+
'sandybrown' => '#f4a460',
|
141 |
+
'seagreen' => '#2e8b57',
|
142 |
+
'seashell' => '#fff5ee',
|
143 |
+
'slateblue' => '#6a5acd',
|
144 |
+
'slategray' => '#708090',
|
145 |
+
'slategrey' => '#708090',
|
146 |
+
'springgreen' => '#00ff7f',
|
147 |
+
'steelblue' => '#4682b4',
|
148 |
+
'turquoise' => '#40e0d0',
|
149 |
+
'white' => '#fff',
|
150 |
+
'whitesmoke' => '#f5f5f5',
|
151 |
+
'yellow' => '#ff0',
|
152 |
+
'yellowgreen' => '#9acd32'
|
153 |
+
);
|
154 |
+
}
|
155 |
+
}
|
classes/external/php/yui-php-cssmin-bundled/Minifier.php
ADDED
@@ -0,0 +1,895 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*!
|
4 |
+
* CssMin
|
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 |
+
namespace Autoptimize\tubalmartin\CssMin;
|
24 |
+
|
25 |
+
class Minifier
|
26 |
+
{
|
27 |
+
const QUERY_FRACTION = '_CSSMIN_QF_';
|
28 |
+
const COMMENT_TOKEN = '_CSSMIN_CMT_%d_';
|
29 |
+
const COMMENT_TOKEN_START = '_CSSMIN_CMT_';
|
30 |
+
const RULE_BODY_TOKEN = '_CSSMIN_RBT_%d_';
|
31 |
+
const PRESERVED_TOKEN = '_CSSMIN_PTK_%d_';
|
32 |
+
|
33 |
+
// Token lists
|
34 |
+
private $comments = array();
|
35 |
+
private $ruleBodies = array();
|
36 |
+
private $preservedTokens = array();
|
37 |
+
|
38 |
+
// Output options
|
39 |
+
private $keepImportantComments = true;
|
40 |
+
private $keepSourceMapComment = false;
|
41 |
+
private $linebreakPosition = 0;
|
42 |
+
|
43 |
+
// PHP ini limits
|
44 |
+
private $raisePhpLimits;
|
45 |
+
private $memoryLimit;
|
46 |
+
private $maxExecutionTime = 60; // 1 min
|
47 |
+
private $pcreBacktrackLimit;
|
48 |
+
private $pcreRecursionLimit;
|
49 |
+
|
50 |
+
// Color maps
|
51 |
+
private $hexToNamedColorsMap;
|
52 |
+
private $namedToHexColorsMap;
|
53 |
+
|
54 |
+
// Regexes
|
55 |
+
private $numRegex;
|
56 |
+
private $charsetRegex = '/@charset [^;]+;/Si';
|
57 |
+
private $importRegex = '/@import [^;]+;/Si';
|
58 |
+
private $namespaceRegex = '/@namespace [^;]+;/Si';
|
59 |
+
private $namedToHexColorsRegex;
|
60 |
+
private $shortenOneZeroesRegex;
|
61 |
+
private $shortenTwoZeroesRegex;
|
62 |
+
private $shortenThreeZeroesRegex;
|
63 |
+
private $shortenFourZeroesRegex;
|
64 |
+
private $unitsGroupRegex = '(?:ch|cm|em|ex|gd|in|mm|px|pt|pc|q|rem|vh|vmax|vmin|vw|%)';
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @param bool|int $raisePhpLimits If true, PHP settings will be raised if needed
|
68 |
+
*/
|
69 |
+
public function __construct($raisePhpLimits = true)
|
70 |
+
{
|
71 |
+
$this->raisePhpLimits = (bool) $raisePhpLimits;
|
72 |
+
$this->memoryLimit = 128 * 1048576; // 128MB in bytes
|
73 |
+
$this->pcreBacktrackLimit = 1000 * 1000;
|
74 |
+
$this->pcreRecursionLimit = 500 * 1000;
|
75 |
+
$this->hexToNamedColorsMap = Colors::getHexToNamedMap();
|
76 |
+
$this->namedToHexColorsMap = Colors::getNamedToHexMap();
|
77 |
+
$this->namedToHexColorsRegex = sprintf(
|
78 |
+
'/([:,( ])(%s)( |,|\)|;|$)/Si',
|
79 |
+
implode('|', array_keys($this->namedToHexColorsMap))
|
80 |
+
);
|
81 |
+
$this->numRegex = sprintf('-?\d*\.?\d+%s?', $this->unitsGroupRegex);
|
82 |
+
$this->setShortenZeroValuesRegexes();
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Parses & minifies the given input CSS string
|
87 |
+
* @param string $css
|
88 |
+
* @return string
|
89 |
+
*/
|
90 |
+
public function run($css = '')
|
91 |
+
{
|
92 |
+
if (empty($css) || !is_string($css)) {
|
93 |
+
return '';
|
94 |
+
}
|
95 |
+
|
96 |
+
$this->resetRunProperties();
|
97 |
+
|
98 |
+
if ($this->raisePhpLimits) {
|
99 |
+
$this->doRaisePhpLimits();
|
100 |
+
}
|
101 |
+
|
102 |
+
return $this->minify($css);
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Sets whether to keep or remove sourcemap special comment.
|
107 |
+
* Sourcemap comments are removed by default.
|
108 |
+
* @param bool $keepSourceMapComment
|
109 |
+
*/
|
110 |
+
public function keepSourceMapComment($keepSourceMapComment = true)
|
111 |
+
{
|
112 |
+
$this->keepSourceMapComment = (bool) $keepSourceMapComment;
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Sets whether to keep or remove important comments.
|
117 |
+
* Important comments outside of a declaration block are kept by default.
|
118 |
+
* @param bool $removeImportantComments
|
119 |
+
*/
|
120 |
+
public function removeImportantComments($removeImportantComments = true)
|
121 |
+
{
|
122 |
+
$this->keepImportantComments = !(bool) $removeImportantComments;
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Sets the approximate column after which long lines will be splitted in the output
|
127 |
+
* with a linebreak.
|
128 |
+
* @param int $position
|
129 |
+
*/
|
130 |
+
public function setLineBreakPosition($position)
|
131 |
+
{
|
132 |
+
$this->linebreakPosition = (int) $position;
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Sets the memory limit for this script
|
137 |
+
* @param int|string $limit
|
138 |
+
*/
|
139 |
+
public function setMemoryLimit($limit)
|
140 |
+
{
|
141 |
+
$this->memoryLimit = Utils::normalizeInt($limit);
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Sets the maximum execution time for this script
|
146 |
+
* @param int|string $seconds
|
147 |
+
*/
|
148 |
+
public function setMaxExecutionTime($seconds)
|
149 |
+
{
|
150 |
+
$this->maxExecutionTime = (int) $seconds;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Sets the PCRE backtrack limit for this script
|
155 |
+
* @param int $limit
|
156 |
+
*/
|
157 |
+
public function setPcreBacktrackLimit($limit)
|
158 |
+
{
|
159 |
+
$this->pcreBacktrackLimit = (int) $limit;
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* Sets the PCRE recursion limit for this script
|
164 |
+
* @param int $limit
|
165 |
+
*/
|
166 |
+
public function setPcreRecursionLimit($limit)
|
167 |
+
{
|
168 |
+
$this->pcreRecursionLimit = (int) $limit;
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Builds regular expressions needed for shortening zero values
|
173 |
+
*/
|
174 |
+
private function setShortenZeroValuesRegexes()
|
175 |
+
{
|
176 |
+
$zeroRegex = '0'. $this->unitsGroupRegex;
|
177 |
+
$numOrPosRegex = '('. $this->numRegex .'|top|left|bottom|right|center) ';
|
178 |
+
$oneZeroSafeProperties = array(
|
179 |
+
'(?:line-)?height',
|
180 |
+
'(?:(?:min|max)-)?width',
|
181 |
+
'top',
|
182 |
+
'left',
|
183 |
+
'background-position',
|
184 |
+
'bottom',
|
185 |
+
'right',
|
186 |
+
'border(?:-(?:top|left|bottom|right))?(?:-width)?',
|
187 |
+
'border-(?:(?:top|bottom)-(?:left|right)-)?radius',
|
188 |
+
'column-(?:gap|width)',
|
189 |
+
'margin(?:-(?:top|left|bottom|right))?',
|
190 |
+
'outline-width',
|
191 |
+
'padding(?:-(?:top|left|bottom|right))?'
|
192 |
+
);
|
193 |
+
|
194 |
+
// First zero regex
|
195 |
+
$regex = '/(^|;)('. implode('|', $oneZeroSafeProperties) .'):%s/Si';
|
196 |
+
$this->shortenOneZeroesRegex = sprintf($regex, $zeroRegex);
|
197 |
+
|
198 |
+
// Multiple zeroes regexes
|
199 |
+
$regex = '/(^|;)(margin|padding|border-(?:width|radius)|background-position):%s/Si';
|
200 |
+
$this->shortenTwoZeroesRegex = sprintf($regex, $numOrPosRegex . $zeroRegex);
|
201 |
+
$this->shortenThreeZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $zeroRegex);
|
202 |
+
$this->shortenFourZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $numOrPosRegex . $zeroRegex);
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Resets properties whose value may change between runs
|
207 |
+
*/
|
208 |
+
private function resetRunProperties()
|
209 |
+
{
|
210 |
+
$this->comments = array();
|
211 |
+
$this->ruleBodies = array();
|
212 |
+
$this->preservedTokens = array();
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* Tries to configure PHP to use at least the suggested minimum settings
|
217 |
+
* @return void
|
218 |
+
*/
|
219 |
+
private function doRaisePhpLimits()
|
220 |
+
{
|
221 |
+
$phpLimits = array(
|
222 |
+
'memory_limit' => $this->memoryLimit,
|
223 |
+
'max_execution_time' => $this->maxExecutionTime,
|
224 |
+
'pcre.backtrack_limit' => $this->pcreBacktrackLimit,
|
225 |
+
'pcre.recursion_limit' => $this->pcreRecursionLimit
|
226 |
+
);
|
227 |
+
|
228 |
+
// If current settings are higher respect them.
|
229 |
+
foreach ($phpLimits as $name => $suggested) {
|
230 |
+
$current = Utils::normalizeInt(ini_get($name));
|
231 |
+
|
232 |
+
if ($current >= $suggested) {
|
233 |
+
continue;
|
234 |
+
}
|
235 |
+
|
236 |
+
// memoryLimit exception: allow -1 for "no memory limit".
|
237 |
+
if ($name === 'memory_limit' && $current === -1) {
|
238 |
+
continue;
|
239 |
+
}
|
240 |
+
|
241 |
+
// maxExecutionTime exception: allow 0 for "no memory limit".
|
242 |
+
if ($name === 'max_execution_time' && $current === 0) {
|
243 |
+
continue;
|
244 |
+
}
|
245 |
+
|
246 |
+
ini_set($name, $suggested);
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
/**
|
251 |
+
* Registers a preserved token
|
252 |
+
* @param string $token
|
253 |
+
* @return string The token ID string
|
254 |
+
*/
|
255 |
+
private function registerPreservedToken($token)
|
256 |
+
{
|
257 |
+
$tokenId = sprintf(self::PRESERVED_TOKEN, count($this->preservedTokens));
|
258 |
+
$this->preservedTokens[$tokenId] = $token;
|
259 |
+
return $tokenId;
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Registers a candidate comment token
|
264 |
+
* @param string $comment
|
265 |
+
* @return string The comment token ID string
|
266 |
+
*/
|
267 |
+
private function registerCommentToken($comment)
|
268 |
+
{
|
269 |
+
$tokenId = sprintf(self::COMMENT_TOKEN, count($this->comments));
|
270 |
+
$this->comments[$tokenId] = $comment;
|
271 |
+
return $tokenId;
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Registers a rule body token
|
276 |
+
* @param string $body the minified rule body
|
277 |
+
* @return string The rule body token ID string
|
278 |
+
*/
|
279 |
+
private function registerRuleBodyToken($body)
|
280 |
+
{
|
281 |
+
if (empty($body)) {
|
282 |
+
return '';
|
283 |
+
}
|
284 |
+
|
285 |
+
$tokenId = sprintf(self::RULE_BODY_TOKEN, count($this->ruleBodies));
|
286 |
+
$this->ruleBodies[$tokenId] = $body;
|
287 |
+
return $tokenId;
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Parses & minifies the given input CSS string
|
292 |
+
* @param string $css
|
293 |
+
* @return string
|
294 |
+
*/
|
295 |
+
private function minify($css)
|
296 |
+
{
|
297 |
+
// Process data urls
|
298 |
+
$css = $this->processDataUrls($css);
|
299 |
+
|
300 |
+
// Process comments
|
301 |
+
$css = preg_replace_callback(
|
302 |
+
'/(?<!\\\\)\/\*(.*?)\*(?<!\\\\)\//Ss',
|
303 |
+
array($this, 'processCommentsCallback'),
|
304 |
+
$css
|
305 |
+
);
|
306 |
+
|
307 |
+
// IE7: Process Microsoft matrix filters (whitespaces between Matrix parameters). Can contain strings inside.
|
308 |
+
$css = preg_replace_callback(
|
309 |
+
'/filter:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^)]+)\)/Ss',
|
310 |
+
array($this, 'processOldIeSpecificMatrixDefinitionCallback'),
|
311 |
+
$css
|
312 |
+
);
|
313 |
+
|
314 |
+
// Process quoted unquotable attribute selectors to unquote them. Covers most common cases.
|
315 |
+
// Likelyhood of a quoted attribute selector being a substring in a string: Very very low.
|
316 |
+
$css = preg_replace(
|
317 |
+
'/\[\s*([a-z][a-z-]+)\s*([\*\|\^\$~]?=)\s*[\'"](-?[a-z_][a-z0-9-_]+)[\'"]\s*\]/Ssi',
|
318 |
+
'[$1$2$3]',
|
319 |
+
$css
|
320 |
+
);
|
321 |
+
|
322 |
+
// Process strings so their content doesn't get accidentally minified
|
323 |
+
$css = preg_replace_callback(
|
324 |
+
'/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S",
|
325 |
+
array($this, 'processStringsCallback'),
|
326 |
+
$css
|
327 |
+
);
|
328 |
+
|
329 |
+
// Normalize all whitespace strings to single spaces. Easier to work with that way.
|
330 |
+
$css = preg_replace('/\s+/S', ' ', $css);
|
331 |
+
|
332 |
+
// Process import At-rules with unquoted URLs so URI reserved characters such as a semicolon may be used safely.
|
333 |
+
$css = preg_replace_callback(
|
334 |
+
'/@import url\(([^\'"]+?)\)( |;)/Si',
|
335 |
+
array($this, 'processImportUnquotedUrlAtRulesCallback'),
|
336 |
+
$css
|
337 |
+
);
|
338 |
+
|
339 |
+
// Process comments
|
340 |
+
$css = $this->processComments($css);
|
341 |
+
|
342 |
+
// Process rule bodies
|
343 |
+
$css = $this->processRuleBodies($css);
|
344 |
+
|
345 |
+
// Process at-rules and selectors
|
346 |
+
$css = $this->processAtRulesAndSelectors($css);
|
347 |
+
|
348 |
+
// Restore preserved rule bodies before splitting
|
349 |
+
$css = strtr($css, $this->ruleBodies);
|
350 |
+
|
351 |
+
// Split long lines in output if required
|
352 |
+
$css = $this->processLongLineSplitting($css);
|
353 |
+
|
354 |
+
// Restore preserved comments and strings
|
355 |
+
$css = strtr($css, $this->preservedTokens);
|
356 |
+
|
357 |
+
return trim($css);
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* Searches & replaces all data urls with tokens before we start compressing,
|
362 |
+
* to avoid performance issues running some of the subsequent regexes against large string chunks.
|
363 |
+
* @param string $css
|
364 |
+
* @return string
|
365 |
+
*/
|
366 |
+
private function processDataUrls($css)
|
367 |
+
{
|
368 |
+
$ret = '';
|
369 |
+
$searchOffset = $substrOffset = 0;
|
370 |
+
|
371 |
+
// Since we need to account for non-base64 data urls, we need to handle
|
372 |
+
// ' and ) being part of the data string.
|
373 |
+
while (preg_match('/url\(\s*(["\']?)data:/Si', $css, $m, PREG_OFFSET_CAPTURE, $searchOffset)) {
|
374 |
+
$matchStartIndex = $m[0][1];
|
375 |
+
$dataStartIndex = $matchStartIndex + 4; // url( length
|
376 |
+
$searchOffset = $matchStartIndex + strlen($m[0][0]);
|
377 |
+
$terminator = $m[1][0]; // ', " or empty (not quoted)
|
378 |
+
$terminatorRegex = '/(?<!\\\\)'. (strlen($terminator) === 0 ? '' : $terminator.'\s*') .'(\))/S';
|
379 |
+
|
380 |
+
$ret .= substr($css, $substrOffset, $matchStartIndex - $substrOffset);
|
381 |
+
|
382 |
+
// Terminator found
|
383 |
+
if (preg_match($terminatorRegex, $css, $matches, PREG_OFFSET_CAPTURE, $searchOffset)) {
|
384 |
+
$matchEndIndex = $matches[1][1];
|
385 |
+
$searchOffset = $matchEndIndex + 1;
|
386 |
+
$token = substr($css, $dataStartIndex, $matchEndIndex - $dataStartIndex);
|
387 |
+
|
388 |
+
// Remove all spaces only for base64 encoded URLs.
|
389 |
+
if (stripos($token, 'base64,') !== false) {
|
390 |
+
$token = preg_replace('/\s+/S', '', $token);
|
391 |
+
}
|
392 |
+
|
393 |
+
$ret .= 'url('. $this->registerPreservedToken(trim($token)) .')';
|
394 |
+
// No end terminator found, re-add the whole match. Should we throw/warn here?
|
395 |
+
} else {
|
396 |
+
$ret .= substr($css, $matchStartIndex, $searchOffset - $matchStartIndex);
|
397 |
+
}
|
398 |
+
|
399 |
+
$substrOffset = $searchOffset;
|
400 |
+
}
|
401 |
+
|
402 |
+
$ret .= substr($css, $substrOffset);
|
403 |
+
|
404 |
+
return $ret;
|
405 |
+
}
|
406 |
+
|
407 |
+
/**
|
408 |
+
* Registers all comments found as candidates to be preserved.
|
409 |
+
* @param array $matches
|
410 |
+
* @return string
|
411 |
+
*/
|
412 |
+
private function processCommentsCallback($matches)
|
413 |
+
{
|
414 |
+
return '/*'. $this->registerCommentToken($matches[1]) .'*/';
|
415 |
+
}
|
416 |
+
|
417 |
+
/**
|
418 |
+
* Preserves old IE Matrix string definition
|
419 |
+
* @param array $matches
|
420 |
+
* @return string
|
421 |
+
*/
|
422 |
+
private function processOldIeSpecificMatrixDefinitionCallback($matches)
|
423 |
+
{
|
424 |
+
return 'filter:progid:DXImageTransform.Microsoft.Matrix('. $this->registerPreservedToken($matches[1]) .')';
|
425 |
+
}
|
426 |
+
|
427 |
+
/**
|
428 |
+
* Preserves strings found
|
429 |
+
* @param array $matches
|
430 |
+
* @return string
|
431 |
+
*/
|
432 |
+
private function processStringsCallback($matches)
|
433 |
+
{
|
434 |
+
$match = $matches[0];
|
435 |
+
$quote = substr($match, 0, 1);
|
436 |
+
$match = substr($match, 1, -1);
|
437 |
+
|
438 |
+
// maybe the string contains a comment-like substring?
|
439 |
+
// one, maybe more? put'em back then
|
440 |
+
if (strpos($match, self::COMMENT_TOKEN_START) !== false) {
|
441 |
+
$match = strtr($match, $this->comments);
|
442 |
+
}
|
443 |
+
|
444 |
+
// minify alpha opacity in filter strings
|
445 |
+
$match = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $match);
|
446 |
+
|
447 |
+
return $quote . $this->registerPreservedToken($match) . $quote;
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* Searches & replaces all import at-rule unquoted urls with tokens so URI reserved characters such as a semicolon
|
452 |
+
* may be used safely in a URL.
|
453 |
+
* @param array $matches
|
454 |
+
* @return string
|
455 |
+
*/
|
456 |
+
private function processImportUnquotedUrlAtRulesCallback($matches)
|
457 |
+
{
|
458 |
+
return '@import url('. $this->registerPreservedToken($matches[1]) .')'. $matches[2];
|
459 |
+
}
|
460 |
+
|
461 |
+
/**
|
462 |
+
* Preserves or removes comments found.
|
463 |
+
* @param string $css
|
464 |
+
* @return string
|
465 |
+
*/
|
466 |
+
private function processComments($css)
|
467 |
+
{
|
468 |
+
foreach ($this->comments as $commentId => $comment) {
|
469 |
+
$commentIdString = '/*'. $commentId .'*/';
|
470 |
+
|
471 |
+
// ! in the first position of the comment means preserve
|
472 |
+
// so push to the preserved tokens keeping the !
|
473 |
+
if ($this->keepImportantComments && strpos($comment, '!') === 0) {
|
474 |
+
$preservedTokenId = $this->registerPreservedToken($comment);
|
475 |
+
// Put new lines before and after /*! important comments
|
476 |
+
$css = str_replace($commentIdString, "\n/*$preservedTokenId*/\n", $css);
|
477 |
+
continue;
|
478 |
+
}
|
479 |
+
|
480 |
+
// # sourceMappingURL= in the first position of the comment means sourcemap
|
481 |
+
// so push to the preserved tokens if {$this->keepSourceMapComment} is truthy.
|
482 |
+
if ($this->keepSourceMapComment && strpos($comment, '# sourceMappingURL=') === 0) {
|
483 |
+
$preservedTokenId = $this->registerPreservedToken($comment);
|
484 |
+
// Add new line before the sourcemap comment
|
485 |
+
$css = str_replace($commentIdString, "\n/*$preservedTokenId*/", $css);
|
486 |
+
continue;
|
487 |
+
}
|
488 |
+
|
489 |
+
// Keep empty comments after child selectors (IE7 hack)
|
490 |
+
// e.g. html >/**/ body
|
491 |
+
if (strlen($comment) === 0 && strpos($css, '>/*'.$commentId) !== false) {
|
492 |
+
$css = str_replace($commentId, $this->registerPreservedToken(''), $css);
|
493 |
+
continue;
|
494 |
+
}
|
495 |
+
|
496 |
+
// in all other cases kill the comment
|
497 |
+
$css = str_replace($commentIdString, '', $css);
|
498 |
+
}
|
499 |
+
|
500 |
+
// Normalize whitespace again
|
501 |
+
$css = preg_replace('/ +/S', ' ', $css);
|
502 |
+
|
503 |
+
return $css;
|
504 |
+
}
|
505 |
+
|
506 |
+
/**
|
507 |
+
* Finds, minifies & preserves all rule bodies.
|
508 |
+
* @param string $css the whole stylesheet.
|
509 |
+
* @return string
|
510 |
+
*/
|
511 |
+
private function processRuleBodies($css)
|
512 |
+
{
|
513 |
+
$ret = '';
|
514 |
+
$searchOffset = $substrOffset = 0;
|
515 |
+
|
516 |
+
while (($blockStartPos = strpos($css, '{', $searchOffset)) !== false) {
|
517 |
+
$blockEndPos = strpos($css, '}', $blockStartPos);
|
518 |
+
$nextBlockStartPos = strpos($css, '{', $blockStartPos + 1);
|
519 |
+
$ret .= substr($css, $substrOffset, $blockStartPos - $substrOffset);
|
520 |
+
|
521 |
+
if ($nextBlockStartPos !== false && $nextBlockStartPos < $blockEndPos) {
|
522 |
+
$ret .= substr($css, $blockStartPos, $nextBlockStartPos - $blockStartPos);
|
523 |
+
$searchOffset = $nextBlockStartPos;
|
524 |
+
} else {
|
525 |
+
$ruleBody = substr($css, $blockStartPos + 1, $blockEndPos - $blockStartPos - 1);
|
526 |
+
$ruleBodyToken = $this->registerRuleBodyToken($this->processRuleBody($ruleBody));
|
527 |
+
$ret .= '{'. $ruleBodyToken .'}';
|
528 |
+
$searchOffset = $blockEndPos + 1;
|
529 |
+
}
|
530 |
+
|
531 |
+
$substrOffset = $searchOffset;
|
532 |
+
}
|
533 |
+
|
534 |
+
$ret .= substr($css, $substrOffset);
|
535 |
+
|
536 |
+
return $ret;
|
537 |
+
}
|
538 |
+
|
539 |
+
/**
|
540 |
+
* Compresses non-group rule bodies.
|
541 |
+
* @param string $body The rule body without curly braces
|
542 |
+
* @return string
|
543 |
+
*/
|
544 |
+
private function processRuleBody($body)
|
545 |
+
{
|
546 |
+
$body = trim($body);
|
547 |
+
|
548 |
+
// Remove spaces before the things that should not have spaces before them.
|
549 |
+
$body = preg_replace('/ ([:=,)*\/;\n])/S', '$1', $body);
|
550 |
+
|
551 |
+
// Remove the spaces after the things that should not have spaces after them.
|
552 |
+
$body = preg_replace('/([:=,(*\/!;\n]) /S', '$1', $body);
|
553 |
+
|
554 |
+
// Replace multiple semi-colons in a row by a single one
|
555 |
+
$body = preg_replace('/;;+/S', ';', $body);
|
556 |
+
|
557 |
+
// Remove semicolon before closing brace except when:
|
558 |
+
// - The last property is prefixed with a `*` (lte IE7 hack) to avoid issues on Symbian S60 3.x browsers.
|
559 |
+
if (!preg_match('/\*[a-z0-9-]+:[^;]+;$/Si', $body)) {
|
560 |
+
$body = rtrim($body, ';');
|
561 |
+
}
|
562 |
+
|
563 |
+
// Remove important comments inside a rule body (because they make no sense here).
|
564 |
+
if (strpos($body, '/*') !== false) {
|
565 |
+
$body = preg_replace('/\n?\/\*[A-Z0-9_]+\*\/\n?/S', '', $body);
|
566 |
+
}
|
567 |
+
|
568 |
+
// Empty rule body? Exit :)
|
569 |
+
if (empty($body)) {
|
570 |
+
return '';
|
571 |
+
}
|
572 |
+
|
573 |
+
// Shorten font-weight values
|
574 |
+
$body = preg_replace(
|
575 |
+
array('/(font-weight:)bold\b/Si', '/(font-weight:)normal\b/Si'),
|
576 |
+
array('${1}700', '${1}400'),
|
577 |
+
$body
|
578 |
+
);
|
579 |
+
|
580 |
+
// Shorten background property
|
581 |
+
$body = preg_replace('/(background:)(?:none|transparent)( !|;|$)/Si', '${1}0 0$2', $body);
|
582 |
+
|
583 |
+
// Shorten opacity IE filter
|
584 |
+
$body = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $body);
|
585 |
+
|
586 |
+
// Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
|
587 |
+
// Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
|
588 |
+
// This makes it more likely that it'll get further compressed in the next step.
|
589 |
+
$body = preg_replace_callback(
|
590 |
+
'/(rgb|hsl)\(([0-9,.% -]+)\)(.|$)/Si',
|
591 |
+
array($this, 'shortenHslAndRgbToHexCallback'),
|
592 |
+
$body
|
593 |
+
);
|
594 |
+
|
595 |
+
// Shorten colors from #AABBCC to #ABC or shorter color name:
|
596 |
+
// - Look for hex colors which don't have a "=" in front of them (to avoid MSIE filters)
|
597 |
+
$body = preg_replace_callback(
|
598 |
+
'/(?<!=)#([0-9a-f]{3,6})( |,|\)|;|$)/Si',
|
599 |
+
array($this, 'shortenHexColorsCallback'),
|
600 |
+
$body
|
601 |
+
);
|
602 |
+
|
603 |
+
// Shorten long named colors with a shorter HEX counterpart: white -> #fff.
|
604 |
+
// Run at least 2 times to cover most cases
|
605 |
+
$body = preg_replace_callback(
|
606 |
+
array($this->namedToHexColorsRegex, $this->namedToHexColorsRegex),
|
607 |
+
array($this, 'shortenNamedColorsCallback'),
|
608 |
+
$body
|
609 |
+
);
|
610 |
+
|
611 |
+
// Replace positive sign from numbers before the leading space is removed.
|
612 |
+
// +1.2em to 1.2em, +.8px to .8px, +2% to 2%
|
613 |
+
$body = preg_replace('/([ :,(])\+(\.?\d+)/S', '$1$2', $body);
|
614 |
+
|
615 |
+
// shorten ms to s
|
616 |
+
$body = preg_replace_callback('/([ :,(])(-?)(\d{3,})ms/Si', function ($matches) {
|
617 |
+
return $matches[1] . $matches[2] . ((int) $matches[3] / 1000) .'s';
|
618 |
+
}, $body);
|
619 |
+
|
620 |
+
// Remove leading zeros from integer and float numbers.
|
621 |
+
// 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
|
622 |
+
$body = preg_replace('/([ :,(])(-?)0+([1-9]?\.?\d+)/S', '$1$2$3', $body);
|
623 |
+
|
624 |
+
// Remove trailing zeros from float numbers.
|
625 |
+
// -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
|
626 |
+
$body = preg_replace('/([ :,(])(-?\d?\.\d+?)0+([^\d])/S', '$1$2$3', $body);
|
627 |
+
|
628 |
+
// Remove trailing .0 -> -9.0 to -9
|
629 |
+
$body = preg_replace('/([ :,(])(-?\d+)\.0([^\d])/S', '$1$2$3', $body);
|
630 |
+
|
631 |
+
// Replace 0 length numbers with 0
|
632 |
+
$body = preg_replace('/([ :,(])-?\.?0+([^\d])/S', '${1}0$2', $body);
|
633 |
+
|
634 |
+
// Shorten zero values for safe properties only
|
635 |
+
$body = preg_replace(
|
636 |
+
array(
|
637 |
+
$this->shortenOneZeroesRegex,
|
638 |
+
$this->shortenTwoZeroesRegex,
|
639 |
+
$this->shortenThreeZeroesRegex,
|
640 |
+
$this->shortenFourZeroesRegex
|
641 |
+
),
|
642 |
+
array(
|
643 |
+
'$1$2:0',
|
644 |
+
'$1$2:$3 0',
|
645 |
+
'$1$2:$3 $4 0',
|
646 |
+
'$1$2:$3 $4 $5 0'
|
647 |
+
),
|
648 |
+
$body
|
649 |
+
);
|
650 |
+
|
651 |
+
// Replace 0 0 0; or 0 0 0 0; with 0 0 for background-position property.
|
652 |
+
$body = preg_replace('/(background-position):0(?: 0){2,3}( !|;|$)/Si', '$1:0 0$2', $body);
|
653 |
+
|
654 |
+
// Shorten suitable shorthand properties with repeated values
|
655 |
+
$body = preg_replace(
|
656 |
+
array(
|
657 |
+
'/(margin|padding|border-(?:width|radius)):('.$this->numRegex.')(?: \2)+( !|;|$)/Si',
|
658 |
+
'/(border-(?:style|color)):([#a-z0-9]+)(?: \2)+( !|;|$)/Si'
|
659 |
+
),
|
660 |
+
'$1:$2$3',
|
661 |
+
$body
|
662 |
+
);
|
663 |
+
$body = preg_replace(
|
664 |
+
array(
|
665 |
+
'/(margin|padding|border-(?:width|radius)):'.
|
666 |
+
'('.$this->numRegex.') ('.$this->numRegex.') \2 \3( !|;|$)/Si',
|
667 |
+
'/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) \2 \3( !|;|$)/Si'
|
668 |
+
),
|
669 |
+
'$1:$2 $3$4',
|
670 |
+
$body
|
671 |
+
);
|
672 |
+
$body = preg_replace(
|
673 |
+
array(
|
674 |
+
'/(margin|padding|border-(?:width|radius)):'.
|
675 |
+
'('.$this->numRegex.') ('.$this->numRegex.') ('.$this->numRegex.') \3( !|;|$)/Si',
|
676 |
+
'/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) ([#a-z0-9]+) \3( !|;|$)/Si'
|
677 |
+
),
|
678 |
+
'$1:$2 $3 $4$5',
|
679 |
+
$body
|
680 |
+
);
|
681 |
+
|
682 |
+
// Lowercase some common functions that can be values
|
683 |
+
$body = preg_replace_callback(
|
684 |
+
'/(?:attr|blur|brightness|circle|contrast|cubic-bezier|drop-shadow|ellipse|from|grayscale|'.
|
685 |
+
'hsla?|hue-rotate|inset|invert|local|minmax|opacity|perspective|polygon|rgba?|rect|repeat|saturate|sepia|'.
|
686 |
+
'steps|to|url|var|-webkit-gradient|'.
|
687 |
+
'(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|(?:repeating-)?(?:linear|radial)-gradient))\(/Si',
|
688 |
+
array($this, 'strtolowerCallback'),
|
689 |
+
$body
|
690 |
+
);
|
691 |
+
|
692 |
+
// Lowercase all uppercase properties
|
693 |
+
$body = preg_replace_callback('/(?:^|;)[A-Z-]+:/S', array($this, 'strtolowerCallback'), $body);
|
694 |
+
|
695 |
+
return $body;
|
696 |
+
}
|
697 |
+
|
698 |
+
/**
|
699 |
+
* Compresses At-rules and selectors.
|
700 |
+
* @param string $css the whole stylesheet with rule bodies tokenized.
|
701 |
+
* @return string
|
702 |
+
*/
|
703 |
+
private function processAtRulesAndSelectors($css)
|
704 |
+
{
|
705 |
+
$charset = '';
|
706 |
+
$imports = '';
|
707 |
+
$namespaces = '';
|
708 |
+
|
709 |
+
// Remove spaces before the things that should not have spaces before them.
|
710 |
+
$css = preg_replace('/ ([@{};>+)\]~=,\/\n])/S', '$1', $css);
|
711 |
+
|
712 |
+
// Remove the spaces after the things that should not have spaces after them.
|
713 |
+
$css = preg_replace('/([{}:;>+(\[~=,\/\n]) /S', '$1', $css);
|
714 |
+
|
715 |
+
// Shorten shortable double colon (CSS3) pseudo-elements to single colon (CSS2)
|
716 |
+
$css = preg_replace('/::(before|after|first-(?:line|letter))(\{|,)/Si', ':$1$2', $css);
|
717 |
+
|
718 |
+
// Retain space for special IE6 cases
|
719 |
+
$css = preg_replace_callback('/:first-(line|letter)(\{|,)/Si', function ($matches) {
|
720 |
+
return ':first-'. strtolower($matches[1]) .' '. $matches[2];
|
721 |
+
}, $css);
|
722 |
+
|
723 |
+
// Find a fraction that may used in some @media queries such as: (min-aspect-ratio: 1/1)
|
724 |
+
// Add token to add the "/" back in later
|
725 |
+
$css = preg_replace('/\(([a-z-]+):([0-9]+)\/([0-9]+)\)/Si', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
|
726 |
+
|
727 |
+
// Remove empty rule blocks up to 2 levels deep.
|
728 |
+
$css = preg_replace(array_fill(0, 2, '/(\{)[^{};\/\n]+\{\}/S'), '$1', $css);
|
729 |
+
$css = preg_replace('/[^{};\/\n]+\{\}/S', '', $css);
|
730 |
+
|
731 |
+
// Two important comments next to each other? Remove extra newline.
|
732 |
+
if ($this->keepImportantComments) {
|
733 |
+
$css = str_replace("\n\n", "\n", $css);
|
734 |
+
}
|
735 |
+
|
736 |
+
// Restore fraction
|
737 |
+
$css = str_replace(self::QUERY_FRACTION, '/', $css);
|
738 |
+
|
739 |
+
// Lowercase some popular @directives
|
740 |
+
$css = preg_replace_callback(
|
741 |
+
'/(?<!\\\\)@(?:charset|document|font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframes|media|'.
|
742 |
+
'namespace|page|supports|viewport)/Si',
|
743 |
+
array($this, 'strtolowerCallback'),
|
744 |
+
$css
|
745 |
+
);
|
746 |
+
|
747 |
+
// Lowercase some popular media types
|
748 |
+
$css = preg_replace_callback(
|
749 |
+
'/[ ,](?:all|aural|braille|handheld|print|projection|screen|tty|tv|embossed|speech)[ ,;{]/Si',
|
750 |
+
array($this, 'strtolowerCallback'),
|
751 |
+
$css
|
752 |
+
);
|
753 |
+
|
754 |
+
// Lowercase some common pseudo-classes & pseudo-elements
|
755 |
+
$css = preg_replace_callback(
|
756 |
+
'/(?<!\\\\):(?:active|after|before|checked|default|disabled|empty|enabled|first-(?:child|of-type)|'.
|
757 |
+
'focus(?:-within)?|hover|indeterminate|in-range|invalid|lang\(|last-(?:child|of-type)|left|link|not\(|'.
|
758 |
+
'nth-(?:child|of-type)\(|nth-last-(?:child|of-type)\(|only-(?:child|of-type)|optional|out-of-range|'.
|
759 |
+
'read-(?:only|write)|required|right|root|:selection|target|valid|visited)/Si',
|
760 |
+
array($this, 'strtolowerCallback'),
|
761 |
+
$css
|
762 |
+
);
|
763 |
+
|
764 |
+
// @charset handling
|
765 |
+
if (preg_match($this->charsetRegex, $css, $matches)) {
|
766 |
+
// Keep the first @charset at-rule found
|
767 |
+
$charset = $matches[0];
|
768 |
+
// Delete all @charset at-rules
|
769 |
+
$css = preg_replace($this->charsetRegex, '', $css);
|
770 |
+
}
|
771 |
+
|
772 |
+
// @import handling
|
773 |
+
$css = preg_replace_callback($this->importRegex, function ($matches) use (&$imports) {
|
774 |
+
// Keep all @import at-rules found for later
|
775 |
+
$imports .= $matches[0];
|
776 |
+
// Delete all @import at-rules
|
777 |
+
return '';
|
778 |
+
}, $css);
|
779 |
+
|
780 |
+
// @namespace handling
|
781 |
+
$css = preg_replace_callback($this->namespaceRegex, function ($matches) use (&$namespaces) {
|
782 |
+
// Keep all @namespace at-rules found for later
|
783 |
+
$namespaces .= $matches[0];
|
784 |
+
// Delete all @namespace at-rules
|
785 |
+
return '';
|
786 |
+
}, $css);
|
787 |
+
|
788 |
+
// Order critical at-rules:
|
789 |
+
// 1. @charset first
|
790 |
+
// 2. @imports below @charset
|
791 |
+
// 3. @namespaces below @imports
|
792 |
+
$css = $charset . $imports . $namespaces . $css;
|
793 |
+
|
794 |
+
return $css;
|
795 |
+
}
|
796 |
+
|
797 |
+
/**
|
798 |
+
* Splits long lines after a specific column.
|
799 |
+
*
|
800 |
+
* Some source control tools don't like it when files containing lines longer
|
801 |
+
* than, say 8000 characters, are checked in. The linebreak option is used in
|
802 |
+
* that case to split long lines after a specific column.
|
803 |
+
*
|
804 |
+
* @param string $css the whole stylesheet.
|
805 |
+
* @return string
|
806 |
+
*/
|
807 |
+
private function processLongLineSplitting($css)
|
808 |
+
{
|
809 |
+
if ($this->linebreakPosition > 0) {
|
810 |
+
$l = strlen($css);
|
811 |
+
$offset = $this->linebreakPosition;
|
812 |
+
while (preg_match('/(?<!\\\\)\}(?!\n)/S', $css, $matches, PREG_OFFSET_CAPTURE, $offset)) {
|
813 |
+
$matchIndex = $matches[0][1];
|
814 |
+
$css = substr_replace($css, "\n", $matchIndex + 1, 0);
|
815 |
+
$offset = $matchIndex + 2 + $this->linebreakPosition;
|
816 |
+
$l += 1;
|
817 |
+
if ($offset > $l) {
|
818 |
+
break;
|
819 |
+
}
|
820 |
+
}
|
821 |
+
}
|
822 |
+
|
823 |
+
return $css;
|
824 |
+
}
|
825 |
+
|
826 |
+
/**
|
827 |
+
* Converts hsl() & rgb() colors to HEX format.
|
828 |
+
* @param $matches
|
829 |
+
* @return string
|
830 |
+
*/
|
831 |
+
private function shortenHslAndRgbToHexCallback($matches)
|
832 |
+
{
|
833 |
+
$type = $matches[1];
|
834 |
+
$values = explode(',', $matches[2]);
|
835 |
+
$terminator = $matches[3];
|
836 |
+
|
837 |
+
if ($type === 'hsl') {
|
838 |
+
$values = Utils::hslToRgb($values);
|
839 |
+
}
|
840 |
+
|
841 |
+
$hexColors = Utils::rgbToHex($values);
|
842 |
+
|
843 |
+
// Restore space after rgb() or hsl() function in some cases such as:
|
844 |
+
// background-image: linear-gradient(to bottom, rgb(210,180,140) 10%, rgb(255,0,0) 90%);
|
845 |
+
if (!empty($terminator) && !preg_match('/[ ,);]/S', $terminator)) {
|
846 |
+
$terminator = ' '. $terminator;
|
847 |
+
}
|
848 |
+
|
849 |
+
return '#'. implode('', $hexColors) . $terminator;
|
850 |
+
}
|
851 |
+
|
852 |
+
/**
|
853 |
+
* Compresses HEX color values of the form #AABBCC to #ABC or short color name.
|
854 |
+
* @param $matches
|
855 |
+
* @return string
|
856 |
+
*/
|
857 |
+
private function shortenHexColorsCallback($matches)
|
858 |
+
{
|
859 |
+
$hex = $matches[1];
|
860 |
+
|
861 |
+
// Shorten suitable 6 chars HEX colors
|
862 |
+
if (strlen($hex) === 6 && preg_match('/^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/Si', $hex, $m)) {
|
863 |
+
$hex = $m[1] . $m[2] . $m[3];
|
864 |
+
}
|
865 |
+
|
866 |
+
// Lowercase
|
867 |
+
$hex = '#'. strtolower($hex);
|
868 |
+
|
869 |
+
// Replace Hex colors with shorter color names
|
870 |
+
$color = array_key_exists($hex, $this->hexToNamedColorsMap) ? $this->hexToNamedColorsMap[$hex] : $hex;
|
871 |
+
|
872 |
+
return $color . $matches[2];
|
873 |
+
}
|
874 |
+
|
875 |
+
/**
|
876 |
+
* Shortens all named colors with a shorter HEX counterpart for a set of safe properties
|
877 |
+
* e.g. white -> #fff
|
878 |
+
* @param array $matches
|
879 |
+
* @return string
|
880 |
+
*/
|
881 |
+
private function shortenNamedColorsCallback($matches)
|
882 |
+
{
|
883 |
+
return $matches[1] . $this->namedToHexColorsMap[strtolower($matches[2])] . $matches[3];
|
884 |
+
}
|
885 |
+
|
886 |
+
/**
|
887 |
+
* Makes a string lowercase
|
888 |
+
* @param array $matches
|
889 |
+
* @return string
|
890 |
+
*/
|
891 |
+
private function strtolowerCallback($matches)
|
892 |
+
{
|
893 |
+
return strtolower($matches[0]);
|
894 |
+
}
|
895 |
+
}
|
classes/external/php/yui-php-cssmin-bundled/Utils.php
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Autoptimize\tubalmartin\CssMin;
|
4 |
+
|
5 |
+
class Utils
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Clamps a number between a minimum and a maximum value.
|
9 |
+
* @param int|float $n the number to clamp
|
10 |
+
* @param int|float $min the lower end number allowed
|
11 |
+
* @param int|float $max the higher end number allowed
|
12 |
+
* @return int|float
|
13 |
+
*/
|
14 |
+
public static function clampNumber($n, $min, $max)
|
15 |
+
{
|
16 |
+
return min(max($n, $min), $max);
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Clamps a RGB color number outside the sRGB color space
|
21 |
+
* @param int|float $n the number to clamp
|
22 |
+
* @return int|float
|
23 |
+
*/
|
24 |
+
public static function clampNumberSrgb($n)
|
25 |
+
{
|
26 |
+
return self::clampNumber($n, 0, 255);
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Converts a HSL color into a RGB color
|
31 |
+
* @param array $hslValues
|
32 |
+
* @return array
|
33 |
+
*/
|
34 |
+
public static function hslToRgb($hslValues)
|
35 |
+
{
|
36 |
+
$h = floatval($hslValues[0]);
|
37 |
+
$s = floatval(str_replace('%', '', $hslValues[1]));
|
38 |
+
$l = floatval(str_replace('%', '', $hslValues[2]));
|
39 |
+
|
40 |
+
// Wrap and clamp, then fraction!
|
41 |
+
$h = ((($h % 360) + 360) % 360) / 360;
|
42 |
+
$s = self::clampNumber($s, 0, 100) / 100;
|
43 |
+
$l = self::clampNumber($l, 0, 100) / 100;
|
44 |
+
|
45 |
+
if ($s == 0) {
|
46 |
+
$r = $g = $b = self::roundNumber(255 * $l);
|
47 |
+
} else {
|
48 |
+
$v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
|
49 |
+
$v1 = (2 * $l) - $v2;
|
50 |
+
$r = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h + (1/3)));
|
51 |
+
$g = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h));
|
52 |
+
$b = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h - (1/3)));
|
53 |
+
}
|
54 |
+
|
55 |
+
return array($r, $g, $b);
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Tests and selects the correct formula for each RGB color channel
|
60 |
+
* @param $v1
|
61 |
+
* @param $v2
|
62 |
+
* @param $vh
|
63 |
+
* @return mixed
|
64 |
+
*/
|
65 |
+
public static function hueToRgb($v1, $v2, $vh)
|
66 |
+
{
|
67 |
+
$vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
|
68 |
+
|
69 |
+
if ($vh * 6 < 1) {
|
70 |
+
return $v1 + ($v2 - $v1) * 6 * $vh;
|
71 |
+
}
|
72 |
+
|
73 |
+
if ($vh * 2 < 1) {
|
74 |
+
return $v2;
|
75 |
+
}
|
76 |
+
|
77 |
+
if ($vh * 3 < 2) {
|
78 |
+
return $v1 + ($v2 - $v1) * ((2 / 3) - $vh) * 6;
|
79 |
+
}
|
80 |
+
|
81 |
+
return $v1;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Convert strings like "64M" or "30" to int values
|
86 |
+
* @param mixed $size
|
87 |
+
* @return int
|
88 |
+
*/
|
89 |
+
public static function normalizeInt($size)
|
90 |
+
{
|
91 |
+
if (is_string($size)) {
|
92 |
+
$letter = substr($size, -1);
|
93 |
+
$size = intval($size);
|
94 |
+
switch ($letter) {
|
95 |
+
case 'M':
|
96 |
+
case 'm':
|
97 |
+
return (int) $size * 1048576;
|
98 |
+
case 'K':
|
99 |
+
case 'k':
|
100 |
+
return (int) $size * 1024;
|
101 |
+
case 'G':
|
102 |
+
case 'g':
|
103 |
+
return (int) $size * 1073741824;
|
104 |
+
}
|
105 |
+
}
|
106 |
+
return (int) $size;
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Converts a string containing and RGB percentage value into a RGB integer value i.e. '90%' -> 229.5
|
111 |
+
* @param $rgbPercentage
|
112 |
+
* @return int
|
113 |
+
*/
|
114 |
+
public static function rgbPercentageToRgbInteger($rgbPercentage)
|
115 |
+
{
|
116 |
+
if (strpos($rgbPercentage, '%') !== false) {
|
117 |
+
$rgbPercentage = self::roundNumber(floatval(str_replace('%', '', $rgbPercentage)) * 2.55);
|
118 |
+
}
|
119 |
+
|
120 |
+
return intval($rgbPercentage, 10);
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Converts a RGB color into a HEX color
|
125 |
+
* @param array $rgbColors
|
126 |
+
* @return array
|
127 |
+
*/
|
128 |
+
public static function rgbToHex($rgbColors)
|
129 |
+
{
|
130 |
+
$hexColors = array();
|
131 |
+
|
132 |
+
// Values outside the sRGB color space should be clipped (0-255)
|
133 |
+
for ($i = 0, $l = count($rgbColors); $i < $l; $i++) {
|
134 |
+
$hexColors[$i] = sprintf("%02x", self::clampNumberSrgb(self::rgbPercentageToRgbInteger($rgbColors[$i])));
|
135 |
+
}
|
136 |
+
|
137 |
+
return $hexColors;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Rounds a number to its closest integer
|
142 |
+
* @param $n
|
143 |
+
* @return int
|
144 |
+
*/
|
145 |
+
public static function roundNumber($n)
|
146 |
+
{
|
147 |
+
return intval(round(floatval($n)), 10);
|
148 |
+
}
|
149 |
+
}
|
{classlesses → classes/external/php/yui-php-cssmin-bundled}/index.html
RENAMED
@@ -1 +1 @@
|
|
1 |
-
<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
|
1 |
+
<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
|
classes/static/toolbar.css
CHANGED
@@ -80,9 +80,9 @@
|
|
80 |
@-webkit-keyframes blink { 50% { visibility: hidden; }}
|
81 |
@keyframes blink { 50% { visibility: hidden; }}
|
82 |
|
83 |
-
|
84 |
/* Some cosmetic Toolbar things */
|
85 |
-
#wp-admin-bar-autoptimize table, #wp-admin-bar-autoptimize th, #wp-admin-bar-autoptimize td
|
86 |
{
|
87 |
border: 0px !important;
|
88 |
}
|
@@ -140,7 +140,7 @@
|
|
140 |
line-height: 16px !important;
|
141 |
}
|
142 |
|
143 |
-
#wp-admin-bar-autoptimize-cache-info .ab-item table
|
144 |
{
|
145 |
display: inline-block !important;
|
146 |
margin-left: 10px !important;
|
@@ -153,7 +153,7 @@
|
|
153 |
margin-top: 5px !important;
|
154 |
}
|
155 |
.autoptimize-radial-bar,
|
156 |
-
.autoptimize-radial-bar .mask,
|
157 |
.autoptimize-radial-bar .fill,
|
158 |
.autoptimize-radial-bar .shadow
|
159 |
{
|
@@ -198,7 +198,7 @@
|
|
198 |
|
199 |
top : 7px !important;
|
200 |
left : 0px !important;
|
201 |
-
|
202 |
overflow : hidden;
|
203 |
}
|
204 |
.autoptimize-radial-bar .numbers
|
@@ -208,7 +208,7 @@
|
|
208 |
font-size : 9px !important;
|
209 |
|
210 |
margin-top : -10px !important;
|
211 |
-
|
212 |
display : inline-block;
|
213 |
vertical-align : top;
|
214 |
text-align : center;
|
@@ -223,7 +223,7 @@
|
|
223 |
box-shadow : 3px 3px 5px rgba(0,0,0,0.3) inset !important;
|
224 |
}
|
225 |
|
226 |
-
.autoptimize-radial-bar .mask,
|
227 |
.autoptimize-radial-bar .fill,
|
228 |
.autoptimize-radial-bar .shadow,
|
229 |
.autoptimize-radial-bar .inset,
|
@@ -243,4 +243,4 @@
|
|
243 |
|
244 |
/* fixes for toolbar on frontend for other themes messing things up */
|
245 |
#wp-admin-bar-autoptimize tr{border:0 !important}
|
246 |
-
#wp-admin-bar-autoptimize td{background-color:#32373c !important}
|
80 |
@-webkit-keyframes blink { 50% { visibility: hidden; }}
|
81 |
@keyframes blink { 50% { visibility: hidden; }}
|
82 |
|
83 |
+
|
84 |
/* Some cosmetic Toolbar things */
|
85 |
+
#wp-admin-bar-autoptimize table, #wp-admin-bar-autoptimize th, #wp-admin-bar-autoptimize td
|
86 |
{
|
87 |
border: 0px !important;
|
88 |
}
|
140 |
line-height: 16px !important;
|
141 |
}
|
142 |
|
143 |
+
#wp-admin-bar-autoptimize-cache-info .ab-item table
|
144 |
{
|
145 |
display: inline-block !important;
|
146 |
margin-left: 10px !important;
|
153 |
margin-top: 5px !important;
|
154 |
}
|
155 |
.autoptimize-radial-bar,
|
156 |
+
.autoptimize-radial-bar .mask,
|
157 |
.autoptimize-radial-bar .fill,
|
158 |
.autoptimize-radial-bar .shadow
|
159 |
{
|
198 |
|
199 |
top : 7px !important;
|
200 |
left : 0px !important;
|
201 |
+
|
202 |
overflow : hidden;
|
203 |
}
|
204 |
.autoptimize-radial-bar .numbers
|
208 |
font-size : 9px !important;
|
209 |
|
210 |
margin-top : -10px !important;
|
211 |
+
|
212 |
display : inline-block;
|
213 |
vertical-align : top;
|
214 |
text-align : center;
|
223 |
box-shadow : 3px 3px 5px rgba(0,0,0,0.3) inset !important;
|
224 |
}
|
225 |
|
226 |
+
.autoptimize-radial-bar .mask,
|
227 |
.autoptimize-radial-bar .fill,
|
228 |
.autoptimize-radial-bar .shadow,
|
229 |
.autoptimize-radial-bar .inset,
|
243 |
|
244 |
/* fixes for toolbar on frontend for other themes messing things up */
|
245 |
#wp-admin-bar-autoptimize tr{border:0 !important}
|
246 |
+
#wp-admin-bar-autoptimize td{background-color:#32373c !important}
|
classlesses/autoptimizeCacheChecker.php
DELETED
@@ -1,65 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
3 |
-
|
4 |
-
/*
|
5 |
-
* cachechecker code
|
6 |
-
* new in AO 2.0
|
7 |
-
*
|
8 |
-
* daily cronned job (filter to change freq. + filter to disable)
|
9 |
-
* checks if cachesize is > 0.5GB (filter to change maxsize)
|
10 |
-
* if so an option is set
|
11 |
-
* if that option is set, notice on admin is shown
|
12 |
-
*
|
13 |
-
*/
|
14 |
-
|
15 |
-
if (is_admin()) {
|
16 |
-
add_action('plugins_loaded','ao_cachechecker_setup');
|
17 |
-
}
|
18 |
-
|
19 |
-
function ao_cachechecker_setup() {
|
20 |
-
$doCacheCheck = (bool) apply_filters( 'autoptimize_filter_cachecheck_do', true);
|
21 |
-
$cacheCheckSchedule = wp_get_schedule( 'ao_cachechecker' );
|
22 |
-
$AOCCfreq = apply_filters('autoptimize_filter_cachecheck_frequency','daily');
|
23 |
-
if (!in_array($AOCCfreq,array('hourly','daily','monthly'))) {
|
24 |
-
$AOCCfreq='daily';
|
25 |
-
}
|
26 |
-
if ( $doCacheCheck && ( !$cacheCheckSchedule || $cacheCheckSchedule !== $AOCCfreq ) ) {
|
27 |
-
wp_schedule_event(time(), $AOCCfreq, 'ao_cachechecker');
|
28 |
-
} else if ( $cacheCheckSchedule && !$doCacheCheck ) {
|
29 |
-
wp_clear_scheduled_hook( 'ao_cachechecker' );
|
30 |
-
}
|
31 |
-
}
|
32 |
-
|
33 |
-
add_action('ao_cachechecker', 'ao_cachechecker_cronjob');
|
34 |
-
function ao_cachechecker_cronjob() {
|
35 |
-
$maxSize = (int) apply_filters( "autoptimize_filter_cachecheck_maxsize", 536870912);
|
36 |
-
$doCacheCheck = (bool) apply_filters( "autoptimize_filter_cachecheck_do", true);
|
37 |
-
$statArr=autoptimizeCache::stats();
|
38 |
-
$cacheSize=round($statArr[1]);
|
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) {
|
50 |
-
error_log("Autoptimize could not send cache size warning mail.");
|
51 |
-
}
|
52 |
-
}
|
53 |
-
}
|
54 |
-
}
|
55 |
-
}
|
56 |
-
|
57 |
-
add_action('admin_notices', 'autoptimize_cachechecker_notice');
|
58 |
-
function autoptimize_cachechecker_notice() {
|
59 |
-
if ((bool) get_option("autoptimize_cachesize_notice",false)) {
|
60 |
-
echo '<div class="notice notice-warning"><p>';
|
61 |
-
_e('<strong>Autoptimize\'s cache size is getting big</strong>, consider purging the cache. Have a look at <a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank">the Autoptimize FAQ</a> to see how you can keep the cache size under control.', 'autoptimize' );
|
62 |
-
echo '</p></div>';
|
63 |
-
update_option("autoptimize_cachesize_notice",false);
|
64 |
-
}
|
65 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classlesses/autoptimizeExtra.php
DELETED
@@ -1,386 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
3 |
-
|
4 |
-
// initialize
|
5 |
-
if ( is_admin() ) {
|
6 |
-
add_action( 'admin_menu', 'autoptimize_extra_admin' );
|
7 |
-
add_filter( 'autoptimize_filter_settingsscreen_tabs','add_autoptimize_extra_tab' );
|
8 |
-
} else {
|
9 |
-
autoptimize_extra_init();
|
10 |
-
}
|
11 |
-
|
12 |
-
// get option
|
13 |
-
function autoptimize_extra_get_options() {
|
14 |
-
$_default_val = array("autoptimize_extra_checkbox_field_1"=>"0","autoptimize_extra_checkbox_field_0"=>"0","autoptimize_extra_radio_field_4"=>"1","autoptimize_extra_text_field_2"=>"","autoptimize_extra_text_field_3"=>"");
|
15 |
-
$_option_val = get_option( 'autoptimize_extra_settings' );
|
16 |
-
if (empty($_option_val)) {
|
17 |
-
$_option_val = $_default_val;
|
18 |
-
}
|
19 |
-
return $_option_val;
|
20 |
-
}
|
21 |
-
|
22 |
-
// frontend init
|
23 |
-
function autoptimize_extra_init() {
|
24 |
-
$autoptimize_extra_options = autoptimize_extra_get_options();
|
25 |
-
|
26 |
-
/* disable emojis */
|
27 |
-
if ( !empty($autoptimize_extra_options['autoptimize_extra_checkbox_field_1']) ) {
|
28 |
-
autoptimize_extra_disable_emojis();
|
29 |
-
}
|
30 |
-
|
31 |
-
/* remove version from query string */
|
32 |
-
if ( !empty($autoptimize_extra_options['autoptimize_extra_checkbox_field_0']) ) {
|
33 |
-
add_filter( 'script_loader_src', 'autoptimize_extra_remove_qs', 15, 1 );
|
34 |
-
add_filter( 'style_loader_src', 'autoptimize_extra_remove_qs', 15, 1 );
|
35 |
-
}
|
36 |
-
|
37 |
-
/*
|
38 |
-
* async JS
|
39 |
-
*
|
40 |
-
* is_plugin_active is not available in frontend by default
|
41 |
-
* cfr. https://codex.wordpress.org/Function_Reference/is_plugin_active
|
42 |
-
* so we need to source in wp-admin/includes/plugin.php
|
43 |
-
*/
|
44 |
-
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
45 |
-
$_asyncJSactive = false;
|
46 |
-
if ( function_exists('is_plugin_active') && is_plugin_active('async-javascript/async-javascript.php') ) {
|
47 |
-
$_asyncJSactive = true;
|
48 |
-
}
|
49 |
-
if ( !empty($autoptimize_extra_options['autoptimize_extra_text_field_3']) && $_asyncJSactive === false ) {
|
50 |
-
add_filter('autoptimize_filter_js_exclude','autoptimize_extra_async_js',10,1);
|
51 |
-
}
|
52 |
-
|
53 |
-
/* optimize google fonts */
|
54 |
-
if ( !empty( $autoptimize_extra_options['autoptimize_extra_radio_field_4'] ) && ( $autoptimize_extra_options['autoptimize_extra_radio_field_4'] != "1" ) ) {
|
55 |
-
add_filter( 'wp_resource_hints', 'autoptimize_extra_gfonts_remove_dnsprefetch', 10, 2 );
|
56 |
-
if ( $autoptimize_extra_options['autoptimize_extra_radio_field_4'] == "2" ) {
|
57 |
-
add_filter('autoptimize_filter_css_removables','autoptimize_extra_remove_gfonts',10,1);
|
58 |
-
} else {
|
59 |
-
add_filter('autoptimize_html_after_minify','autoptimize_extra_gfonts',10,1);
|
60 |
-
add_filter('autoptimize_extra_filter_tobepreconn','autoptimize_extra_preconnectgooglefonts',10,1);
|
61 |
-
}
|
62 |
-
}
|
63 |
-
|
64 |
-
/* preconnect */
|
65 |
-
if ( !empty($autoptimize_extra_options['autoptimize_extra_text_field_2']) || has_filter('autoptimize_extra_filter_tobepreconn') ) {
|
66 |
-
add_filter( 'wp_resource_hints', 'autoptimize_extra_preconnect', 10, 2 );
|
67 |
-
}
|
68 |
-
}
|
69 |
-
|
70 |
-
// disable emoji's functions
|
71 |
-
function autoptimize_extra_disable_emojis() {
|
72 |
-
// all actions related to emojis
|
73 |
-
remove_action( 'admin_print_styles', 'print_emoji_styles' );
|
74 |
-
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
|
75 |
-
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
|
76 |
-
remove_action( 'wp_print_styles', 'print_emoji_styles' );
|
77 |
-
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
|
78 |
-
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
|
79 |
-
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
|
80 |
-
|
81 |
-
// filter to remove TinyMCE emojis
|
82 |
-
add_filter( 'tiny_mce_plugins', 'autoptimize_extra_disable_emojis_tinymce' );
|
83 |
-
|
84 |
-
// and remove dns-prefetch for emoji
|
85 |
-
add_filter( 'wp_resource_hints', 'autoptimize_extra_emojis_remove_dns_prefetch', 10, 2 );
|
86 |
-
}
|
87 |
-
|
88 |
-
function autoptimize_extra_disable_emojis_tinymce( $plugins ) {
|
89 |
-
if ( is_array( $plugins ) ) {
|
90 |
-
return array_diff( $plugins, array( 'wpemoji' ) );
|
91 |
-
} else {
|
92 |
-
return array();
|
93 |
-
}
|
94 |
-
}
|
95 |
-
|
96 |
-
function autoptimize_extra_emojis_remove_dns_prefetch( $urls, $relation_type ) {
|
97 |
-
$_emoji_svg_url = apply_filters( 'emoji_svg_url', 'https://s.w.org/images/core/emoji/' );
|
98 |
-
|
99 |
-
return autoptimize_extra_remove_dns_prefetch( $urls, $relation_type, $_emoji_svg_url );
|
100 |
-
}
|
101 |
-
|
102 |
-
// remove query string function
|
103 |
-
function autoptimize_extra_remove_qs( $src ) {
|
104 |
-
if ( strpos($src, '?ver=') ) {
|
105 |
-
$src = remove_query_arg( 'ver', $src );
|
106 |
-
}
|
107 |
-
return $src;
|
108 |
-
}
|
109 |
-
|
110 |
-
// async function
|
111 |
-
function autoptimize_extra_async_js($in) {
|
112 |
-
$autoptimize_extra_options = autoptimize_extra_get_options();
|
113 |
-
|
114 |
-
// get exclusions
|
115 |
-
$AO_JSexclArrayIn = array();
|
116 |
-
if (!empty($in)) {
|
117 |
-
$AO_JSexclArrayIn = array_fill_keys(array_filter(array_map('trim',explode(",",$in))),"");
|
118 |
-
}
|
119 |
-
|
120 |
-
// get asyncs
|
121 |
-
$_fromSetting = $autoptimize_extra_options['autoptimize_extra_text_field_3'];
|
122 |
-
$AO_asynced_JS = array_fill_keys(array_filter(array_map('trim',explode(",",$_fromSetting))),"");
|
123 |
-
$AO_async_flag = apply_filters('autoptimize_filter_extra_async',"async");
|
124 |
-
foreach ($AO_asynced_JS as $JSkey => $JSvalue) {
|
125 |
-
$AO_asynced_JS[$JSkey] = $AO_async_flag;
|
126 |
-
}
|
127 |
-
|
128 |
-
// merge exclusions & asyncs in one array and return to AO API
|
129 |
-
$AO_excl_w_async = array_merge( $AO_JSexclArrayIn, $AO_asynced_JS );
|
130 |
-
return $AO_excl_w_async;
|
131 |
-
}
|
132 |
-
|
133 |
-
// preconnect function
|
134 |
-
function autoptimize_extra_preconnect($hints, $relation_type) {
|
135 |
-
$autoptimize_extra_options = autoptimize_extra_get_options();
|
136 |
-
|
137 |
-
// get setting and store in array
|
138 |
-
$_to_be_preconnected = array_filter(array_map('trim',explode(",",$autoptimize_extra_options['autoptimize_extra_text_field_2'])));
|
139 |
-
$_to_be_preconnected = apply_filters( 'autoptimize_extra_filter_tobepreconn', $_to_be_preconnected );
|
140 |
-
|
141 |
-
// walk array, extract domain and add to new array with crossorigin attribute
|
142 |
-
foreach ($_to_be_preconnected as $_preconn_single) {
|
143 |
-
$_preconn_parsed = parse_url($_preconn_single);
|
144 |
-
|
145 |
-
if ( is_array($_preconn_parsed) && empty($_preconn_parsed['scheme']) ) {
|
146 |
-
$_preconn_domain = "//".$_preconn_parsed['host'];
|
147 |
-
} else if ( is_array($_preconn_parsed) ) {
|
148 |
-
$_preconn_domain = $_preconn_parsed['scheme']."://".$_preconn_parsed['host'];
|
149 |
-
}
|
150 |
-
|
151 |
-
if ( !empty($_preconn_domain) ) {
|
152 |
-
$_preconn_hint = array('href' => $_preconn_domain);
|
153 |
-
// fonts don't get preconnected unless crossorigin flag is set, non-fonts don't get preconnected if origin flag is set
|
154 |
-
// so hardcode fonts.gstatic.com to come with crossorigin and have filter to add other domains if needed
|
155 |
-
$_preconn_crossorigin = apply_filters( 'autoptimize_extra_filter_preconn_crossorigin', array('https://fonts.gstatic.com') );
|
156 |
-
if ( in_array( $_preconn_domain, $_preconn_crossorigin ) ) {
|
157 |
-
$_preconn_hint['crossorigin'] = 'anonymous';
|
158 |
-
}
|
159 |
-
$_new_hints[] = $_preconn_hint;
|
160 |
-
}
|
161 |
-
}
|
162 |
-
|
163 |
-
// merge in wordpress' preconnect hints
|
164 |
-
if ( 'preconnect' === $relation_type && !empty($_new_hints) ) {
|
165 |
-
$hints = array_merge($hints, $_new_hints);
|
166 |
-
}
|
167 |
-
|
168 |
-
return $hints;
|
169 |
-
}
|
170 |
-
|
171 |
-
// google font functions
|
172 |
-
function autoptimize_extra_gfonts_remove_dnsprefetch ( $urls, $relation_type ) {
|
173 |
-
$_gfonts_url = "fonts.googleapis.com";
|
174 |
-
|
175 |
-
return autoptimize_extra_remove_dns_prefetch( $urls, $relation_type, $_gfonts_url );
|
176 |
-
}
|
177 |
-
|
178 |
-
function autoptimize_extra_remove_gfonts($in) {
|
179 |
-
// simply remove google fonts
|
180 |
-
return $in.", fonts.googleapis.com";
|
181 |
-
}
|
182 |
-
|
183 |
-
function autoptimize_extra_gfonts($in) {
|
184 |
-
$autoptimize_extra_options = autoptimize_extra_get_options();
|
185 |
-
|
186 |
-
// extract fonts, partly based on wp rocket's extraction code
|
187 |
-
$_without_comments = preg_replace( '/<!--(.*)-->/Uis', '', $in );
|
188 |
-
preg_match_all( '#<link(?:\s+(?:(?!href\s*=\s*)[^>])+)?(?:\s+href\s*=\s*([\'"])((?:https?:)?\/\/fonts\.googleapis\.com\/css(?:(?!\1).)+)\1)(?:\s+[^>]*)?>#iU', $_without_comments, $matches );
|
189 |
-
|
190 |
-
$i = 0;
|
191 |
-
$fontsCollection = array();
|
192 |
-
if ( ! $matches[2] ) {
|
193 |
-
return $in;
|
194 |
-
}
|
195 |
-
|
196 |
-
// store them in $fonts array
|
197 |
-
foreach ( $matches[2] as $font ) {
|
198 |
-
if ( ! preg_match( '/rel=["\']dns-prefetch["\']/', $matches[0][ $i ] ) ) {
|
199 |
-
// Get fonts name.
|
200 |
-
$font = str_replace( array( '%7C', '%7c' ) , '|', $font );
|
201 |
-
$font = explode( 'family=', $font );
|
202 |
-
$font = ( isset( $font[1] ) ) ? explode( '&', $font[1] ) : array();
|
203 |
-
// Add font to $fonts[$i] but make sure not to pollute with an empty family
|
204 |
-
$_thisfont = array_values( array_filter( explode( '|', reset( $font ) ) ) );
|
205 |
-
if ( !empty($_thisfont) ) {
|
206 |
-
$fontsCollection[$i]["fonts"] = $_thisfont;
|
207 |
-
// And add subset if any
|
208 |
-
$subset = ( is_array( $font ) ) ? end( $font ) : '';
|
209 |
-
if ( false !== strpos( $subset, 'subset=' ) ) {
|
210 |
-
$subset = explode( 'subset=', $subset );
|
211 |
-
$fontsCollection[$i]["subsets"] = explode( ',', $subset[1] );
|
212 |
-
}
|
213 |
-
}
|
214 |
-
// And remove Google Fonts.
|
215 |
-
$in = str_replace( $matches[0][ $i ], '', $in );
|
216 |
-
}
|
217 |
-
$i++;
|
218 |
-
}
|
219 |
-
|
220 |
-
$_fontsOut="";
|
221 |
-
if ( $autoptimize_extra_options['autoptimize_extra_radio_field_4'] == "3" ) {
|
222 |
-
// aggregate & link
|
223 |
-
$_fontsString="";
|
224 |
-
$_subsetString="";
|
225 |
-
foreach ($fontsCollection as $font) {
|
226 |
-
$_fontsString .= '|'.trim( implode( '|' , $font["fonts"] ), '|' );
|
227 |
-
if ( !empty( $font["subsets"] ) ) {
|
228 |
-
$_subsetString .= implode( ',', $font["subsets"] );
|
229 |
-
}
|
230 |
-
}
|
231 |
-
|
232 |
-
if (!empty($_subsetString)) {
|
233 |
-
$_fontsString = $_fontsString."#038;subset=".$_subsetString;
|
234 |
-
}
|
235 |
-
|
236 |
-
$_fontsString = str_replace( '|', '%7C', ltrim($_fontsString,'|') );
|
237 |
-
|
238 |
-
if ( ! empty( $_fontsString ) ) {
|
239 |
-
$_fontsOut = '<link rel="stylesheet" id="ao_optimized_gfonts" href="https://fonts.googleapis.com/css?family=' . $_fontsString . '" />';
|
240 |
-
}
|
241 |
-
} else if ( $autoptimize_extra_options['autoptimize_extra_radio_field_4'] == "4" ) {
|
242 |
-
// aggregate & load async (webfont.js impl.)
|
243 |
-
$_fontsArray = array();
|
244 |
-
foreach ($fontsCollection as $_fonts) {
|
245 |
-
if ( !empty( $_fonts["subsets"] ) ) {
|
246 |
-
$_subset = implode(",",$_fonts["subsets"]);
|
247 |
-
foreach ($_fonts["fonts"] as $key => $_one_font) {
|
248 |
-
$_one_font = $_one_font.":".$_subset;
|
249 |
-
$_fonts["fonts"][$key] = $_one_font;
|
250 |
-
}
|
251 |
-
}
|
252 |
-
$_fontsArray = array_merge($_fontsArray, $_fonts["fonts"]);
|
253 |
-
}
|
254 |
-
|
255 |
-
$_fontsOut = '<script data-cfasync="false" type="text/javascript">WebFontConfig={google:{families:[\'';
|
256 |
-
foreach ($_fontsArray as $_font) {
|
257 |
-
$_fontsOut .= $_font."','";
|
258 |
-
}
|
259 |
-
$_fontsOut = trim(trim($_fontsOut,"'"),",");
|
260 |
-
$_fontsOut .= '] },classes:false, events:false, timeout:1500};(function() {var wf = document.createElement(\'script\');wf.src=\'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js\';wf.type=\'text/javascript\';wf.defer=\'true\';var s=document.getElementsByTagName(\'script\')[0];s.parentNode.insertBefore(wf, s);})();</script>';
|
261 |
-
}
|
262 |
-
|
263 |
-
// inject in HTML
|
264 |
-
$out = substr_replace($in, $_fontsOut."<link", strpos($in, "<link"), strlen("<link"));
|
265 |
-
return $out;
|
266 |
-
}
|
267 |
-
|
268 |
-
function autoptimize_extra_preconnectgooglefonts($in) {
|
269 |
-
$autoptimize_extra_options = autoptimize_extra_get_options();
|
270 |
-
|
271 |
-
// preconnect to fonts.gstatic.com speed up download of static font-files
|
272 |
-
$in[] = "https://fonts.gstatic.com";
|
273 |
-
if ( $autoptimize_extra_options['autoptimize_extra_radio_field_4'] == "4" ) {
|
274 |
-
// and more preconnects for webfont.js
|
275 |
-
$in[] = "https://ajax.googleapis.com";
|
276 |
-
$in[] = "https://fonts.googleapis.com";
|
277 |
-
}
|
278 |
-
return $in;
|
279 |
-
}
|
280 |
-
|
281 |
-
function autoptimize_extra_remove_dns_prefetch( $urls, $relation_type, $_remove_url ) {
|
282 |
-
if ( 'dns-prefetch' == $relation_type ) {
|
283 |
-
$_count=0;
|
284 |
-
foreach ($urls as $_url) {
|
285 |
-
if ( strpos($_url, $_remove_url) !== false ) {
|
286 |
-
unset($urls[$_count]);
|
287 |
-
}
|
288 |
-
$_count++;
|
289 |
-
}
|
290 |
-
}
|
291 |
-
|
292 |
-
return $urls;
|
293 |
-
}
|
294 |
-
|
295 |
-
/* admin page functions */
|
296 |
-
function autoptimize_extra_admin() {
|
297 |
-
add_submenu_page( null, 'autoptimize_extra', 'autoptimize_extra', 'manage_options', 'autoptimize_extra', 'autoptimize_extra_options_page' );
|
298 |
-
register_setting( 'autoptimize_extra_settings', 'autoptimize_extra_settings' );
|
299 |
-
}
|
300 |
-
|
301 |
-
function add_autoptimize_extra_tab($in) {
|
302 |
-
$in=array_merge($in,array('autoptimize_extra' => __('Extra','autoptimize')));
|
303 |
-
return $in;
|
304 |
-
}
|
305 |
-
|
306 |
-
function autoptimize_extra_options_page() {
|
307 |
-
$autoptimize_extra_options = autoptimize_extra_get_options();
|
308 |
-
$_googlef = $autoptimize_extra_options['autoptimize_extra_radio_field_4'];
|
309 |
-
?>
|
310 |
-
<style>
|
311 |
-
#ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
|
312 |
-
#ao_settings_form .form-table th {font-weight: 100;}
|
313 |
-
#autoptimize_extra_descr{font-size: 120%;}
|
314 |
-
</style>
|
315 |
-
<div class="wrap">
|
316 |
-
<h1><?php _e('Autoptimize Settings','autoptimize'); ?></h1>
|
317 |
-
<?php echo autoptimizeConfig::ao_admin_tabs(); ?>
|
318 |
-
<form id='ao_settings_form' action='options.php' method='post'>
|
319 |
-
<?php settings_fields('autoptimize_extra_settings'); ?>
|
320 |
-
<h2><?php _e('Extra Auto-Optimizations','autoptimize'); ?></h2>
|
321 |
-
<span id='autoptimize_extra_descr'><?php _e('The following settings can improve your site\'s performance even more.','autoptimize'); ?></span>
|
322 |
-
<table class="form-table">
|
323 |
-
<tr>
|
324 |
-
<th scope="row"><?php _e('Remove emojis','autoptimize'); ?></th>
|
325 |
-
<td>
|
326 |
-
<label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_1]' <?php if (!empty($autoptimize_extra_options['autoptimize_extra_checkbox_field_1']) && 1 == $autoptimize_extra_options['autoptimize_extra_checkbox_field_1']) echo 'checked="checked"'; ?> value='1'><?php _e('Removes WordPress\' core emojis\' inline CSS, inline JavaScript, and an otherwise un-autoptimized JavaScript file.','autoptimize'); ?></label>
|
327 |
-
</td>
|
328 |
-
</tr>
|
329 |
-
<tr>
|
330 |
-
<th scope="row"><?php _e('Remove query strings from static resources','autoptimize'); ?></th>
|
331 |
-
<td>
|
332 |
-
<label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_0]' <?php if (!empty( $autoptimize_extra_options['autoptimize_extra_checkbox_field_0']) && 1 == $autoptimize_extra_options['autoptimize_extra_checkbox_field_0']) echo 'checked="checked"'; ?> value='1'><?php _e('Removing query strings (or more specificaly the <code>ver</code> parameter) will not improve load time, but might improve performance scores.','autoptimize'); ?></label>
|
333 |
-
</td>
|
334 |
-
</tr>
|
335 |
-
<tr>
|
336 |
-
<th scope="row"><?php _e('Google Fonts','autoptimize'); ?></th>
|
337 |
-
<td>
|
338 |
-
<input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="1" <?php if (!in_array($_googlef,array(2,3,4))) {echo "checked"; } ?> ><?php _e('Leave as is','autoptimize'); ?><br/>
|
339 |
-
<input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="2" <?php checked(2, $_googlef, true); ?> ><?php _e('Remove Google Fonts','autoptimize'); ?><br/>
|
340 |
-
<input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="3" <?php checked(3, $_googlef, true); ?> ><?php _e('Combine and link in head','autoptimize'); ?><br/>
|
341 |
-
<input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="4" <?php checked(4, $_googlef, true); ?> ><?php _e('Combine and load fonts asynchronously with <a href="https://github.com/typekit/webfontloader#readme" target="_blank">webfont.js</a>','autoptimize'); ?><br/>
|
342 |
-
</td>
|
343 |
-
</tr>
|
344 |
-
<tr>
|
345 |
-
<th scope="row"><?php _e('Preconnect to 3rd party domains <em>(advanced users)</em>','autoptimize'); ?></th>
|
346 |
-
<td>
|
347 |
-
<label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_2]' value='<?php echo $autoptimize_extra_options['autoptimize_extra_text_field_2']; ?>'><br /><?php _e('Add 3rd party domains you want the browser to <a href="https://www.keycdn.com/support/preconnect/#primary" target="_blank">preconnect</a> to, separated by comma\'s. Make sure to include the correct protocol (HTTP or HTTPS).','autoptimize'); ?></label>
|
348 |
-
</td>
|
349 |
-
</tr>
|
350 |
-
<tr>
|
351 |
-
<th scope="row"><?php _e('Async Javascript-files <em>(advanced users)</em>','autoptimize'); ?></th>
|
352 |
-
<td>
|
353 |
-
<?php if ( function_exists('is_plugin_active') && is_plugin_active('async-javascript/async-javascript.php') ) {
|
354 |
-
_e('You have "Async JavaScript" installed,','autoptimize');
|
355 |
-
$asj_config_url="options-general.php?page=async-javascript";
|
356 |
-
echo sprintf(' <a href="'.$asj_config_url.'">%s</a>', __('configuration of async javascript is best done there.','autoptimize'));
|
357 |
-
} else { ?>
|
358 |
-
<input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_3]' value='<?php echo $autoptimize_extra_options['autoptimize_extra_text_field_3']; ?>'>
|
359 |
-
<br />
|
360 |
-
<?php
|
361 |
-
_e('Comma-separated list of local or 3rd party JS-files that should loaded with the <code>async</code> flag. JS-files from your own site will be automatically excluded if added here. ','autoptimize');
|
362 |
-
echo sprintf( __('Configuration of async javascript is easier and more flexible using the %s plugin.','autoptimize'), '"<a href="https://wordpress.org/plugins/async-javascript" target="_blank">Async Javascript</a>"');
|
363 |
-
$asj_install_url= network_admin_url()."plugin-install.php?s=async+javascript&tab=search&type=term";
|
364 |
-
echo sprintf(' <a href="'.$asj_install_url.'">%s</a>', __('Click here to install and activate it.','autoptimize'));
|
365 |
-
} ?>
|
366 |
-
</td>
|
367 |
-
</tr>
|
368 |
-
<tr>
|
369 |
-
<th scope="row"><?php _e('Optimize YouTube videos','autoptimize'); ?></th>
|
370 |
-
<td>
|
371 |
-
<?php if ( function_exists('is_plugin_active') && is_plugin_active('wp-youtube-lyte/wp-youtube-lyte.php') ) {
|
372 |
-
_e('Great, you have WP YouTube Lyte installed.','autoptimize');
|
373 |
-
$lyte_config_url="options-general.php?page=lyte_settings_page";
|
374 |
-
echo sprintf(' <a href="'.$lyte_config_url.'">%s</a>', __('Click here to configure it.','autoptimize'));
|
375 |
-
} else {
|
376 |
-
echo sprintf( __('%s allows you to “lazy load” your videos, by inserting responsive “Lite YouTube Embeds". ','autoptimize'),'<a href="https://wordpress.org/plugins/wp-youtube-lyte" target="_blank">WP YouTube Lyte</a>');
|
377 |
-
$lyte_install_url= network_admin_url()."plugin-install.php?s=lyte&tab=search&type=term";
|
378 |
-
echo sprintf(' <a href="'.$lyte_install_url.'">%s</a>', __('Click here to install and activate it.','autoptimize'));
|
379 |
-
} ?>
|
380 |
-
</td>
|
381 |
-
</tr>
|
382 |
-
</table>
|
383 |
-
<p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e('Save Changes','autoptimize') ?>" /></p>
|
384 |
-
</form>
|
385 |
-
<?php
|
386 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classlesses/autoptimizePageCacheFlush.php
DELETED
@@ -1,56 +0,0 @@
|
|
1 |
-
<?php
|
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()) {
|
10 |
-
$blog_id = get_current_blog_id();
|
11 |
-
wp_cache_clear_cache($blog_id);
|
12 |
-
} else {
|
13 |
-
wp_cache_clear_cache();
|
14 |
-
}
|
15 |
-
} else if ( has_action('cachify_flush_cache') ) {
|
16 |
-
do_action('cachify_flush_cache');
|
17 |
-
} else if ( function_exists('w3tc_pgcache_flush') ) {
|
18 |
-
w3tc_pgcache_flush();
|
19 |
-
} else if ( function_exists('wp_fast_cache_bulk_delete_all') ) {
|
20 |
-
wp_fast_cache_bulk_delete_all(); // still to retest
|
21 |
-
} else if (class_exists("WpFastestCache")) {
|
22 |
-
$wpfc = new WpFastestCache();
|
23 |
-
$wpfc -> deleteCache();
|
24 |
-
} else if ( class_exists("c_ws_plugin__qcache_purging_routines") ) {
|
25 |
-
c_ws_plugin__qcache_purging_routines::purge_cache_dir(); // quick cache, still to retest
|
26 |
-
} else if ( class_exists("zencache") ) {
|
27 |
-
zencache::clear();
|
28 |
-
} else if ( class_exists("comet_cache") ) {
|
29 |
-
comet_cache::clear();
|
30 |
-
} else if ( class_exists("WpeCommon") ) {
|
31 |
-
if ( apply_filters('autoptimize_flush_wpengine_aggressive', false) ) {
|
32 |
-
if ( method_exists( "WpeCommon", "purge_memcached" ) ) {
|
33 |
-
WpeCommon::purge_memcached();
|
34 |
-
}
|
35 |
-
if ( method_exists( "WpeCommon", "clear_maxcdn_cache" ) ) {
|
36 |
-
WpeCommon::clear_maxcdn_cache();
|
37 |
-
}
|
38 |
-
}
|
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;
|
47 |
-
if (is_multisite()) {
|
48 |
-
$blog_id = get_current_blog_id();
|
49 |
-
prune_super_cache( get_supercache_dir( $blog_id ), true );
|
50 |
-
prune_super_cache( $cache_path . 'blogs/', true );
|
51 |
-
} else {
|
52 |
-
prune_super_cache($cache_path.'supercache/',true);
|
53 |
-
prune_super_cache($cache_path,true);
|
54 |
-
}
|
55 |
-
}
|
56 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classlesses/autoptimizePartners.php
DELETED
@@ -1,122 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/*
|
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)) {
|
11 |
-
add_filter('autoptimize_filter_settingsscreen_tabs','ao_add_partner_tabs');
|
12 |
-
}
|
13 |
-
}
|
14 |
-
|
15 |
-
function ao_add_partner_tabs($in) {
|
16 |
-
$in=array_merge($in,array('ao_partners' => __('Optimize More!','autoptimize')));
|
17 |
-
return $in;
|
18 |
-
}
|
19 |
-
|
20 |
-
add_action('admin_menu','ao_partners_init');
|
21 |
-
function ao_partners_init() {
|
22 |
-
if (apply_filters('autoptimize_filter_show_partner_tabs',true)) {
|
23 |
-
$hook=add_submenu_page(NULL,'AO partner','AO partner','manage_options','ao_partners','ao_partners');
|
24 |
-
// register_settings here as well if needed
|
25 |
-
}
|
26 |
-
}
|
27 |
-
|
28 |
-
function ao_partners() {
|
29 |
-
?>
|
30 |
-
<style>
|
31 |
-
.itemDetail {
|
32 |
-
background: #fff;
|
33 |
-
width: 250px;
|
34 |
-
min-height: 290px;
|
35 |
-
border: 1px solid #ccc;
|
36 |
-
float: left;
|
37 |
-
padding: 15px;
|
38 |
-
position: relative;
|
39 |
-
margin: 0 10px 10px 0;
|
40 |
-
}
|
41 |
-
.itemTitle {
|
42 |
-
margin-top:0px;
|
43 |
-
margin-bottom:10px;
|
44 |
-
}
|
45 |
-
.itemImage {
|
46 |
-
text-align: center;
|
47 |
-
}
|
48 |
-
.itemImage img {
|
49 |
-
max-width: 95%;
|
50 |
-
max-height: 150px;
|
51 |
-
}
|
52 |
-
.itemDescription {
|
53 |
-
margin-bottom:30px;
|
54 |
-
}
|
55 |
-
.itemButtonRow {
|
56 |
-
position: absolute;
|
57 |
-
bottom: 10px;
|
58 |
-
right: 10px;
|
59 |
-
width:100%;
|
60 |
-
}
|
61 |
-
.itemButton {
|
62 |
-
float:right;
|
63 |
-
}
|
64 |
-
.itemButton a {
|
65 |
-
text-decoration: none;
|
66 |
-
color: #555;
|
67 |
-
}
|
68 |
-
.itemButton a:hover {
|
69 |
-
text-decoration: none;
|
70 |
-
color: #23282d;
|
71 |
-
}
|
72 |
-
</style>
|
73 |
-
<div class="wrap">
|
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(); ?>
|
81 |
-
</div>
|
82 |
-
</div>
|
83 |
-
<?php
|
84 |
-
}
|
85 |
-
|
86 |
-
function getAOPartnerFeed() {
|
87 |
-
$noFeedText=__( 'Have a look at <a href="http://optimizingmatters.com/">optimizingmatters.com</a> for Autoptimize power-ups!', 'autoptimize' );
|
88 |
-
|
89 |
-
if (apply_filters('autoptimize_settingsscreen_remotehttp',true)) {
|
90 |
-
$rss = fetch_feed( "http://feeds.feedburner.com/OptimizingMattersDownloads" );
|
91 |
-
$maxitems = 0;
|
92 |
-
|
93 |
-
if ( ! is_wp_error( $rss ) ) {
|
94 |
-
$maxitems = $rss->get_item_quantity( 20 );
|
95 |
-
$rss_items = $rss->get_items( 0, $maxitems );
|
96 |
-
} ?>
|
97 |
-
<ul>
|
98 |
-
<?php
|
99 |
-
if ( $maxitems == 0 ) {
|
100 |
-
echo $noFeedText;
|
101 |
-
} else {
|
102 |
-
foreach ( $rss_items as $item ) :
|
103 |
-
$itemURL = esc_url( $item->get_permalink() ); ?>
|
104 |
-
<li class="itemDetail">
|
105 |
-
<h3 class="itemTitle"><a href="<?php echo $itemURL; ?>" target="_blank"><?php echo esc_html( $item->get_title() ); ?></a></h3>
|
106 |
-
<?php
|
107 |
-
if (($enclosure = $item->get_enclosure()) && (strpos($enclosure->get_type(),"image")!==false) ) {
|
108 |
-
$itemImgURL=esc_url($enclosure->get_link());
|
109 |
-
echo "<div class=\"itemImage\"><a href=\"".$itemURL."\" target=\"_blank\"><img src=\"".$itemImgURL."\"/></a></div>";
|
110 |
-
}
|
111 |
-
?>
|
112 |
-
<div class="itemDescription"><?php echo wp_kses_post($item -> get_description() ); ?></div>
|
113 |
-
<div class="itemButtonRow"><div class="itemButton button-secondary"><a href="<?php echo $itemURL; ?>" target="_blank"><?php _e('More info','autoptimize'); ?></a></div></div>
|
114 |
-
</li>
|
115 |
-
<?php endforeach; ?>
|
116 |
-
<?php } ?>
|
117 |
-
</ul>
|
118 |
-
<?php
|
119 |
-
} else {
|
120 |
-
echo $noFeedText;
|
121 |
-
}
|
122 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classlesses/autoptimizeSpeedupper.php
DELETED
@@ -1,105 +0,0 @@
|
|
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_snippetcacher($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 |
-
|
34 |
-
if ( (substr($scriptsrc,-1,1)!==";") && (substr($scriptsrc,-1,1)!=="}") ) {
|
35 |
-
$scriptsrc.=";";
|
36 |
-
}
|
37 |
-
|
38 |
-
if ( !empty($jsfilename) && str_replace( apply_filters('autoptimize_filter_js_speedup_cache',false), '', $jsfilename ) === $jsfilename ) {
|
39 |
-
// don't cache inline CSS or if filter says no
|
40 |
-
$ccheck->cache($scriptsrc,'text/javascript');
|
41 |
-
}
|
42 |
-
}
|
43 |
-
unset($ccheck);
|
44 |
-
|
45 |
-
return $scriptsrc;
|
46 |
-
}
|
47 |
-
|
48 |
-
function ao_css_snippetcacher($cssin,$cssfilename) {
|
49 |
-
$md5hash = "snippet_".md5($cssin);
|
50 |
-
$ccheck = new autoptimizeCache($md5hash,'css');
|
51 |
-
if($ccheck->check()) {
|
52 |
-
$stylesrc = $ccheck->retrieve();
|
53 |
-
} else {
|
54 |
-
if ( ( strpos($cssfilename,"min.css") === false ) && ( str_replace( apply_filters('autoptimize_filter_css_consider_minified',false), '', $cssfilename ) === $cssfilename ) ) {
|
55 |
-
if (class_exists('Minify_CSS_Compressor')) {
|
56 |
-
$tmp_code = trim(Minify_CSS_Compressor::process($cssin));
|
57 |
-
} else if(class_exists('CSSmin')) {
|
58 |
-
$cssmin = new CSSmin();
|
59 |
-
if (method_exists($cssmin,"run")) {
|
60 |
-
$tmp_code = trim($cssmin->run($cssin));
|
61 |
-
} elseif (@is_callable(array($cssmin,"minify"))) {
|
62 |
-
$tmp_code = trim(CssMin::minify($cssin));
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
if (!empty($tmp_code)) {
|
67 |
-
$stylesrc = $tmp_code;
|
68 |
-
unset($tmp_code);
|
69 |
-
} else {
|
70 |
-
$stylesrc = $cssin;
|
71 |
-
}
|
72 |
-
} else {
|
73 |
-
// .min.css -> no heavy-lifting, just some cleanup
|
74 |
-
$stylesrc=preg_replace("#^\s*\/\*[^!].*\*\/\s?#Us","",$cssin);
|
75 |
-
$stylesrc=preg_replace("#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $stylesrc);
|
76 |
-
$stylesrc=autoptimizeStyles::fixurls($cssfilename,$stylesrc);
|
77 |
-
}
|
78 |
-
if ( !empty($cssfilename) && ( str_replace( apply_filters('autoptimize_filter_css_speedup_cache',false), '', $cssfilename ) === $cssfilename ) ) {
|
79 |
-
// only cache CSS if not inline and allowed by filter
|
80 |
-
$ccheck->cache($stylesrc,'text/css');
|
81 |
-
}
|
82 |
-
}
|
83 |
-
unset($ccheck);
|
84 |
-
return $stylesrc;
|
85 |
-
}
|
86 |
-
|
87 |
-
function ao_css_speedup_cleanup($cssin) {
|
88 |
-
// speedupper results in aggregated CSS not being minified, so the filestart-marker AO adds when aggregating need to be removed
|
89 |
-
return trim(str_replace(array('/*FILESTART*/','/*FILESTART2*/'),'',$cssin));
|
90 |
-
}
|
91 |
-
|
92 |
-
function ao_js_speedup_cleanup($jsin) {
|
93 |
-
// cleanup
|
94 |
-
return trim($jsin);
|
95 |
-
}
|
96 |
-
|
97 |
-
// conditionally attach filters
|
98 |
-
if ( apply_filters('autoptimize_css_do_minify',true) ) {
|
99 |
-
add_filter('autoptimize_css_individual_style','ao_css_snippetcacher',10,2);
|
100 |
-
add_filter('autoptimize_css_after_minify','ao_css_speedup_cleanup',10,1);
|
101 |
-
}
|
102 |
-
if ( apply_filters('autoptimize_js_do_minify',true) ) {
|
103 |
-
add_filter('autoptimize_js_individual_script','ao_js_snippetcacher',10,2);
|
104 |
-
add_filter('autoptimize_js_after_minify','ao_js_speedup_cleanup',10,1);
|
105 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classlesses/autoptimizeUpdateCode.php
DELETED
@@ -1,106 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/*
|
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 |
-
|
11 |
-
switch($autoptimize_major_version) {
|
12 |
-
case "1.6":
|
13 |
-
// from back in the days when I did not yet consider multisite
|
14 |
-
// if user was on version 1.6.x, force advanced options to be shown by default
|
15 |
-
update_option('autoptimize_show_adv','1');
|
16 |
-
|
17 |
-
// and remove old options
|
18 |
-
$to_delete_options=array("autoptimize_cdn_css","autoptimize_cdn_css_url","autoptimize_cdn_js","autoptimize_cdn_js_url","autoptimize_cdn_img","autoptimize_cdn_img_url","autoptimize_css_yui","autoptimize_js_yui");
|
19 |
-
foreach ($to_delete_options as $del_opt) {
|
20 |
-
delete_option( $del_opt );
|
21 |
-
}
|
22 |
-
$majorUp = true;
|
23 |
-
case "1.7":
|
24 |
-
// force 3.8 dashicons in CSS exclude options when upgrading from 1.7 to 1.8
|
25 |
-
if ( !is_multisite() ) {
|
26 |
-
$css_exclude = get_option('autoptimize_css_exclude');
|
27 |
-
if (empty($css_exclude)) {
|
28 |
-
$css_exclude = "admin-bar.min.css, dashicons.min.css";
|
29 |
-
} else if (strpos($css_exclude,"dashicons.min.css")===false) {
|
30 |
-
$css_exclude .= ", dashicons.min.css";
|
31 |
-
}
|
32 |
-
update_option('autoptimize_css_exclude',$css_exclude);
|
33 |
-
} else {
|
34 |
-
global $wpdb;
|
35 |
-
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
36 |
-
$original_blog_id = get_current_blog_id();
|
37 |
-
foreach ( $blog_ids as $blog_id ) {
|
38 |
-
switch_to_blog( $blog_id );
|
39 |
-
$css_exclude = get_option('autoptimize_css_exclude');
|
40 |
-
if (empty($css_exclude)) {
|
41 |
-
$css_exclude = "admin-bar.min.css, dashicons.min.css";
|
42 |
-
} else if (strpos($css_exclude,"dashicons.min.css")===false) {
|
43 |
-
$css_exclude .= ", dashicons.min.css";
|
44 |
-
}
|
45 |
-
update_option('autoptimize_css_exclude',$css_exclude);
|
46 |
-
}
|
47 |
-
switch_to_blog( $original_blog_id );
|
48 |
-
}
|
49 |
-
$majorUp = true;
|
50 |
-
case "1.9":
|
51 |
-
/*
|
52 |
-
* 2.0 will not aggregate inline CSS/JS by default, but we want users
|
53 |
-
* upgrading from 1.9 to keep their inline code aggregated by default.
|
54 |
-
*/
|
55 |
-
if ( !is_multisite() ) {
|
56 |
-
update_option('autoptimize_css_include_inline','on');
|
57 |
-
update_option('autoptimize_js_include_inline','on');
|
58 |
-
} else {
|
59 |
-
global $wpdb;
|
60 |
-
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
61 |
-
$original_blog_id = get_current_blog_id();
|
62 |
-
foreach ( $blog_ids as $blog_id ) {
|
63 |
-
switch_to_blog( $blog_id );
|
64 |
-
update_option('autoptimize_css_include_inline','on');
|
65 |
-
update_option('autoptimize_js_include_inline','on');
|
66 |
-
}
|
67 |
-
switch_to_blog( $original_blog_id );
|
68 |
-
}
|
69 |
-
$majorUp = true;
|
70 |
-
case "2.2":
|
71 |
-
/*
|
72 |
-
* 2.3 has no "remove google fonts" in main screen, moved to "extra"
|
73 |
-
*/
|
74 |
-
if ( !is_multisite() ) {
|
75 |
-
$_nogooglefont = get_option('autoptimize_css_nogooglefont','');
|
76 |
-
$_ao_extrasetting = get_option('autoptimize_extra_settings','');
|
77 |
-
if ( ($_nogooglefont == 1) && ( empty($_ao_extrasetting) ) ) {
|
78 |
-
$_aoextra_removegfonts = array("autoptimize_extra_checkbox_field_1"=>"0","autoptimize_extra_checkbox_field_0"=>"0","autoptimize_extra_radio_field_4"=>"1","autoptimize_extra_text_field_2"=>"","autoptimize_extra_text_field_3"=>"");
|
79 |
-
update_option( 'autoptimize_extra_settings', $_aoextra_removegfonts );
|
80 |
-
}
|
81 |
-
delete_option('autoptimize_css_nogooglefont');
|
82 |
-
} else {
|
83 |
-
global $wpdb;
|
84 |
-
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
85 |
-
$original_blog_id = get_current_blog_id();
|
86 |
-
foreach ( $blog_ids as $blog_id ) {
|
87 |
-
switch_to_blog( $blog_id );
|
88 |
-
$_nogooglefont = get_option('autoptimize_css_nogooglefont','');
|
89 |
-
$_ao_extrasetting = get_option('autoptimize_extra_settings','');
|
90 |
-
if ( ($_nogooglefont == 1) && ( empty($_ao_extrasetting) ) ) {
|
91 |
-
$_aoextra_removegfonts = array("autoptimize_extra_checkbox_field_1"=>"0","autoptimize_extra_checkbox_field_0"=>"0","autoptimize_extra_radio_field_4"=>"1","autoptimize_extra_text_field_2"=>"","autoptimize_extra_text_field_3"=>"");
|
92 |
-
update_option( 'autoptimize_extra_settings', $_aoextra_removegfonts );
|
93 |
-
}
|
94 |
-
delete_option('autoptimize_css_nogooglefont');
|
95 |
-
}
|
96 |
-
switch_to_blog( $original_blog_id );
|
97 |
-
}
|
98 |
-
$majorUp = true;
|
99 |
-
}
|
100 |
-
|
101 |
-
if ( $majorUp === true && get_transient('autoptimize_stale_option_buster') == false ) {
|
102 |
-
// clear cache and notify user to check result if major upgrade
|
103 |
-
set_transient('autoptimize_stale_option_buster', 'Mamsie & Liessie zehhe: ZWIJH!', HOUR_IN_SECONDS);
|
104 |
-
autoptimizeCache::clearall();
|
105 |
-
add_action('admin_notices', 'autoptimize_update_config_notice');
|
106 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config/default.php
CHANGED
@@ -5,7 +5,7 @@ if(!isset($_SERVER['HTTP_ACCEPT_ENCODING']))
|
|
5 |
$_SERVER['HTTP_ACCEPT_ENCODING'] = '';
|
6 |
if(!isset($_SERVER['HTTP_USER_AGENT']))
|
7 |
$_SERVER['HTTP_USER_AGENT'] = '';
|
8 |
-
|
9 |
// Determine supported compression method
|
10 |
$gzip = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
|
11 |
$deflate = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate');
|
@@ -14,15 +14,15 @@ $deflate = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate');
|
|
14 |
$encoding = $gzip ? 'gzip' : ($deflate ? 'deflate' : 'none');
|
15 |
|
16 |
// Check for buggy versions of Internet Explorer
|
17 |
-
if (!strstr($_SERVER['HTTP_USER_AGENT'], 'Opera') &&
|
18 |
preg_match('/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i', $_SERVER['HTTP_USER_AGENT'], $matches))
|
19 |
{
|
20 |
$version = floatval($matches[1]);
|
21 |
-
|
22 |
if ($version < 6)
|
23 |
$encoding = 'none';
|
24 |
-
|
25 |
-
if ($version == 6 && !strstr($_SERVER['HTTP_USER_AGENT'], 'EV1'))
|
26 |
$encoding = 'none';
|
27 |
}
|
28 |
|
@@ -30,10 +30,20 @@ if (!strstr($_SERVER['HTTP_USER_AGENT'], 'Opera') &&
|
|
30 |
if(ini_get('output_handler') == 'ob_gzhandler' || ini_get('zlib.output_compression') == 1)
|
31 |
$encoding = 'none';
|
32 |
|
33 |
-
|
34 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
// first check if we have to send 304
|
|
|
|
|
37 |
$eTag=md5($contents);
|
38 |
$modTime=filemtime(__FILE__.'.none');
|
39 |
|
@@ -46,20 +56,32 @@ if (($modTimeMatch)||($eTagMatch)) {
|
|
46 |
header('Connection: close');
|
47 |
} else {
|
48 |
// send all sorts of headers
|
49 |
-
$expireTime=60*60*24*
|
50 |
-
|
51 |
-
if(isset($encoding) && $encoding != 'none')
|
52 |
-
{
|
53 |
header('Content-Encoding: '.$encoding);
|
54 |
}
|
55 |
header('Vary: Accept-Encoding');
|
56 |
header('Content-Length: '.strlen($contents));
|
57 |
header('Content-type: %%CONTENT%%; charset=utf-8');
|
|
|
58 |
header('Cache-Control: max-age='.$expireTime.', public, immutable');
|
59 |
-
header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expireTime).' GMT');
|
60 |
header('ETag: ' . $eTag);
|
61 |
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modTime).' GMT');
|
62 |
-
|
63 |
// send output
|
64 |
echo $contents;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
}
|
5 |
$_SERVER['HTTP_ACCEPT_ENCODING'] = '';
|
6 |
if(!isset($_SERVER['HTTP_USER_AGENT']))
|
7 |
$_SERVER['HTTP_USER_AGENT'] = '';
|
8 |
+
|
9 |
// Determine supported compression method
|
10 |
$gzip = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
|
11 |
$deflate = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate');
|
14 |
$encoding = $gzip ? 'gzip' : ($deflate ? 'deflate' : 'none');
|
15 |
|
16 |
// Check for buggy versions of Internet Explorer
|
17 |
+
if (!strstr($_SERVER['HTTP_USER_AGENT'], 'Opera') &&
|
18 |
preg_match('/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i', $_SERVER['HTTP_USER_AGENT'], $matches))
|
19 |
{
|
20 |
$version = floatval($matches[1]);
|
21 |
+
|
22 |
if ($version < 6)
|
23 |
$encoding = 'none';
|
24 |
+
|
25 |
+
if ($version == 6 && !strstr($_SERVER['HTTP_USER_AGENT'], 'EV1'))
|
26 |
$encoding = 'none';
|
27 |
}
|
28 |
|
30 |
if(ini_get('output_handler') == 'ob_gzhandler' || ini_get('zlib.output_compression') == 1)
|
31 |
$encoding = 'none';
|
32 |
|
33 |
+
$iscompressed = file_exists(__FILE__.'.'.$encoding);
|
34 |
+
if($encoding != 'none' && $iscompressed == false)
|
35 |
+
{
|
36 |
+
$flag = ($encoding == 'gzip' ? FORCE_GZIP : FORCE_DEFLATE);
|
37 |
+
$code = file_get_contents(__FILE__.'.none');
|
38 |
+
$contents = gzencode($code,9,$flag);
|
39 |
+
}else{
|
40 |
+
//Get data
|
41 |
+
$contents = file_get_contents(__FILE__.'.'.$encoding);
|
42 |
+
}
|
43 |
|
44 |
// first check if we have to send 304
|
45 |
+
// inspired by http://www.jonasjohn.de/snippets/php/caching.htm
|
46 |
+
|
47 |
$eTag=md5($contents);
|
48 |
$modTime=filemtime(__FILE__.'.none');
|
49 |
|
56 |
header('Connection: close');
|
57 |
} else {
|
58 |
// send all sorts of headers
|
59 |
+
$expireTime=60*60*24*355; // 1y max according to RFC
|
60 |
+
if ($encoding != 'none') {
|
|
|
|
|
61 |
header('Content-Encoding: '.$encoding);
|
62 |
}
|
63 |
header('Vary: Accept-Encoding');
|
64 |
header('Content-Length: '.strlen($contents));
|
65 |
header('Content-type: %%CONTENT%%; charset=utf-8');
|
66 |
+
header('Cache-Control: max-age='.$expireTime.', public, must-revalidate');
|
67 |
header('Cache-Control: max-age='.$expireTime.', public, immutable');
|
68 |
+
header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expireTime).' GMT');
|
69 |
header('ETag: ' . $eTag);
|
70 |
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modTime).' GMT');
|
71 |
+
|
72 |
// send output
|
73 |
echo $contents;
|
74 |
+
|
75 |
+
//And write to filesystem cache if not done yet
|
76 |
+
if($encoding != 'none' && $iscompressed == false)
|
77 |
+
{
|
78 |
+
//Write the content we sent
|
79 |
+
file_put_contents(__FILE__.'.'.$encoding,$contents);
|
80 |
+
|
81 |
+
//And write the new content
|
82 |
+
$flag = ($encoding == 'gzip' ? FORCE_DEFLATE : FORCE_GZIP);
|
83 |
+
$ext = ($encoding == 'gzip' ? 'deflate' : 'gzip');
|
84 |
+
$contents = gzencode($code,9,$flag);
|
85 |
+
file_put_contents(__FILE__.'.'.$ext,$contents);
|
86 |
+
}
|
87 |
}
|
config/delayed.php
DELETED
@@ -1,86 +0,0 @@
|
|
1 |
-
<?php exit;
|
2 |
-
|
3 |
-
//Check everything exists before using it
|
4 |
-
if(!isset($_SERVER['HTTP_ACCEPT_ENCODING']))
|
5 |
-
$_SERVER['HTTP_ACCEPT_ENCODING'] = '';
|
6 |
-
if(!isset($_SERVER['HTTP_USER_AGENT']))
|
7 |
-
$_SERVER['HTTP_USER_AGENT'] = '';
|
8 |
-
|
9 |
-
// Determine supported compression method
|
10 |
-
$gzip = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
|
11 |
-
$deflate = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate');
|
12 |
-
|
13 |
-
// Determine used compression method
|
14 |
-
$encoding = $gzip ? 'gzip' : ($deflate ? 'deflate' : 'none');
|
15 |
-
|
16 |
-
// Check for buggy versions of Internet Explorer
|
17 |
-
if (!strstr($_SERVER['HTTP_USER_AGENT'], 'Opera') &&
|
18 |
-
preg_match('/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i', $_SERVER['HTTP_USER_AGENT'], $matches))
|
19 |
-
{
|
20 |
-
$version = floatval($matches[1]);
|
21 |
-
|
22 |
-
if ($version < 6)
|
23 |
-
$encoding = 'none';
|
24 |
-
|
25 |
-
if ($version == 6 && !strstr($_SERVER['HTTP_USER_AGENT'], 'EV1'))
|
26 |
-
$encoding = 'none';
|
27 |
-
}
|
28 |
-
|
29 |
-
//Some servers compress the output of PHP - Don't break in those cases
|
30 |
-
if(ini_get('output_handler') == 'ob_gzhandler' || ini_get('zlib.output_compression') == 1)
|
31 |
-
$encoding = 'none';
|
32 |
-
|
33 |
-
$iscompressed = file_exists(__FILE__.'.'.$encoding);
|
34 |
-
if($encoding != 'none' && $iscompressed == false)
|
35 |
-
{
|
36 |
-
$flag = ($encoding == 'gzip' ? FORCE_GZIP : FORCE_DEFLATE);
|
37 |
-
$code = file_get_contents(__FILE__.'.none');
|
38 |
-
$contents = gzencode($code,9,$flag);
|
39 |
-
}else{
|
40 |
-
//Get data
|
41 |
-
$contents = file_get_contents(__FILE__.'.'.$encoding);
|
42 |
-
}
|
43 |
-
|
44 |
-
// first check if we have to send 304
|
45 |
-
// inspired by http://www.jonasjohn.de/snippets/php/caching.htm
|
46 |
-
|
47 |
-
$eTag=md5($contents);
|
48 |
-
$modTime=filemtime(__FILE__.'.none');
|
49 |
-
|
50 |
-
date_default_timezone_set("UTC");
|
51 |
-
$eTagMatch = (isset($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'],$eTag));
|
52 |
-
$modTimeMatch = (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $modTime);
|
53 |
-
|
54 |
-
if (($modTimeMatch)||($eTagMatch)) {
|
55 |
-
header('HTTP/1.1 304 Not Modified');
|
56 |
-
header('Connection: close');
|
57 |
-
} else {
|
58 |
-
// send all sorts of headers
|
59 |
-
$expireTime=60*60*24*355; // 1y max according to RFC
|
60 |
-
if ($encoding != 'none') {
|
61 |
-
header('Content-Encoding: '.$encoding);
|
62 |
-
}
|
63 |
-
header('Vary: Accept-Encoding');
|
64 |
-
header('Content-Length: '.strlen($contents));
|
65 |
-
header('Content-type: %%CONTENT%%; charset=utf-8');
|
66 |
-
header('Cache-Control: max-age='.$expireTime.', public, immutable');
|
67 |
-
header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expireTime).' GMT');
|
68 |
-
header('ETag: ' . $eTag);
|
69 |
-
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modTime).' GMT');
|
70 |
-
|
71 |
-
// send output
|
72 |
-
echo $contents;
|
73 |
-
|
74 |
-
//And write to filesystem cache if not done yet
|
75 |
-
if($encoding != 'none' && $iscompressed == false)
|
76 |
-
{
|
77 |
-
//Write the content we sent
|
78 |
-
file_put_contents(__FILE__.'.'.$encoding,$contents);
|
79 |
-
|
80 |
-
//And write the new content
|
81 |
-
$flag = ($encoding == 'gzip' ? FORCE_DEFLATE : FORCE_GZIP);
|
82 |
-
$ext = ($encoding == 'gzip' ? 'deflate' : 'gzip');
|
83 |
-
$contents = gzencode($code,9,$flag);
|
84 |
-
file_put_contents(__FILE__.'.'.$ext,$contents);
|
85 |
-
}
|
86 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
localization/index.html
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
|
|
readme.txt
CHANGED
@@ -1,21 +1,22 @@
|
|
1 |
=== Autoptimize ===
|
2 |
-
Contributors: futtta, optimizingmatters, turl
|
3 |
-
Tags: optimize, minify, 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.9
|
7 |
-
|
|
|
8 |
|
9 |
-
Autoptimize speeds up your website by optimizing JS, CSS and
|
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 (but can also defer), moves and defers scripts to the footer and minifies HTML. The "Extra" options allow you to async non-aggregated JavaScript, remove WordPress core emoji cruft
|
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](
|
19 |
|
20 |
(Speed-surfing image under creative commons [by LL Twistiti](https://www.flickr.com/photos/twistiti/818552808/))
|
21 |
|
@@ -35,7 +36,7 @@ It concatenates all scripts and styles, minifies and compresses them, adds expir
|
|
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 |
|
@@ -134,7 +135,7 @@ Both CSS and JS optimization can skip code from being aggregated and minimized b
|
|
134 |
|
135 |
= Configuring & Troubleshooting Autoptimize =
|
136 |
|
137 |
-
After having installed and activated the plugin, you'll have access to an admin page where you can to enable HTML, CSS and JavaScript optimization. According to your liking, you can start of just enabling all of them, or if you're more cautious one at a time.
|
138 |
|
139 |
If your blog doesn't function normally after having turned on Autoptimize, here are some pointers to identify & solve such issues using "advanced settings":
|
140 |
|
@@ -147,13 +148,7 @@ If your blog doesn't function normally after having turned on Autoptimize, here
|
|
147 |
|
148 |
= Help, I have a blank page or an internal server error after enabling Autoptimize!! =
|
149 |
|
150 |
-
|
151 |
-
|
152 |
-
In some rare cases the [CSS minification component](https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port/) currently used by Autoptimize crashes due to a lack of resources (see [detailed technical explanation here](http://blog.futtta.be/2014/01/14/irregular-expressions-have-your-stack-for-lunch/)). You can in that case either disable CSS optimization, try to exclude specific CSS from being aggregated or activate the legacy minifiers which don't have that problem. The latter can be accomplished by adding this to your wp-config.php:
|
153 |
-
|
154 |
-
`define("AUTOPTIMIZE_LEGACY_MINIFIERS","true");`
|
155 |
-
|
156 |
-
The "legacy minifiers" will remain in Autoptimize "for ever" and changes to wp-config.php are not affected by core-, theme- or plugin-upgrades so you should be good to go.
|
157 |
|
158 |
= But I still have blank autoptimized CSS or JS-files! =
|
159 |
|
@@ -196,14 +191,13 @@ NextGen Galleries does some nifty stuff to add JavaScript. In order for Autoptim
|
|
196 |
= What is noptimize? =
|
197 |
|
198 |
Starting with version 1.6.6 Autoptimize excludes everything inside noptimize tags, e.g.:
|
199 |
-
|
200 |
-
<!--noptimize--><script>alert('this will not get autoptimized')></script><!--/noptimize-->
|
201 |
|
202 |
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).
|
203 |
|
204 |
= Can I change the directory & filename of cached autoptimize files? =
|
205 |
|
206 |
-
Yes, if you want to serve files from e.g. /wp-content/resources/aggregated_12345.css instead of the default /wp-content/cache/autoptimize/autoptimize_12345.css, then add this to wp-config.php:
|
207 |
`
|
208 |
define('AUTOPTIMIZE_CACHE_CHILD_DIR','/resources/');
|
209 |
define('AUTOPTIMIZE_CACHEFILE_PREFIX','aggregated_');
|
@@ -213,7 +207,7 @@ define('AUTOPTIMIZE_CACHEFILE_PREFIX','aggregated_');
|
|
213 |
|
214 |
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.
|
215 |
|
216 |
-
= What does "remove
|
217 |
|
218 |
This new option in Autoptimize 2.3 removes the inline CSS, inline JS and linked JS-file added by WordPress core. As such is can have a small positive impact on your site's performance.
|
219 |
|
@@ -223,7 +217,7 @@ Although some online performance assessement tools will single out "query string
|
|
223 |
|
224 |
= (How) should I optimize Google Fonts? =
|
225 |
|
226 |
-
Google Fonts are typically loaded by a "render blocking" linked CSS-file. If you have a theme and plugins that use Google Fonts, you might end up with multiple such CSS-files. Autoptimize (since version 2.3) now let's you lessen the impact of Google Fonts by either removing them alltogether or by optimizing the way they are loaded. There are two optimization-flavors; the first one is "combine and link", which replaces all requests for Google Fonts into one request, which will still be render-blocking but will allow the fonts to be loaded immediately (meaning you won't see fonts change while the page is loading). The alternative is "combine and load async" which uses JavaScript to load the fonts in a non-render blocking manner but which might cause a "flash of unstyled text".
|
227 |
|
228 |
= Should I use "preconnect" =
|
229 |
|
@@ -233,6 +227,33 @@ Preconnect is a somewhat advanced feature to instruct browsers ([if they support
|
|
233 |
|
234 |
JavaScript files that are not autoptimized (because they were excluded or because they are hosted elsewhere) are typically render-blocking. By adding them in the comma-separated "async JS" field, Autoptimize will add the async flag causing the browser to load those files asynchronously (i.e. non-render blocking). This can however break your site (page), e.g. if you async "js/jquery/jquery.js" you will very likely get "jQuery is not defined"-errors. Use with care.
|
235 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
= Where can I get help? =
|
237 |
|
238 |
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/).
|
@@ -249,9 +270,20 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
|
|
249 |
|
250 |
== Changelog ==
|
251 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
= 2.3.4 =
|
253 |
* bugfix: is_plugin_active causing errors in some cases as [reported by @iluminancia and @lozula](https://wordpress.org/support/topic/fatal-error-after-update-to-2-3-3/)
|
254 |
-
* bugfix: added language domain to 4 __/_e functions, un grand merci
|
255 |
|
256 |
= 2.3.3 =
|
257 |
* improvement: updated to latest version of Filamentgroup's loadCSS
|
@@ -270,10 +302,10 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
|
|
270 |
* fix for issue with update-code in some circumstances, thanks to [Rajendra Zore](https://rajendrazore.com/) to report & help fix!
|
271 |
|
272 |
= 2.3.0 =
|
273 |
-
* new: optimize Google fonts with
|
274 |
* new: Async JS, can be applied to local or 3rd party JS (if local it will be auto-excluded from autoptimization)
|
275 |
* new: support to tell browsers to preconnect (= dns lookup + tcp/ip connection + ssl negotiation) to 3rd party domains (depends on browser support, works in Chrome & Firefox)
|
276 |
-
* new: remove WordPress
|
277 |
* new: remove (version parameter from) Querystring
|
278 |
* new: support to clear cache through WP CLI thanks to [junaidbhura](https://junaidbhura.com)
|
279 |
* lots of [bugfixes and small improvements done by some seriously smart people via GitHub](https://github.com/futtta/autoptimize/commits/master) (thanks all!!), including [a fix for AO 2.2 which saw the HTML minifier go PacMan on spaces](https://github.com/futtta/autoptimize/commit/0f6ac683c35bc82d1ac2d496ae3b66bb53e49f88) in some circumstances.
|
@@ -284,7 +316,7 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
|
|
284 |
* readme update
|
285 |
|
286 |
= 2.2.1 =
|
287 |
-
* fix for images being referenced in CSS not all being translated to correct path, leading to 404
|
288 |
* fix for "[] operator not supported for strings" error in PHP7.1 as reported by falk-wussow.de
|
289 |
* fix for security hash busting AO's cache in some cases (esp. in 2.1.1)
|
290 |
|
@@ -292,7 +324,7 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
|
|
292 |
* new: Autoptimize minifies first (caching the individual snippets) and aggregrates the minified snippets, resulting in huge performance improvements for uncached JS/ CSS.
|
293 |
* new: option to enable/ disable AO for logged in users (on by default)
|
294 |
* new: option to enable/ disable AO on WooCommerce, Easy Digital Downloads or WP eCommerce cart/ checkout page (on by default)
|
295 |
-
* improvement: switched to [rel=preload + Filamentgroup
|
296 |
* improvement: switched to YUI CSS minifier PHP-port 2.8.4-p10 (so not to the 3.x branch yet)
|
297 |
* improvements to the logic of which JS/ CSS can be optimized (getPath function) increasing reliability of the aggregation process
|
298 |
* 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!!
|
@@ -309,7 +341,7 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
|
|
309 |
|
310 |
= 2.1.0 =
|
311 |
* 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!
|
312 |
-
* new: An extra "More Optimization"-tab is shown (can be hidden with
|
313 |
* new: If cache size becomes too big, a mail will be sent to the site admin (pass `false` to `autoptimize_filter_cachecheck_sendmail` filter to disable or pass alternative email to the `autoptimize_filter_cachecheck_mailto` filter to change email-address)
|
314 |
* new: power-users can enable Autoptimize to pre-gzip the autoptimized files by passing `true` to `autoptimize_filter_cache_create_static_gzip`, kudo's to (Draikin)[https://github.com/Draikin] for this!
|
315 |
* improvement: admin GUI updated (again; thanks Pablo!) with some responsiveness added in the mix (not showing the right hand column on smaller screen-sizes)
|
@@ -347,259 +379,5 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
|
|
347 |
* lots of small and bigger bugfixes, I won't bother you with a full list but have a look at [the commmit log on GitHub](https://github.com/futtta/autoptimize/commits/master).
|
348 |
* tested and confirmed working with PHP7
|
349 |
|
350 |
-
=
|
351 |
-
*
|
352 |
-
* bugfix: make default add_action hook back into "template_redirect" instead of "init" to fix multiple problems as reported by [schecteracademicservices, bond138, rickenbacker](https://wordpress.org/support/topic/192-concatenated-js-but-193-does-not-for-me?replies=11), [Rick Sportel](https://wordpress.org/support/topic/version-193-made-plugin-wp-cdn-rewrite-crash?replies=3#post-6833159) and [wizray](https://wordpress.org/support/topic/the-page-loads-both-the-auto-combined-css-file-and-origin-raw-file?replies=11#post-6833146). If you do need Autoptimize to initialize earlier (e.g. when using Nextgen Galleries), then add this to your wp-config.php:
|
353 |
-
`define("AUTOPTIMIZE_INIT_EARLIER","true");`
|
354 |
-
|
355 |
-
= 1.9.3 =
|
356 |
-
* improvement: more intelligent CDN-replacement logic, thanks [Squazz for reporting and testing](https://wordpress.org/support/topic/enable-cdn-for-images-referenced-in-the-css?replies=9)
|
357 |
-
* improvement: allow strings (comments) to be excluded from HTML-optimization (comment removal)
|
358 |
-
* improvement: changed priority with which AO gets triggered by WordPress, solving JS not being aggregated when NextGen Galleries is active, with great [help from msebald](https://wordpress.org/support/topic/js-options-dont-work-if-html-disabled/)
|
359 |
-
* improvement: extra JS exclude-strings: gist.github.com, text/html, text/template, wp-slimstat.min.js, _stq, nonce, post_id (the latter two were removed from the "manual" exclude list on the settings-page)
|
360 |
-
* new in API: autoptimize_filter_html_exclude, autoptimize_filter_css_defer, autoptimize_filter_css_inline, autoptimize_filter_base_replace_cdn, autopitmize_filter_js_noptimize, autopitmize_filter_css_noptimize, autopitmize_filter_html_noptimize
|
361 |
-
* bugfix: remove some PHP notices, as [reported by dimitrov.adrian](https://wordpress.org/support/topic/php-errors-39)
|
362 |
-
* bugfix: make sure HTML-optimalization does not gobble a space before a cite [as proposed by ecdltf](https://wordpress.org/support/topic/%E2%80%9Coptimize-html%E2%80%9D-is-gobbling-whitespace-before-cite-tag)
|
363 |
-
* bugfix: cleaning the cache did not work on non-default directories as [encountered by NoahJ Champion](https://wordpress.org/support/topic/changing-the-wp-content-path-to-top-level?replies=10#post-6573657)
|
364 |
-
* upgraded to [yui compressor php port 2.4.8-4](https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port)
|
365 |
-
* added arabic translation, thanks to the [ekleel team](http://www.ekleel.net)
|
366 |
-
* tested with WordPress 4.2 beta 3
|
367 |
-
|
368 |
-
= 1.9.2 =
|
369 |
-
First of all; Happy holidays, all the best for 2015!!
|
370 |
-
|
371 |
-
* New: support for alternative cache-directory and file-prefix as requested by a.o. [Jassi Bacha](https://wordpress.org/support/topic/requesthelp-add-ability-to-specify-cache-folder?replies=1#post-6300128), [Cluster666](https://wordpress.org/support/topic/rewrite-js-path?replies=6#post-6363535) and Baris Unver.
|
372 |
-
* Improvement: hard-exclude all linked-data json objects (script type=application/ld+json)
|
373 |
-
* Improvement: several filters added to the API, e.g. to alter optimized HTML, CSS or JS
|
374 |
-
* Bugfix: set Autoptimize priority back from 11 to 2 (as previously) to avoid some pages not being optimized (thanks to [CaveatLector for investigating & reporting](https://wordpress.org/support/topic/wp-property-plugin-add_action-priority-incompatibility?replies=1))
|
375 |
-
* Bugfix (in YUI-CSS-compressor-PHP-port): don't convert bools to percentages in rotate3D-transforms (cfr. [bugreport on Github](https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port/issues/17))
|
376 |
-
* Bugfix: background images with a space in the path didn't load, [reported by johnh10](https://wordpress.org/support/topic/optimize-css-code-error-with-background-image-elements?replies=6#post-6201582).
|
377 |
-
* Bugfix: SVG image with fill:url broken after CSS optimization as [reported by Tkama](https://wordpress.org/support/topic/one-more-broblem-with-plugin?replies=2)
|
378 |
-
* Updated translation for Swedish, new translation for Ukrainian by [Zanatoly of SebWeo.com](http://SebWeo.com)
|
379 |
-
* Updated readme.txt
|
380 |
-
* Confirmed working with WordPress 4.1
|
381 |
-
|
382 |
-
= 1.9.1 =
|
383 |
-
* hard-exclude [the sidelink-search-box introduced in WP SEO v1.6](http://wordpress.org/plugins/wordpress-seo/changelog/) from JS optimization (this [broke some JS-optimization badly](http://wordpress.org/support/topic/190-breaks-js?replies=4))
|
384 |
-
* bugfix: first add semi-colon to inline script, only then add try-catch if required instead of the other way around.
|
385 |
-
|
386 |
-
= 1.9.0 =
|
387 |
-
* "Inline and defer CSS" allows one to specify which "above the fold CSS" should be inlined, while the normal optimized CSS is deferred.
|
388 |
-
* Inlined Base64-encoded background Images will now be cached as well and the threshold for inlining these images has been bumped up to 4096 bytes (from 2560).
|
389 |
-
* Separate cache-directories for CSS and JS in /wp-content/cache/autoptimize, which should result in faster cache pruning (and in some cases possibly faster serving of individual aggregated files).
|
390 |
-
* Autoptimized CSS is now injected before the <title>-tag, JS before </body> (and after </title> when forced in head). This can be overridden in the API.
|
391 |
-
* Some usability improvements of the administration-page
|
392 |
-
* Multiple hooks added to the API a.o. filters to not aggregate inline CSS or JS and filters to aggregate but not minify CSS or JS.
|
393 |
-
* Updated translations for Dutch, French, German, Persian and Polish and new translations for Brazilian Portuguese (thanks to [Leonardo Antonioli](http://tobeguarany.com/)) and Turkish (kudo's [Baris Unver](http://beyn.org/))
|
394 |
-
* Multiple bugfixes & improvements
|
395 |
-
* Tested with WordPress 4.0 rc3
|
396 |
-
|
397 |
-
= 1.8.5 =
|
398 |
-
* Updated to lastest version of [CSS minification component](https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port/)
|
399 |
-
* Improvement: for multi-sites the cache is now written to separate directories, avoiding one site to clean out the cache for the entire installation. Code [contributed by Joern Lund](http://wordpress.org/support/topic/multisite-blog-admin-can-delete-entire-network-cache), kudo's Joern!!
|
400 |
-
* Improvement: add WordPress plugin header to autoptimize_helper.php_example to make it easier to enable it as a module
|
401 |
-
* Improvement: nonce and post_id are added to default configuration for JS exclusion
|
402 |
-
* Improvement: explicitely exclude wp-admin from being Autoptimized
|
403 |
-
* Bugfix: plupload.min.js, syntaxhighlighter and "adsbygoogle" are excluded from JS aggregation.
|
404 |
-
* Bugfix: avoid double closing body-tags when Autoptimize adds JS to HTML as [reported by Can](http://wordpress.org/support/topic/works-like-a-charm-but-i-have-two-problems)
|
405 |
-
* Bugfix: make .htaccess compatible with both Apache 2.2 and 2.4 (http://wordpress.org/support/topic/feature-request-support-generating-htaccess-files-for-apache-24?replies=3)
|
406 |
-
|
407 |
-
= 1.8.4 =
|
408 |
-
* Bugfix: code in inline JS (or CSS) can be wrapped inside HTML-comments, but these got removed since 1.8.2 as part of a bugfix.
|
409 |
-
|
410 |
-
= 1.8.3 =
|
411 |
-
* Bugfix: avoid useless warnings on is_callable to flood php error log as [reported by Praveen Kumar](http://wordpress.org/support/topic/182-breaks-css-and-js?replies=14#post-5377604)
|
412 |
-
|
413 |
-
= 1.8.2 =
|
414 |
-
* Improvement: more graceful failure when minifier classes exist but method does not, based on [bug-report by Franck160](http://wordpress.org/support/topic/confict-with-dynamic-to-top)
|
415 |
-
* Improvement: deferred CSS is also outputted in noscript-tags
|
416 |
-
* Improvement: differentiate between Apache version in .htaccess file as suggested by [iMadalin](http://www.imadalin.ro/)
|
417 |
-
* Improvement: also aggregate protocol-less CSS/JS URI's (as [suggested by Ross](http://wordpress.org/support/topic/protocol-less-url-support))
|
418 |
-
* Improvement: disable autoptimization based on parameter in querystring (for debugging)
|
419 |
-
* Bugfix: some CSS-imports were not being aggregated/ minified
|
420 |
-
* Bugfix: add CSS before <title instead of <title> to avoid breakage when title includes other attributes (e.g. itemscope)
|
421 |
-
* Bugfix: make sure javascript or css between comments is not aggregated as reported by [Milap Gajjar](http://wordpress.org/support/topic/the-optimized-css-contains-duplicate-classes)
|
422 |
-
* Tested with WordPress 3.9 (beta 1)
|
423 |
-
* Updates in FAQ
|
424 |
-
|
425 |
-
= 1.8.1 =
|
426 |
-
* bugfix: CSS in conditional comments was not excluded from aggregation as reported by [Rolf](http://www.finkbeiner-holz.de/) and [bottapress](http://www.wordpress-hebergement.fr/)
|
427 |
-
|
428 |
-
= 1.8.0 =
|
429 |
-
* New: Option to inline all CSS [as suggested by Hamed](http://wordpress.org/support/topic/make-style-sheet-inline)
|
430 |
-
* New: set of filters to provide a simple API to change Autoptimize behavior (e.g. replace "defer" with "async", disabling Autoptimization on certain pages, specificy non-aggregatable script to be moved after aggregated one (cfr. http://wordpress.org/support/topic/feature-request-some-extra-options?replies=14), size of image to be data-urized). More info in the included autoptimize_helper.php_example.
|
431 |
-
* Improvement: exclude (css in) noscript-tags as [proposed by belg4mit](http://wordpress.org/support/topic/feature-suggestion-noscript-for-css)
|
432 |
-
* Improvement: switch default delivery of optimized CSS/JS-files from PHP to static files
|
433 |
-
* Updated [upstream CSS minifier](https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port/commit/fb33d2ffd0963692747101330b175a80173ce21b)
|
434 |
-
* Improvement (force gzip of static files) and Bugfix (force expiry for dynamic files, thanks to [Willem Razenberg](http://www.column-razenberg.nl/) in .htaccess
|
435 |
-
* Improvement: fail gracefully when things go wrong (e.g. CSS import resulting in empty aggregated CSS-files [reported by Danka](http://wordpress.org/support/topic/very-good-332) or when the theme is broken [as seen by Prateek Gupta](http://wordpress.org/support/topic/js-optimization-break-site-white-page-issue?replies=14#post-5038941))
|
436 |
-
* Updated translations and Polish added (thanks to [Jakub Sierpinski](http://www.sierpinski.pl/)).
|
437 |
-
* Bugfix: stop import-statements in CSS comments to be taken into acccount [hat tip to Josef from blog-it-solutions.de](http://www.blog-it-solutions.de/)
|
438 |
-
* Bugfix: fix for blur in CSS breakeage as [reported by Chris of clickpanic.com](http://blog.clickpanic.com/)
|
439 |
-
|
440 |
-
= 1.7.3 =
|
441 |
-
* improvement: remove cache + options on uninstall as [requested by Gingerbreadmen](http://wordpress.org/support/topic/wp_options-entries)
|
442 |
-
* improvement: set .htaccess to allow PHP execution in wp-content/cache/autoptimize when saving optimized files as PHP, as suggested by (David Mottershead of bermuda4u.com)[http://www.bermuda4u.com/] but forbid PHP execution when saving aggregated script/css as static files (except for multisite).
|
443 |
-
* bugfix: avoid Yoast SEO sitemaps going blank (due optimization of Yoast's dynamically built XML/XSL) as reported by [Vance Hallman](http://www.icefishing.co) and [Armand Hadife](http://solar-flag-pole-lights.com/). More info on this issue [can be found on my blog](http://blog.futtta.be/2013/12/09/blank-yoast-seo-sitemaps-no-more/).
|
444 |
-
* smaller changes to readme.txt
|
445 |
-
|
446 |
-
= 1.7.2 =
|
447 |
-
* improvement: extra checks in CSS @import-handling + move import rules to top of CSS if not imported successfully, based a.o. on bug reports [by ozum](http://wordpress.org/support/topic/zero-lenght-file-with-css-optimization) and by [Peter Stolwijk](http://wordpress.org/support/topic/cant-activate-plugin-22?replies=13#post-4891377)
|
448 |
-
* improvement: check if JS and CSS minifier classes exist and only load if they don't to avoid possible conflicts with other themes or plugins that already loaded minifiers
|
449 |
-
* tested and approved for WordPress 3.8 (beta1)
|
450 |
-
|
451 |
-
= 1.7.1 =
|
452 |
-
* New: support for mapped domains as suggested by [Michael for tiremoni.com](http://tiremoni.com/)
|
453 |
-
* Added an .htaccess to wp-content/cache/autoptimize to overwrite other caching directives (fixing a problem with WP Super Cache's .htaccess really, [as reported](http://wordpress.org/support/topic/expiresmax-age-compatibility-with-supercache) by [Hugh of www.unitedworldschools.org](http://www.unitedworldschools.org/))
|
454 |
-
* bugfix: Autoptimize broke data:uri's in CSS in some cases as reported by [Josef from blog-it-solutions.de](http://www.blog-it-solutions.de/)
|
455 |
-
* bugfix: avoid PHP notice if CSS exclusion list is empty
|
456 |
-
* moved "do not donate"-image into plugin
|
457 |
-
|
458 |
-
= 1.7.0 =
|
459 |
-
* New: exclude CSS
|
460 |
-
* New: defer CSS
|
461 |
-
* Updated minimizing components (JSMin & YUI PHP CSSMin)
|
462 |
-
* Updated admin-page, hiding advanced configuration options
|
463 |
-
* Updated CDN-support for added simplicity (code & UI-wise), including changing background image url in CSS
|
464 |
-
* Updated/ new translations provided for [French: wordpress-hebergement.fr](http://www.wordpress-hebergement.fr/), [Persian: Hamed Irani](http://basics.ir/), [Swedish: Jonathan Sulo](http://sulo.se/), [German: blog-it-solutions.de](http://www.blog-it-solutions.de/) and Dutch
|
465 |
-
* Removed support for YUI
|
466 |
-
* Flush HTML caching plugin's cache when flushing Autoptimize's one
|
467 |
-
* fix for BOM marker in CSS-files [as seen in Frontier theme](http://wordpress.org/support/topic/sidebar-problem-42), kudo's to [Download Converter](http://convertertoolz.com/) for reporting!
|
468 |
-
* fix for [protocol-less 3rd party scripts disappearing](http://wordpress.org/support/topic/javascript-optimize-breaks-twentythirteen-mobile-menu), thanks for reporting p33t3r!
|
469 |
-
* fix for stylesheets without type="text/css" not being autoptimized as reported by [renzo](http://cocobeanproductions.com/)
|
470 |
-
* tested with WordPress 3.7 beta2 (admin-bar.min.js added to automatically excluded scripts)
|
471 |
-
|
472 |
-
= 1.6.6 =
|
473 |
-
* New: disable autoptimizatoin by putting part of your HTML, JS or CSS in between noptimize-tags, e.g.;
|
474 |
-
`<!--noptimize--><script>alert('this will not get autoptimized');</script><!--/noptimize-->`
|
475 |
-
* Added extra check to prevent plugin-files being called outside of WordPress as suggested in [this good article on security](http://mikejolley.com/2013/08/keeping-your-shit-secure-whilst-developing-for-wordpress/).
|
476 |
-
* Added small notice to be displayed after installation/ activation to ask user to configure the plugin as well.
|
477 |
-
* Added Persian translation, thanks to [Hamed T.](http://basics.ir/)
|
478 |
-
|
479 |
-
= 1.6.5 =
|
480 |
-
* new javascript-debug option to force the aggregated javascript file in the head-section of the HTML instead of at the bottom
|
481 |
-
* YUI compression & CDN are now deprecated functionality that will be removed in 1.7.0
|
482 |
-
|
483 |
-
= 1.6.4 =
|
484 |
-
* fix for PHP notice about mfunc_functions
|
485 |
-
* fix for strpos warnings due to empty values from the "Exclude scripts from autoptimize" configuration as [reported by CandleFOREX](http://wordpress.org/support/topic/empty-needle-warning)
|
486 |
-
* fix for broken feeds as [reported by Dinata and talgalili](http://wordpress.org/support/topic/feed-issue-5)
|
487 |
-
|
488 |
-
= 1.6.3 =
|
489 |
-
* fix for IE-hacks with javascript inside, causing javascript breakage (as seen in Sampression theme) as reported by [Takahiro of hiskip.com](http://www.hiskip.com/wp/)
|
490 |
-
* fix for escaping problem of imported css causing css breakage (as seen in Sampression theme) as reported by Takahiro as well
|
491 |
-
* fix to parse imports with syntax @import 'custom.css' not being parsed (as seen in Arras theme), again as reported by Takahiro
|
492 |
-
* fix for complex media types in media-attribute [as reported by jvwisssen](http://wordpress.org/support/topic/autoptimize-and-media-queries)
|
493 |
-
* fix for disappearing background-images that were already datauri's [as reported by will.blaschko](http://wordpress.org/support/topic/data-uris)
|
494 |
-
* fix not to strip out comments in HTML needed by WP Super Cache or W3 Total Cache (e.g. mfunc)
|
495 |
-
* added check to clean cache on upgrade
|
496 |
-
* updated FAQ in readme with information on troubleshooting and support
|
497 |
-
* tested with WordPress 3.6 beta
|
498 |
-
|
499 |
-
= 1.6.2 =
|
500 |
-
* Yet another emergency bugfix I'm afraid: apache_request_headers (again in config/delayed.php) is only available on ... Apache (duh), breaking non-Apache systems such as ngnix, Lighttpd and MS IIS badly. Reported by multiple users, thanks all!
|
501 |
-
|
502 |
-
= 1.6.1 =
|
503 |
-
* fixed stupid typo in config/delayed.php which broke things badly (april fools-wise); strpos instead of str_pos as reported by Takahiro.
|
504 |
-
|
505 |
-
= 1.6.0 =
|
506 |
-
* You can now specify scripts that should not be Autoptimized in the admin page. Just add the names (or part of the path) of the scripts in a comma-separated list and that JavaScript-file will remain untouched by Autoptimize.
|
507 |
-
* Added support for ETag and LastModified (essentially for a better pagespeed score, as the files are explicitely cacheable for 1 year)
|
508 |
-
* Autoptimizing for logged in users is enabled again
|
509 |
-
* Autoptimize now creates an index.html in wp-content/cache/autoptimize to prevent snooping (as [proposed by Chris](http://blog.futtta.be/2013/01/07/adopting-an-oss-orphan-autoptimize/#li-comment-36292))
|
510 |
-
* bugfix: removed all deprecated functions ([reported by Hypolythe](http://wordpress.org/support/topic/many-deprecated-errors) and diff by Heiko Adams, thanks guys!)
|
511 |
-
* bugfix for HTTPS-problem as [reported by dbs121](http://wordpress.org/support/topic/woocommerce-autoptimizer-https-issue)
|
512 |
-
* bugfix for breakage with unusual WordPress directory layout as reported by [Josef from blog-it-solutions.de](http://www.blog-it-solutions.de/).
|
513 |
-
|
514 |
-
= 1.5.1 =
|
515 |
-
* bugfix: add CSS before opening title-tag instead of after closing title, to avoid CSS being loaded in wrong order, as reported by [fotofashion](http://fotoandfashion.de/) and [blogitsolutions](http://www.blog-it-solutions.de) (thanks guys)
|
516 |
-
|
517 |
-
= 1.5 =
|
518 |
-
* first bugfix release by [futtta](http://blog.futtta.be/2013/01/07/adopting-an-oss-orphan-autoptimize/), thanks for a great plugin Turl!
|
519 |
-
* misc bug fixes, a.o. support for Twenty Twelve theme, admin bar problem in WP3.5, data-uri breaking CSS file naming
|
520 |
-
|
521 |
-
= 1.4 =
|
522 |
-
* Add support for inline style tags with CSS media
|
523 |
-
* Fix Wordpress top bar
|
524 |
-
|
525 |
-
= 1.3 =
|
526 |
-
* Add workaround for TinyMCEComments
|
527 |
-
* Add workaround for asynchronous Google Analytics
|
528 |
-
|
529 |
-
= 1.2 =
|
530 |
-
* Add workaround for Chitika ads.
|
531 |
-
* Add workaround for LinkWithin widget.
|
532 |
-
* Belorussian translation
|
533 |
-
|
534 |
-
= 1.1 =
|
535 |
-
* Add workarounds for amazon and fastclick
|
536 |
-
* Add workaround for Comment Form Quicktags
|
537 |
-
* Fix issue with Vipers Video Quicktags
|
538 |
-
* Fix a bug in where some scripts that shouldn't be moved were moved
|
539 |
-
* Fix a bug in where the config page wouldn't appear
|
540 |
-
* Fix @import handling
|
541 |
-
* Implement an option to disable js/css gzipping
|
542 |
-
* Implement CDN functionality
|
543 |
-
* Implement data: URI generation for images
|
544 |
-
* Support YUI CSS/JS Compressor
|
545 |
-
* Performance increases
|
546 |
-
* Handle WP Super Cache's cache files better
|
547 |
-
* Update translations
|
548 |
-
|
549 |
-
= 1.0 =
|
550 |
-
* Add workaround for whos.among.us
|
551 |
-
* Support preserving HTML Comments.
|
552 |
-
* Implement "delayed cache compression"
|
553 |
-
* French translation
|
554 |
-
* Update Spanish translation
|
555 |
-
|
556 |
-
= 0.9 =
|
557 |
-
* Add workaround for networkedblogs.
|
558 |
-
* Add workarounds for histats and statscounter
|
559 |
-
* Add workaround for smowtion and infolinks.
|
560 |
-
* Add workaround for Featured Content Gallery
|
561 |
-
* Simplified Chinese translation
|
562 |
-
* Update Spanish Translation
|
563 |
-
* Modify the cache system so it uses wp-content/cache/
|
564 |
-
* Add a clear cache button
|
565 |
-
|
566 |
-
= 0.8 =
|
567 |
-
* Add workaround for Vipers Video Quicktags
|
568 |
-
* Support <link> tags without media.
|
569 |
-
* Take even more precautions so we don't break urls in CSS
|
570 |
-
* Support adding try-catch wrappings to JavaScript code
|
571 |
-
* Add workaround for Wordpress.com Stats
|
572 |
-
* Fix a bug in where the tags wouldn't move
|
573 |
-
* Update translation template
|
574 |
-
* Update Spanish translation
|
575 |
-
|
576 |
-
= 0.7 =
|
577 |
-
* Add fix for DISQUS Comment System.
|
578 |
-
|
579 |
-
= 0.6 =
|
580 |
-
* Add workaround for mybloglog, blogcatalog, tweetmeme and Google CSE
|
581 |
-
|
582 |
-
= 0.5 =
|
583 |
-
* Support localization
|
584 |
-
* Fix the move and don't move system (again)
|
585 |
-
* Improve url detection in CSS
|
586 |
-
* Support looking for scripts and styles on just the header
|
587 |
-
* Fix an issue with data: uris getting modified
|
588 |
-
* Spanish translation
|
589 |
-
|
590 |
-
= 0.4 =
|
591 |
-
* Write plugin description in English
|
592 |
-
* Set default config to everything off
|
593 |
-
* Add link from plugins page to options page
|
594 |
-
* Fix problems with scripts that shouldn't be moved and were moved all the same
|
595 |
-
|
596 |
-
= 0.3 =
|
597 |
-
* Disable CSS media on @imports - caused an infinite loop
|
598 |
-
|
599 |
-
= 0.2 =
|
600 |
-
* Support CSS media
|
601 |
-
* Fix an issue in the IE Hacks preservation mechanism
|
602 |
-
* Fix an issue with some urls getting broken in CSS
|
603 |
-
|
604 |
-
= 0.1 =
|
605 |
-
* First released version.
|
1 |
=== Autoptimize ===
|
2 |
+
Contributors: futtta, optimizingmatters, zytzagoo, turl
|
3 |
+
Tags: optimize, minify, performance, pagespeed, image optimization
|
4 |
Donate link: http://blog.futtta.be/2013/10/21/do-not-donate-to-me/
|
5 |
Requires at least: 4.0
|
6 |
Tested up to: 4.9
|
7 |
+
Requires PHP: 5.3
|
8 |
+
Stable tag: 2.4.0
|
9 |
|
10 |
+
Autoptimize speeds up your website by optimizing JS, CSS, HTML, Google Fonts and images, async-ing JS, removing emoji cruft and more.
|
11 |
|
12 |
== Description ==
|
13 |
|
14 |
+
Autoptimize makes optimizing your site really easy. It can aggregate, minify and cache scripts and styles, injects CSS in the page head by default (but can also defer), moves and defers scripts to the footer and minifies HTML. The "Extra" options allow you to optimize Google Fonts and images, async non-aggregated JavaScript, remove WordPress core emoji cruft and more. As such it can improve your site's performance even when already on HTTP/2! There is extensive API available to enable you to tailor Autoptimize to each and every site's specific needs.
|
15 |
|
16 |
If you consider performance important, you really should use one of the many caching plugins to do page caching. Some good candidates to complement Autoptimize that way are e.g. [WP Super Cache](http://wordpress.org/plugins/wp-super-cache/), [HyperCache](http://wordpress.org/plugins/hyper-cache/), [Comet Cache](https://wordpress.org/plugins/comet-cache/) or [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler).
|
17 |
|
18 |
> <strong>Premium Support</strong><br>
|
19 |
+
> We provide great [Autoptimize Pro Support and Web Performance Optimization services](https://autoptimize.com/), check out our offering on [https://autoptimize.com/](https://autoptimize.com/)!
|
20 |
|
21 |
(Speed-surfing image under creative commons [by LL Twistiti](https://www.flickr.com/photos/twistiti/818552808/))
|
22 |
|
36 |
|
37 |
= But I'm on HTTP/2, so I don't need Autoptimize? =
|
38 |
|
39 |
+
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, simply untick the "aggregate JS-files" and/ or "aggregate CSS-files" options).
|
40 |
|
41 |
= Will this work with my blog? =
|
42 |
|
135 |
|
136 |
= Configuring & Troubleshooting Autoptimize =
|
137 |
|
138 |
+
After having installed and activated the plugin, you'll have access to an admin page where you can to enable HTML, CSS and JavaScript optimization. According to your liking, you can start of just enabling all of them, or if you're more cautious one at a time.
|
139 |
|
140 |
If your blog doesn't function normally after having turned on Autoptimize, here are some pointers to identify & solve such issues using "advanced settings":
|
141 |
|
148 |
|
149 |
= Help, I have a blank page or an internal server error after enabling Autoptimize!! =
|
150 |
|
151 |
+
Make sure you're not running other HTML, CSS or JS minification plugins (BWP minify, WP minify, ...) simultaneously with Autoptimize or disable that functionality your page caching plugin (W3 Total Cache, WP Fastest Cache, ...). Try enabling only CSS or only JS optimization to see which one causes the server error and follow the generic troubleshooting steps to find a workaround.
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
|
153 |
= But I still have blank autoptimized CSS or JS-files! =
|
154 |
|
191 |
= What is noptimize? =
|
192 |
|
193 |
Starting with version 1.6.6 Autoptimize excludes everything inside noptimize tags, e.g.:
|
194 |
+
`<!--noptimize--><script>alert('this will not get autoptimized');</script><!--/noptimize-->`
|
|
|
195 |
|
196 |
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).
|
197 |
|
198 |
= Can I change the directory & filename of cached autoptimize files? =
|
199 |
|
200 |
+
Yes, if you want to serve files from e.g. /wp-content/resources/aggregated_12345.css instead of the default /wp-content/cache/autoptimize/autoptimize_12345.css, then add this to wp-config.php:
|
201 |
`
|
202 |
define('AUTOPTIMIZE_CACHE_CHILD_DIR','/resources/');
|
203 |
define('AUTOPTIMIZE_CACHEFILE_PREFIX','aggregated_');
|
207 |
|
208 |
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.
|
209 |
|
210 |
+
= What does "remove emojis" do? =
|
211 |
|
212 |
This new option in Autoptimize 2.3 removes the inline CSS, inline JS and linked JS-file added by WordPress core. As such is can have a small positive impact on your site's performance.
|
213 |
|
217 |
|
218 |
= (How) should I optimize Google Fonts? =
|
219 |
|
220 |
+
Google Fonts are typically loaded by a "render blocking" linked CSS-file. If you have a theme and plugins that use Google Fonts, you might end up with multiple such CSS-files. Autoptimize (since version 2.3) now let's you lessen the impact of Google Fonts by either removing them alltogether or by optimizing the way they are loaded. There are two optimization-flavors; the first one is "combine and link", which replaces all requests for Google Fonts into one request, which will still be render-blocking but will allow the fonts to be loaded immediately (meaning you won't see fonts change while the page is loading). The alternative is "combine and load async" which uses JavaScript to load the fonts in a non-render blocking manner but which might cause a "flash of unstyled text". Starting form Autopitimize 2.4 "aggregate & preload" allows to aggregate all Google Font-files in one CSS-file that is preloaded, which should not be considered render blocking, but the fonts are available sooner (so less of a flash of unstyled text).
|
221 |
|
222 |
= Should I use "preconnect" =
|
223 |
|
227 |
|
228 |
JavaScript files that are not autoptimized (because they were excluded or because they are hosted elsewhere) are typically render-blocking. By adding them in the comma-separated "async JS" field, Autoptimize will add the async flag causing the browser to load those files asynchronously (i.e. non-render blocking). This can however break your site (page), e.g. if you async "js/jquery/jquery.js" you will very likely get "jQuery is not defined"-errors. Use with care.
|
229 |
|
230 |
+
= How does image optimization work? =
|
231 |
+
|
232 |
+
When image optimization is on, Autoptimize will look for png, gif, jpeg (.jpg) files in image tags and in your CSS files that are loaded from your own domain and change the src (source) to the ShortPixel CDN for those.
|
233 |
+
|
234 |
+
= Where can I get more info on image optimization? =
|
235 |
+
|
236 |
+
Have a look at [Shortpixel's FAQ](https://shortpixel.helpscoutdocs.com/category/60-shortpixel-ai-cdn).
|
237 |
+
|
238 |
+
= Can I disable the minification of excluded JS/ CSS? =
|
239 |
+
|
240 |
+
As from AO 2.4 excluded JS/ CSS are minified if the (filename indicates the) file is not minified yet. You can disable this with these filters;
|
241 |
+
|
242 |
+
`
|
243 |
+
add_filter('autoptimize_filter_js_minify_excluded','__return_false');
|
244 |
+
add_filter('autoptimize_filter_css_minify_excluded','__return_false');`
|
245 |
+
|
246 |
+
= Can I disable AO listening to page cache purges? =
|
247 |
+
|
248 |
+
As from AO 2.4 AO "listens" to page cache purges to clear its own cache. You can disable this behavior with this filter;
|
249 |
+
|
250 |
+
`
|
251 |
+
add_filter('autoptimize_filter_main_hookpagecachepurge','__return_false');`
|
252 |
+
|
253 |
+
= Why can't I upgrade from 2.3.4 to 2.4.0 (or higher)? =
|
254 |
+
|
255 |
+
Main reason (apart from occasional hickups that seem to be inherent to plugin upgrades) is that AO 2.4 requires you to be running PHP 5.3 or higher. And let's face it; you should actually be running PHP 7.x if you value performance (and security and support), no?
|
256 |
+
|
257 |
= Where can I get help? =
|
258 |
|
259 |
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/).
|
270 |
|
271 |
== Changelog ==
|
272 |
|
273 |
+
= 2.4.0 =
|
274 |
+
* refactored significantly (no more "classlesses", all is OO), classes are autoloaded, tests added (travis-ci) by zytzagoo who deserves praise for his hard work!
|
275 |
+
* new: image optimization (see "Extra"-tab) using Shortpixel's smart image optimization proxy
|
276 |
+
* new: you can now disable JS/ CSS-files being aggregated, having them minified individually instead
|
277 |
+
* new: Google Fonts can now be "aggregated & preloaded", this uses CSS which is loaded non render-blocking
|
278 |
+
* new: Autoptimize "listens" to page caches being cleared, upon which it purges it's own cache as well. Support depends on known action hooks firing by the page cache, supported by Hyper Cache, WP Rocket, W3 Total Cache, KeyCDN Cache Enabler, Swift, WP Super Cache, WP Fastest Cache and Comet Cache.
|
279 |
+
* new: local JS/ CSS-files that are excluded from optimization are minified by default (can be overridden by filter)
|
280 |
+
* improvement: updated minifiers (with very significant improvements for YUI CSS compressor PHP port)
|
281 |
+
* improvement: "remove all Google Fonts" is now more careful (avoiding removing entire CSS blocks)
|
282 |
+
* misc. under the hood improvements (e.g. more robust cache clearing, better support for multibyte character sets, improved CDN rewrite logic, avoid PHP warnings when writing files to cache, ...)
|
283 |
+
|
284 |
= 2.3.4 =
|
285 |
* bugfix: is_plugin_active causing errors in some cases as [reported by @iluminancia and @lozula](https://wordpress.org/support/topic/fatal-error-after-update-to-2-3-3/)
|
286 |
+
* bugfix: added language domain to 4 __/_e functions, un grand merci à Guillaume Blet!
|
287 |
|
288 |
= 2.3.3 =
|
289 |
* improvement: updated to latest version of Filamentgroup's loadCSS
|
302 |
* fix for issue with update-code in some circumstances, thanks to [Rajendra Zore](https://rajendrazore.com/) to report & help fix!
|
303 |
|
304 |
= 2.3.0 =
|
305 |
+
* new: optimize Google fonts with "combine & link" and "combine and load async" (with webload.js), intelligently preconnecting to Google's domains to limit performance impact even further
|
306 |
* new: Async JS, can be applied to local or 3rd party JS (if local it will be auto-excluded from autoptimization)
|
307 |
* new: support to tell browsers to preconnect (= dns lookup + tcp/ip connection + ssl negotiation) to 3rd party domains (depends on browser support, works in Chrome & Firefox)
|
308 |
+
* new: remove WordPress' core's emoji CSS & JS
|
309 |
* new: remove (version parameter from) Querystring
|
310 |
* new: support to clear cache through WP CLI thanks to [junaidbhura](https://junaidbhura.com)
|
311 |
* lots of [bugfixes and small improvements done by some seriously smart people via GitHub](https://github.com/futtta/autoptimize/commits/master) (thanks all!!), including [a fix for AO 2.2 which saw the HTML minifier go PacMan on spaces](https://github.com/futtta/autoptimize/commit/0f6ac683c35bc82d1ac2d496ae3b66bb53e49f88) in some circumstances.
|
316 |
* readme update
|
317 |
|
318 |
= 2.2.1 =
|
319 |
+
* fix for images being referenced in CSS not all being translated to correct path, leading to 404's as reported by Jeff Inho
|
320 |
* fix for "[] operator not supported for strings" error in PHP7.1 as reported by falk-wussow.de
|
321 |
* fix for security hash busting AO's cache in some cases (esp. in 2.1.1)
|
322 |
|
324 |
* new: Autoptimize minifies first (caching the individual snippets) and aggregrates the minified snippets, resulting in huge performance improvements for uncached JS/ CSS.
|
325 |
* new: option to enable/ disable AO for logged in users (on by default)
|
326 |
* new: option to enable/ disable AO on WooCommerce, Easy Digital Downloads or WP eCommerce cart/ checkout page (on by default)
|
327 |
+
* 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/)
|
328 |
* improvement: switched to YUI CSS minifier PHP-port 2.8.4-p10 (so not to the 3.x branch yet)
|
329 |
* improvements to the logic of which JS/ CSS can be optimized (getPath function) increasing reliability of the aggregation process
|
330 |
* 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!!
|
341 |
|
342 |
= 2.1.0 =
|
343 |
* 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!
|
344 |
+
* new: An extra "More Optimization"-tab is shown (can be hidden with ´autoptimize_filter_show_partner_tabs´-filter) with information about related optimization tools- and services.
|
345 |
* new: If cache size becomes too big, a mail will be sent to the site admin (pass `false` to `autoptimize_filter_cachecheck_sendmail` filter to disable or pass alternative email to the `autoptimize_filter_cachecheck_mailto` filter to change email-address)
|
346 |
* new: power-users can enable Autoptimize to pre-gzip the autoptimized files by passing `true` to `autoptimize_filter_cache_create_static_gzip`, kudo's to (Draikin)[https://github.com/Draikin] for this!
|
347 |
* improvement: admin GUI updated (again; thanks Pablo!) with some responsiveness added in the mix (not showing the right hand column on smaller screen-sizes)
|
379 |
* lots of small and bigger bugfixes, I won't bother you with a full list but have a look at [the commmit log on GitHub](https://github.com/futtta/autoptimize/commits/master).
|
380 |
* tested and confirmed working with PHP7
|
381 |
|
382 |
+
= older =
|
383 |
+
* see [https://plugins.svn.wordpress.org/autoptimize/tags/2.3.4/readme.txt](https://plugins.svn.wordpress.org/autoptimize/tags/2.3.4/readme.txt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|