PluginProbe
Autoptimize / trunk
Autoptimize vtrunk
3.1.16 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 All 108 releases
← All changes | classes/autoptimizeScripts.php +102 -13 2.7.6 → trunk View file →
@@ -57,9 +57,14 @@
57 57 '_stq',
58 58 'nonce',
59 59 'post_id',
60 60 'data-noptimize',
61 + 'data-cfasync',
62 + 'data-pagespeed-no-defer',
61 63 'logHuman',
64 + 'amp-mobile-version-switcher',
65 + 'data-rocketlazyloadscript',
66 + 'rocket-browser-checker-js-after',
62 67 );
63 68
64 69 /**
65 70 * List of to be moved JS.
@@ -108,8 +113,22 @@
108 113 */
109 114 private $aggregate = true;
110 115
111 116 /**
117 + * Setting; if not aggregated, should we defer?
118 + *
119 + * @var bool
120 + */
121 + private $defer_not_aggregate = false;
122 +
123 + /**
124 + * Setting; defer inline JS?
125 + *
126 + * @var bool
127 + */
128 + private $defer_inline = false;
129 +
130 + /**
112 131 * Setting; try/catch wrapping or not.
113 132 *
114 133 * @var bool
115 134 */
@@ -199,9 +218,19 @@
199 218 * @param array $options all options.
200 219 */
201 220 public function read( $options )
202 221 {
203 - $noptimize_js = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content );
222 + $noptimize_js = false;
223 +
224 + // If page/ post check post_meta to see if optimize is off.
225 + if ( false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_js_optimize' ) ) {
226 + $noptimize_js = true;
227 + }
228 +
229 + // And a filter to enforce JS noptimize.
230 + $noptimize_js = apply_filters( 'autoptimize_filter_js_noptimize', $noptimize_js, $this->content );
231 +
232 + // And finally bail if noptimize_js is true.
204 233 if ( $noptimize_js ) {
205 234 return false;
206 235 }
207 236
@@ -206,9 +235,8 @@
206 235 }
207 236
208 237 // only optimize known good JS?
209 238 $allowlist_js = apply_filters( 'autoptimize_filter_js_allowlist', '', $this->content );
210 - $allowlist_js = apply_filters( 'autoptimize_filter_js_whitelist', $allowlist_js, $this->content ); // fixme: to be removed in next version.
211 239 if ( ! empty( $allowlist_js ) ) {
212 240 $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_js ) ) );
213 241 }
214 242
@@ -232,9 +260,21 @@
232 260 // Returning true for "dontaggregate" turns off aggregation.
233 261 if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) {
234 262 $this->aggregate = false;
235 263 }
264 + // and the filter that should have been there to begin with.
265 + $this->aggregate = apply_filters( 'autoptimize_filter_js_aggregate', $this->aggregate );
236 266
267 + // Defer when not aggregating.
268 + if ( false === $this->aggregate && apply_filters( 'autoptimize_filter_js_defer_not_aggregate', $options['defer_not_aggregate'] ) ) {
269 + $this->defer_not_aggregate = true;
270 + }
271 +
272 + // Defer inline JS?
273 + if ( ( true === $this->defer_not_aggregate && apply_filters( 'autoptimize_js_filter_defer_inline', $options['defer_inline'] ) ) || apply_filters( 'autoptimize_js_filter_force_defer_inline', false ) ) {
274 + $this->defer_inline = true;
275 + }
276 +
237 277 // include inline?
238 278 if ( apply_filters( 'autoptimize_js_include_inline', $options['include_inline'] ) ) {
239 279 $this->include_inline = true;
240 280 }
@@ -335,8 +375,25 @@
335 375 }
336 376 }
337 377 }
338 378
379 + // not aggregating but deferring?
380 + if ( $this->defer_not_aggregate && false === $this->aggregate && ( str_replace( $this->dontmove, '', $path ) === $path || ( apply_filters( 'autoptimize_filter_js_defer_external', true ) && str_replace( $this->dontmove, '', $orig_tag ) === $orig_tag ) ) && strpos( $new_tag, ' defer' ) === false ) {
381 + if ( false !== strpos( $new_tag, ' async' ) && true === apply_filters( 'autoptimize_filter_js_defer_trumps_async', true ) ) {
382 + // remove async flag to ensure JS is properly deferred, otherwise the asynced JS might fire
383 + // before deferred inlined JS is executed, off course except filter is set to false which
384 + // re-institutes previous behavior.
385 + $new_tag = str_replace( array( " async='async'", ' async="async"', ' async=async', ' async' ), '', $new_tag );
386 + }
387 +
388 + if ( false === strpos( $new_tag, ' async' ) ) {
389 + // either async wasn't there to begin with or it was removed.
390 + // if async is there, the autoptimize_filter_js_defer_trumps_async
391 + // filter was set to false and in that case defer should not be added.
392 + $new_tag = str_replace( '<script ', '<script defer ', $new_tag );
393 + }
394 + }
395 +
339 396 // Should we minify the non-aggregated script?
340 397 // -> if aggregate is on and exclude minify is on
341 398 // -> if aggregate is off and the file is not in dontmove.
342 399 if ( $path && $this->minify_excluded ) {
@@ -353,8 +410,13 @@
353 410 }
354 411 }
355 412 }
356 413
414 + // Check if we still need to CDN (esp. for already minified resources).
415 + if ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) {
416 + $new_tag = str_replace( $url, $this->url_replace_cdn( $url ), $new_tag );
417 + }
418 +
357 419 if ( $this->ismovable( $new_tag ) ) {
358 420 // can be moved, flags and all.
359 421 if ( $this->movetolast( $new_tag ) ) {
360 422 $this->move['last'][] = $new_tag;
@@ -385,18 +447,46 @@
385 447 $code = preg_replace( '#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm', '$1', $code[1] );
386 448 $code = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code );
387 449 $this->scripts[] = 'INLINE;' . $code;
388 450 } else {
389 - // Can we move this?
390 - $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
391 - if ( $this->ismovable( $tag ) || '' !== $autoptimize_js_moveable ) {
392 - if ( $this->movetolast( $tag ) || 'last' === $autoptimize_js_moveable ) {
393 - $this->move['last'][] = $tag;
451 + $_inline_deferable = apply_filters( 'autoptimize_filters_js_inline_deferable', array( 'nonce', 'post_id', 'syntaxhighlighter' ) );
452 + $_inline_dontmove = array_values( array_diff( $this->dontmove, $_inline_deferable ) );
453 + if ( false === $this->defer_inline ) {
454 + // Can we move this?
455 + $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
456 + if ( $this->ismovable( $tag ) || '' !== $autoptimize_js_moveable ) {
457 + if ( $this->movetolast( $tag ) || 'last' === $autoptimize_js_moveable ) {
458 + $this->move['last'][] = $tag;
459 + } else {
460 + $this->move['first'][] = $tag;
461 + }
394 462 } else {
395 - $this->move['first'][] = $tag;
463 + $tag = '';
396 464 }
465 + } else if ( str_replace( $_inline_dontmove, '', $tag ) === $tag && strlen( $tag ) < apply_filters( 'autoptimize_filter_script_defer_inline_maxsize', 200000 ) ) {
466 + // defer inline JS by base64 encoding it but only if string is not ridiculously huge (to avoid issues with below regex mainly).
467 + preg_match( '#<script(?:[^>](?!id=))*\s*(?:id=(["\'])([^"\']+)\1)*+[^>]*+>(.*?)<\/script>#is', $tag, $match );
468 + if ( $match[2] ) {
469 + $_id = 'id="' . $match[2] . '" ';
470 + } else {
471 + $_id = '';
472 + }
473 +
474 + // if "minify inline" is on and if more then 9 spaces or 4 line breaks are found
475 + // in the inline JS then it is likely not minified, so minify before base64-encoding.
476 + $_script_contents = $match[3];
477 + if ( 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_html_minify_inline', 'off' ) && substr_count( $_script_contents, ' ' ) > 9 && substr_count( $_script_contents, "\n" ) > 4 && true === apply_filters( 'autoptimize_filter_script_defer_inline_minify', true ) ) {
478 + $_tmp_script_contents = trim( JSMin::minify( $_script_contents ) );
479 + if ( ! empty( $_tmp_script_contents ) ) {
480 + $_script_contents = $_tmp_script_contents;
481 + }
482 + }
483 +
484 + // base64 and defer the lot already.
485 + $new_tag = '<script defer ' . $_id . 'src="data:text/javascript;base64,' . base64_encode( $_script_contents ) . '"></script>';
486 + $this->content = str_replace( $this->hide_comments( $tag ), $new_tag, $this->content );
487 + $tag = '';
397 488 } else {
398 - // We shouldn't touch this.
399 489 $tag = '';
400 490 }
401 491 }
402 492 // Re-hide comments to be able to do the removal based on tag from $this->content.
@@ -428,9 +518,9 @@
428 518 *
429 519 * @param string $tag Script node & child(ren).
430 520 * @return bool
431 521 */
432 - public function should_aggregate( $tag )
522 + public static function should_aggregate( $tag )
433 523 {
434 524 if ( empty( $tag ) ) {
435 525 return false;
436 526 }
@@ -443,9 +533,9 @@
443 533 if ( ! empty( $tag_parts[1] ) ) {
444 534 $tag_without_contents = $tag_parts[1];
445 535 }
446 536
447 - $has_type = ( strpos( $tag_without_contents, 'type' ) !== false );
537 + $has_type = ( strpos( $tag_without_contents, ' type' ) !== false );
448 538
449 539 $type_valid = false;
450 540 if ( $has_type ) {
451 541 $type_valid = (bool) preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $tag_without_contents );
@@ -698,9 +788,8 @@
698 788 /**
699 789 * Determines wheter a <script> $tag can be excluded from minification (as already minified) based on:
700 790 * - inject_min_late being active
701 791 * - filename ending in `min.js`
702 - * - filename matching `js/jquery/jquery.js` (WordPress core jquery, is minified)
703 792 * - filename matching one passed in the consider minified filter
704 793 *
705 794 * @param string $js_path Path to JS file.
706 795 * @return bool
@@ -709,9 +798,9 @@
709 798 $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
710 799 if ( true !== $this->inject_min_late ) {
711 800 // late-inject turned off.
712 801 return false;
713 - } elseif ( ( false === strpos( $js_path, 'min.js' ) ) && ( false === strpos( $js_path, 'wp-includes/js/jquery/jquery.js' ) ) && ( str_replace( $consider_minified_array, '', $js_path ) === $js_path ) ) {
802 + } elseif ( ( false === strpos( $js_path, 'min.js' ) ) && ( str_replace( $consider_minified_array, '', $js_path ) === $js_path ) ) {
714 803 // file not minified based on filename & filter.
715 804 return false;
716 805 } else {
717 806 // phew, all is safe, we can late-inject.