| @@ -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,13 @@ | ||
| 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 && strpos( $new_tag, ' async' ) === false ) { | |
| 381 | + $new_tag = str_replace( '<script ', '<script defer ', $new_tag ); | |
| 382 | + } | |
| 383 | + | |
| 338 | 384 | // Should we minify the non-aggregated script? |
| 339 | 385 | // -> if aggregate is on and exclude minify is on |
| 340 | 386 | // -> if aggregate is off and the file is not in dontmove. |
| 341 | 387 | if ( $path && $this->minify_excluded ) { |
| @@ -344,15 +390,21 @@ | ||
| 344 | 390 | $minified_url = $this->minify_single( $path ); |
| 345 | 391 | if ( ! empty( $minified_url ) ) { |
| 346 | 392 | // Replace original URL with minified URL from cache. |
| 347 | 393 | $new_tag = str_replace( $url, $minified_url, $new_tag ); |
| 348 | - } else { | |
| 349 | - // Remove the original script tag, because cache content is empty. | |
| 394 | + } elseif ( apply_filters( 'autoptimize_filter_ccsjs_remove_empty_minified_url', false ) ) { | |
| 395 | + // Remove the original script tag, because cache content is empty but only if filter | |
| 396 | + // is trued because $minified_url is also false if original JS is minified already. | |
| 350 | 397 | $new_tag = ''; |
| 351 | 398 | } |
| 352 | 399 | } |
| 353 | 400 | } |
| 354 | 401 | |
| 402 | + // Check if we still need to CDN (esp. for already minified resources). | |
| 403 | + if ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) { | |
| 404 | + $new_tag = str_replace( $url, $this->url_replace_cdn( $url ), $new_tag ); | |
| 405 | + } | |
| 406 | + | |
| 355 | 407 | if ( $this->ismovable( $new_tag ) ) { |
| 356 | 408 | // can be moved, flags and all. |
| 357 | 409 | if ( $this->movetolast( $new_tag ) ) { |
| 358 | 410 | $this->move['last'][] = $new_tag; |
| @@ -383,18 +435,35 @@ | ||
| 383 | 435 | $code = preg_replace( '#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm', '$1', $code[1] ); |
| 384 | 436 | $code = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code ); |
| 385 | 437 | $this->scripts[] = 'INLINE;' . $code; |
| 386 | 438 | } 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; | |
| 439 | + $_inline_deferable = apply_filters( 'autoptimize_filters_js_inline_deferable', array( 'nonce', 'post_id', 'syntaxhighlighter' ) ); | |
| 440 | + $_inline_dontmove = array_values( array_diff( $this->dontmove, $_inline_deferable ) ); | |
| 441 | + if ( false === $this->defer_inline ) { | |
| 442 | + // Can we move this? | |
| 443 | + $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag ); | |
| 444 | + if ( $this->ismovable( $tag ) || '' !== $autoptimize_js_moveable ) { | |
| 445 | + if ( $this->movetolast( $tag ) || 'last' === $autoptimize_js_moveable ) { | |
| 446 | + $this->move['last'][] = $tag; | |
| 447 | + } else { | |
| 448 | + $this->move['first'][] = $tag; | |
| 449 | + } | |
| 392 | 450 | } else { |
| 393 | - $this->move['first'][] = $tag; | |
| 451 | + $tag = ''; | |
| 394 | 452 | } |
| 453 | + } else if ( str_replace( $_inline_dontmove, '', $tag ) === $tag && strlen( $tag ) < apply_filters( 'autoptimize_filter_script_defer_inline_maxsize', 200000 ) ) { | |
| 454 | + // defer inline JS by base64 encoding it but only if string is not ridiculously huge (to avoid issues with below regex mainly). | |
| 455 | + preg_match( '#<script(?:[^>](?!id=))*\s*(?:id=(["\'])([^"\']+)\1)*+[^>]*+>(.*?)<\/script>#is', $tag, $match ); | |
| 456 | + if ( $match[2] ) { | |
| 457 | + $_id = 'id="' . $match[2] . '" '; | |
| 458 | + } else { | |
| 459 | + $_id = ''; | |
| 460 | + } | |
| 461 | + | |
| 462 | + $new_tag = '<script defer ' . $_id . 'src="data:text/javascript;base64,' . base64_encode( $match[3] ) . '"></script>'; | |
| 463 | + $this->content = str_replace( $tag, $new_tag, $this->content ); | |
| 464 | + $tag = ''; | |
| 395 | 465 | } else { |
| 396 | - // We shouldn't touch this. | |
| 397 | 466 | $tag = ''; |
| 398 | 467 | } |
| 399 | 468 | } |
| 400 | 469 | // Re-hide comments to be able to do the removal based on tag from $this->content. |
| @@ -426,9 +495,9 @@ | ||
| 426 | 495 | * |
| 427 | 496 | * @param string $tag Script node & child(ren). |
| 428 | 497 | * @return bool |
| 429 | 498 | */ |
| 430 | - public function should_aggregate( $tag ) | |
| 499 | + public static function should_aggregate( $tag ) | |
| 431 | 500 | { |
| 432 | 501 | if ( empty( $tag ) ) { |
| 433 | 502 | return false; |
| 434 | 503 | } |
| @@ -600,9 +669,9 @@ | ||
| 600 | 669 | return $this->content; |
| 601 | 670 | } |
| 602 | 671 | |
| 603 | 672 | /** |
| 604 | - * Checks against the white- and blacklists. | |
| 673 | + * Checks against the allow- and blocklists. | |
| 605 | 674 | * |
| 606 | 675 | * @param string $tag JS tag. |
| 607 | 676 | */ |
| 608 | 677 | private function ismergeable( $tag ) |
| @@ -610,15 +679,15 @@ | ||
| 610 | 679 | if ( empty( $tag ) || ! $this->aggregate ) { |
| 611 | 680 | return false; |
| 612 | 681 | } |
| 613 | 682 | |
| 614 | - if ( ! empty( $this->whitelist ) ) { | |
| 615 | - foreach ( $this->whitelist as $match ) { | |
| 683 | + if ( ! empty( $this->allowlist ) ) { | |
| 684 | + foreach ( $this->allowlist as $match ) { | |
| 616 | 685 | if ( false !== strpos( $tag, $match ) ) { |
| 617 | 686 | return true; |
| 618 | 687 | } |
| 619 | 688 | } |
| 620 | - // No match with whitelist. | |
| 689 | + // No match with allowlist. | |
| 621 | 690 | return false; |
| 622 | 691 | } else { |
| 623 | 692 | foreach ( $this->domove as $match ) { |
| 624 | 693 | if ( false !== strpos( $tag, $match ) ) { |
| @@ -643,11 +712,11 @@ | ||
| 643 | 712 | } |
| 644 | 713 | } |
| 645 | 714 | |
| 646 | 715 | /** |
| 647 | - * Checks agains the blacklist. | |
| 716 | + * Checks agains the blocklist. | |
| 648 | 717 | * |
| 649 | - * @param string $tag tag to check for blacklist (exclusions). | |
| 718 | + * @param string $tag tag to check for blocklist (exclusions). | |
| 650 | 719 | */ |
| 651 | 720 | private function ismovable( $tag ) |
| 652 | 721 | { |
| 653 | 722 | if ( empty( $tag ) || true !== $this->include_inline || apply_filters( 'autoptimize_filter_js_unmovable', true ) ) { |
| @@ -754,8 +823,11 @@ | ||
| 754 | 823 | // Check if minified cache content is empty. |
| 755 | 824 | if ( empty( $contents ) ) { |
| 756 | 825 | return false; |
| 757 | 826 | } |
| 827 | + | |
| 828 | + // Filter contents of excluded minified CSS. | |
| 829 | + $contents = apply_filters( 'autoptimize_filter_js_single_after_minify', $contents ); | |
| 758 | 830 | |
| 759 | 831 | // Store in cache. |
| 760 | 832 | $cache->cache( $contents, 'text/javascript' ); |
| 761 | 833 | } |