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 +120 -26 2.7.1 → 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 */
@@ -164,13 +183,13 @@
164 183 */
165 184 private $md5hash = '';
166 185
167 186 /**
168 - * Setting (filter); whitelist of to be aggregated JS.
187 + * Setting (filter); allowlist of to be aggregated JS.
169 188 *
170 189 * @var string
171 190 */
172 - private $whitelist = '';
191 + private $allowlist = '';
173 192
174 193 /**
175 194 * Setting (filter); holds JS that should be removed.
176 195 *
@@ -199,17 +218,27 @@
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
208 237 // only optimize known good JS?
209 - $whitelist_js = apply_filters( 'autoptimize_filter_js_whitelist', '', $this->content );
210 - if ( ! empty( $whitelist_js ) ) {
211 - $this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelist_js ) ) );
238 + $allowlist_js = apply_filters( 'autoptimize_filter_js_allowlist', '', $this->content );
239 + if ( ! empty( $allowlist_js ) ) {
240 + $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_js ) ) );
212 241 }
213 242
214 243 // is there JS we should simply remove?
215 244 $removable_js = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
@@ -231,9 +260,21 @@
231 260 // Returning true for "dontaggregate" turns off aggregation.
232 261 if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) {
233 262 $this->aggregate = false;
234 263 }
264 + // and the filter that should have been there to begin with.
265 + $this->aggregate = apply_filters( 'autoptimize_filter_js_aggregate', $this->aggregate );
235 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 +
236 277 // include inline?
237 278 if ( apply_filters( 'autoptimize_js_include_inline', $options['include_inline'] ) ) {
238 279 $this->include_inline = true;
239 280 }
@@ -300,9 +341,9 @@
300 341
301 342 // Get script files.
302 343 if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) {
303 344 foreach ( $matches[0] as $tag ) {
304 - // only consider script aggregation for types whitelisted in should_aggregate-function.
345 + // only consider script aggregation for types allowlisted in should_aggregate-function.
305 346 $should_aggregate = $this->should_aggregate( $tag );
306 347 if ( ! $should_aggregate ) {
307 348 $tag = '';
308 349 continue;
@@ -334,8 +375,25 @@
334 375 }
335 376 }
336 377 }
337 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 +
338 396 // Should we minify the non-aggregated script?
339 397 // -> if aggregate is on and exclude minify is on
340 398 // -> if aggregate is off and the file is not in dontmove.
341 399 if ( $path && $this->minify_excluded ) {
@@ -344,15 +402,21 @@
344 402 $minified_url = $this->minify_single( $path );
345 403 if ( ! empty( $minified_url ) ) {
346 404 // Replace original URL with minified URL from cache.
347 405 $new_tag = str_replace( $url, $minified_url, $new_tag );
348 - } else {
349 - // Remove the original script tag, because cache content is empty.
406 + } elseif ( apply_filters( 'autoptimize_filter_ccsjs_remove_empty_minified_url', false ) ) {
407 + // Remove the original script tag, because cache content is empty but only if filter
408 + // is trued because $minified_url is also false if original JS is minified already.
350 409 $new_tag = '';
351 410 }
352 411 }
353 412 }
354 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 +
355 419 if ( $this->ismovable( $new_tag ) ) {
356 420 // can be moved, flags and all.
357 421 if ( $this->movetolast( $new_tag ) ) {
358 422 $this->move['last'][] = $new_tag;
@@ -383,18 +447,46 @@
383 447 $code = preg_replace( '#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm', '$1', $code[1] );
384 448 $code = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code );
385 449 $this->scripts[] = 'INLINE;' . $code;
386 450 } else {
387 - // Can we move this?
388 - $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
389 - if ( $this->ismovable( $tag ) || '' !== $autoptimize_js_moveable ) {
390 - if ( $this->movetolast( $tag ) || 'last' === $autoptimize_js_moveable ) {
391 - $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 + }
392 462 } else {
393 - $this->move['first'][] = $tag;
463 + $tag = '';
394 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 = '';
395 488 } else {
396 - // We shouldn't touch this.
397 489 $tag = '';
398 490 }
399 491 }
400 492 // Re-hide comments to be able to do the removal based on tag from $this->content.
@@ -426,9 +518,9 @@
426 518 *
427 519 * @param string $tag Script node & child(ren).
428 520 * @return bool
429 521 */
430 - public function should_aggregate( $tag )
522 + public static function should_aggregate( $tag )
431 523 {
432 524 if ( empty( $tag ) ) {
433 525 return false;
434 526 }
@@ -441,9 +533,9 @@
441 533 if ( ! empty( $tag_parts[1] ) ) {
442 534 $tag_without_contents = $tag_parts[1];
443 535 }
444 536
445 - $has_type = ( strpos( $tag_without_contents, 'type' ) !== false );
537 + $has_type = ( strpos( $tag_without_contents, ' type' ) !== false );
446 538
447 539 $type_valid = false;
448 540 if ( $has_type ) {
449 541 $type_valid = (bool) preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $tag_without_contents );
@@ -600,9 +692,9 @@
600 692 return $this->content;
601 693 }
602 694
603 695 /**
604 - * Checks against the white- and blacklists.
696 + * Checks against the allow- and blocklists.
605 697 *
606 698 * @param string $tag JS tag.
607 699 */
608 700 private function ismergeable( $tag )
@@ -610,15 +702,15 @@
610 702 if ( empty( $tag ) || ! $this->aggregate ) {
611 703 return false;
612 704 }
613 705
614 - if ( ! empty( $this->whitelist ) ) {
615 - foreach ( $this->whitelist as $match ) {
706 + if ( ! empty( $this->allowlist ) ) {
707 + foreach ( $this->allowlist as $match ) {
616 708 if ( false !== strpos( $tag, $match ) ) {
617 709 return true;
618 710 }
619 711 }
620 - // No match with whitelist.
712 + // No match with allowlist.
621 713 return false;
622 714 } else {
623 715 foreach ( $this->domove as $match ) {
624 716 if ( false !== strpos( $tag, $match ) ) {
@@ -643,11 +735,11 @@
643 735 }
644 736 }
645 737
646 738 /**
647 - * Checks agains the blacklist.
739 + * Checks agains the blocklist.
648 740 *
649 - * @param string $tag tag to check for blacklist (exclusions).
741 + * @param string $tag tag to check for blocklist (exclusions).
650 742 */
651 743 private function ismovable( $tag )
652 744 {
653 745 if ( empty( $tag ) || true !== $this->include_inline || apply_filters( 'autoptimize_filter_js_unmovable', true ) ) {
@@ -696,9 +788,8 @@
696 788 /**
697 789 * Determines wheter a <script> $tag can be excluded from minification (as already minified) based on:
698 790 * - inject_min_late being active
699 791 * - filename ending in `min.js`
700 - * - filename matching `js/jquery/jquery.js` (WordPress core jquery, is minified)
701 792 * - filename matching one passed in the consider minified filter
702 793 *
703 794 * @param string $js_path Path to JS file.
704 795 * @return bool
@@ -707,9 +798,9 @@
707 798 $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
708 799 if ( true !== $this->inject_min_late ) {
709 800 // late-inject turned off.
710 801 return false;
711 - } 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 ) ) {
712 803 // file not minified based on filename & filter.
713 804 return false;
714 805 } else {
715 806 // phew, all is safe, we can late-inject.
@@ -754,8 +845,11 @@
754 845 // Check if minified cache content is empty.
755 846 if ( empty( $contents ) ) {
756 847 return false;
757 848 }
849 +
850 + // Filter contents of excluded minified CSS.
851 + $contents = apply_filters( 'autoptimize_filter_js_single_after_minify', $contents );
758 852
759 853 // Store in cache.
760 854 $cache->cache( $contents, 'text/javascript' );
761 855 }