PluginProbe
Autoptimize / 2.8.0
Autoptimize v2.8.0
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 2.7.8 All 107 releases
autoptimize / classes / autoptimizeScripts.php

autoptimizeScripts.php in Autoptimize 2.8.0, at classes/autoptimizeScripts.php

795 lines 27.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class for JS optimization.
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit;
8 }
9
10 class autoptimizeScripts extends autoptimizeBase
11 {
12
13 /**
14 * Stores founds scripts.
15 *
16 * @var array
17 */
18 private $scripts = array();
19
20 /**
21 * Stores to be moved JS.
22 *
23 * @var array
24 */
25 private $move = array(
26 'first' => array(),
27 'last' => array(),
28 );
29
30 /**
31 * List of not to be moved JS.
32 *
33 * @var array
34 */
35 private $dontmove = array(
36 'document.write',
37 'html5.js',
38 'show_ads.js',
39 'google_ad',
40 'histats.com/js',
41 'statcounter.com/counter/counter.js',
42 'ws.amazon.com/widgets',
43 'media.fastclick.net',
44 '/ads/',
45 'comment-form-quicktags/quicktags.php',
46 'edToolbar',
47 'intensedebate.com',
48 'scripts.chitika.net/',
49 '_gaq.push',
50 'jotform.com/',
51 'admin-bar.min.js',
52 'GoogleAnalyticsObject',
53 'plupload.full.min.js',
54 'syntaxhighlighter',
55 'adsbygoogle',
56 'gist.github.com',
57 '_stq',
58 'nonce',
59 'post_id',
60 'data-noptimize',
61 'logHuman',
62 'amp-mobile-version-switcher',
63 );
64
65 /**
66 * List of to be moved JS.
67 *
68 * @var array
69 */
70 private $domove = array(
71 'gaJsHost',
72 'load_cmc',
73 'jd.gallery.transitions.js',
74 'swfobject.embedSWF(',
75 'tiny_mce.js',
76 'tinyMCEPreInit.go',
77 );
78
79 /**
80 * List of JS that can be moved last (not used any more).
81 *
82 * @var array
83 */
84 private $domovelast = array(
85 'addthis.com',
86 '/afsonline/show_afs_search.js',
87 'disqus.js',
88 'networkedblogs.com/getnetworkwidget',
89 'infolinks.com/js/',
90 'jd.gallery.js.php',
91 'jd.gallery.transitions.js',
92 'swfobject.embedSWF(',
93 'linkwithin.com/widget.js',
94 'tiny_mce.js',
95 'tinyMCEPreInit.go',
96 );
97
98 /**
99 * Setting CDN base URL.
100 *
101 * @var string
102 */
103 public $cdn_url = '';
104
105 /**
106 * Setting; aggregate or not.
107 *
108 * @var bool
109 */
110 private $aggregate = true;
111
112 /**
113 * Setting; if not aggregated, should we defer?
114 *
115 * @var bool
116 */
117 private $defer_not_aggregate = false;
118
119 /**
120 * Setting; try/catch wrapping or not.
121 *
122 * @var bool
123 */
124 private $trycatch = false;
125
126 /**
127 * State; is JS already minified.
128 *
129 * @var bool
130 */
131 private $alreadyminified = false;
132
133 /**
134 * Setting; force JS in head or not.
135 *
136 * @var bool
137 */
138 private $forcehead = true;
139
140 /**
141 * Setting; aggregate inline JS or not.
142 *
143 * @var bool
144 */
145 private $include_inline = false;
146
147 /**
148 * State; holds JS code.
149 *
150 * @var string
151 */
152 private $jscode = '';
153
154 /**
155 * State; holds URL of JS-file.
156 *
157 * @var string
158 */
159 private $url = '';
160
161 /**
162 * State; stores rest of HTML if (old) option "only in head" is on.
163 *
164 * @var string
165 */
166 private $restofcontent = '';
167
168 /**
169 * State; holds md5-hash.
170 *
171 * @var string
172 */
173 private $md5hash = '';
174
175 /**
176 * Setting (filter); allowlist of to be aggregated JS.
177 *
178 * @var string
179 */
180 private $allowlist = '';
181
182 /**
183 * Setting (filter); holds JS that should be removed.
184 *
185 * @var array
186 */
187 private $jsremovables = array();
188
189 /**
190 * Setting (filter); can we inject already minified files after the
191 * unminified aggregate JS has been minified.
192 *
193 * @var bool
194 */
195 private $inject_min_late = true;
196
197 /**
198 * Setting; should excluded JS be minified (if not already).
199 *
200 * @var bool
201 */
202 private $minify_excluded = true;
203
204 /**
205 * Reads the page and collects script tags.
206 *
207 * @param array $options all options.
208 */
209 public function read( $options )
210 {
211 $noptimize_js = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content );
212 if ( $noptimize_js ) {
213 return false;
214 }
215
216 // only optimize known good JS?
217 $allowlist_js = apply_filters( 'autoptimize_filter_js_allowlist', '', $this->content );
218 $allowlist_js = apply_filters( 'autoptimize_filter_js_whitelist', $allowlist_js, $this->content ); // fixme: to be removed in next version.
219 if ( ! empty( $allowlist_js ) ) {
220 $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_js ) ) );
221 }
222
223 // is there JS we should simply remove?
224 $removable_js = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
225 if ( ! empty( $removable_js ) ) {
226 $this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removable_js ) ) );
227 }
228
229 // only header?
230 if ( apply_filters( 'autoptimize_filter_js_justhead', $options['justhead'] ) ) {
231 $content = explode( '</head>', $this->content, 2 );
232 $this->content = $content[0] . '</head>';
233 $this->restofcontent = $content[1];
234 }
235
236 // Determine whether we're doing JS-files aggregation or not.
237 if ( ! $options['aggregate'] ) {
238 $this->aggregate = false;
239 }
240 // Returning true for "dontaggregate" turns off aggregation.
241 if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) {
242 $this->aggregate = false;
243 }
244
245 // Defer when not aggregating.
246 if ( false === $this->aggregate && apply_filters( 'autoptimize_js_filter_defer_not_aggregate', $options['defer_not_aggregate'] ) ) {
247 $this->defer_not_aggregate = true;
248 }
249
250 // include inline?
251 if ( apply_filters( 'autoptimize_js_include_inline', $options['include_inline'] ) ) {
252 $this->include_inline = true;
253 }
254
255 // filter to "late inject minified JS", default to true for now (it is faster).
256 $this->inject_min_late = apply_filters( 'autoptimize_filter_js_inject_min_late', true );
257
258 // filters to override hardcoded do(nt)move(last) array contents (array in, array out!).
259 $this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove );
260 $this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast );
261 $this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove );
262
263 // Determine whether excluded files should be minified if not yet so.
264 if ( ! $options['minify_excluded'] && $options['aggregate'] ) {
265 $this->minify_excluded = false;
266 }
267 $this->minify_excluded = apply_filters( 'autoptimize_filter_js_minify_excluded', $this->minify_excluded, '' );
268
269 // get extra exclusions settings or filter.
270 $exclude_js = $options['js_exclude'];
271 $exclude_js = apply_filters( 'autoptimize_filter_js_exclude', $exclude_js, $this->content );
272
273 if ( '' !== $exclude_js ) {
274 if ( is_array( $exclude_js ) ) {
275 $remove_keys = array_keys( $exclude_js, 'remove' );
276 if ( false !== $remove_keys ) {
277 foreach ( $remove_keys as $remove_key ) {
278 unset( $exclude_js[ $remove_key ] );
279 $this->jsremovables[] = $remove_key;
280 }
281 }
282 $excl_js_arr = array_keys( $exclude_js );
283 } else {
284 $excl_js_arr = array_filter( array_map( 'trim', explode( ',', $exclude_js ) ) );
285 }
286 $this->dontmove = array_merge( $excl_js_arr, $this->dontmove );
287 }
288
289 // Should we add try-catch?
290 if ( $options['trycatch'] ) {
291 $this->trycatch = true;
292 }
293
294 // force js in head?
295 if ( $options['forcehead'] ) {
296 $this->forcehead = true;
297 } else {
298 $this->forcehead = false;
299 }
300
301 $this->forcehead = apply_filters( 'autoptimize_filter_js_forcehead', $this->forcehead );
302
303 // get cdn url.
304 $this->cdn_url = $options['cdn_url'];
305
306 // noptimize me.
307 $this->content = $this->hide_noptimize( $this->content );
308
309 // Save IE hacks.
310 $this->content = $this->hide_iehacks( $this->content );
311
312 // comments.
313 $this->content = $this->hide_comments( $this->content );
314
315 // Get script files.
316 if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) {
317 foreach ( $matches[0] as $tag ) {
318 // only consider script aggregation for types allowlisted in should_aggregate-function.
319 $should_aggregate = $this->should_aggregate( $tag );
320 if ( ! $should_aggregate ) {
321 $tag = '';
322 continue;
323 }
324
325 if ( preg_match( '#<script[^>]*src=("|\')([^>]*)("|\')#Usmi', $tag, $source ) ) {
326 // non-inline script.
327 if ( $this->isremovable( $tag, $this->jsremovables ) ) {
328 $this->content = str_replace( $tag, '', $this->content );
329 continue;
330 }
331
332 $orig_tag = null;
333 $url = current( explode( '?', $source[2], 2 ) );
334 $path = $this->getpath( $url );
335 if ( false !== $path && preg_match( '#\.js$#', $path ) && $this->ismergeable( $tag ) ) {
336 // ok to optimize, add to array.
337 $this->scripts[] = $path;
338 } else {
339 $orig_tag = $tag;
340 $new_tag = $tag;
341
342 // non-mergeable script (excluded or dynamic or external).
343 if ( is_array( $exclude_js ) ) {
344 // should we add flags?
345 foreach ( $exclude_js as $excl_tag => $excl_flags ) {
346 if ( false !== strpos( $orig_tag, $excl_tag ) && in_array( $excl_flags, array( 'async', 'defer' ) ) ) {
347 $new_tag = str_replace( '<script ', '<script ' . $excl_flags . ' ', $new_tag );
348 }
349 }
350 }
351
352 // not aggregating but deferring?
353 if ( $this->defer_not_aggregate && false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path && strpos( $new_tag, ' defer' ) === false ) {
354 $new_tag = str_replace( '<script ', '<script defer ', $new_tag );
355 // and remove async as async+defer=async while we explicitly want defer.
356 if ( strpos( $new_tag, ' async' ) !== false && apply_filters( 'autoptimize_filter_js_defer_remove_async', true ) ) {
357 $new_tag = str_replace( array( ' async', ' async="async"', " async='async'" ), '', $new_tag );
358 }
359 }
360
361 // Should we minify the non-aggregated script?
362 // -> if aggregate is on and exclude minify is on
363 // -> if aggregate is off and the file is not in dontmove.
364 if ( $path && $this->minify_excluded ) {
365 $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
366 if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
367 $minified_url = $this->minify_single( $path );
368 if ( ! empty( $minified_url ) ) {
369 // Replace original URL with minified URL from cache.
370 $new_tag = str_replace( $url, $minified_url, $new_tag );
371 } elseif ( apply_filters( 'autoptimize_filter_ccsjs_remove_empty_minified_url', false ) ) {
372 // Remove the original script tag, because cache content is empty but only if filter
373 // is trued because $minified_url is also false if original JS is minified already.
374 $new_tag = '';
375 }
376 }
377 }
378
379 if ( $this->ismovable( $new_tag ) ) {
380 // can be moved, flags and all.
381 if ( $this->movetolast( $new_tag ) ) {
382 $this->move['last'][] = $new_tag;
383 } else {
384 $this->move['first'][] = $new_tag;
385 }
386 } else {
387 // cannot be moved, so if flag was added re-inject altered tag immediately.
388 if ( ( '' !== $new_tag && $orig_tag !== $new_tag ) || ( '' === $new_tag && apply_filters( 'autoptimize_filter_js_remove_empty_files', false ) ) ) {
389 $this->content = str_replace( $orig_tag, $new_tag, $this->content );
390 $orig_tag = '';
391 }
392 // and forget about the $tag (not to be touched any more).
393 $tag = '';
394 }
395 }
396 } else {
397 // Inline script.
398 if ( $this->isremovable( $tag, $this->jsremovables ) ) {
399 $this->content = str_replace( $tag, '', $this->content );
400 continue;
401 }
402
403 // unhide comments, as javascript may be wrapped in comment-tags for old times' sake.
404 $tag = $this->restore_comments( $tag );
405 if ( $this->ismergeable( $tag ) && $this->include_inline ) {
406 preg_match( '#<script.*>(.*)</script>#Usmi', $tag, $code );
407 $code = preg_replace( '#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm', '$1', $code[1] );
408 $code = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code );
409 $this->scripts[] = 'INLINE;' . $code;
410 } else {
411 // Can we move this?
412 $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
413 if ( $this->ismovable( $tag ) || '' !== $autoptimize_js_moveable ) {
414 if ( $this->movetolast( $tag ) || 'last' === $autoptimize_js_moveable ) {
415 $this->move['last'][] = $tag;
416 } else {
417 $this->move['first'][] = $tag;
418 }
419 } else {
420 // We shouldn't touch this.
421 $tag = '';
422 }
423 }
424 // Re-hide comments to be able to do the removal based on tag from $this->content.
425 $tag = $this->hide_comments( $tag );
426 }
427
428 // Remove the original script tag.
429 $this->content = str_replace( $tag, '', $this->content );
430 }
431
432 return true;
433 }
434
435 // No script files, great ;-) .
436 return false;
437 }
438
439 /**
440 * Determines wheter a certain `<script>` $tag should be aggregated or not.
441 *
442 * We consider these as "aggregation-safe" currently:
443 * - script tags without a `type` attribute
444 * - script tags with these `type` attribute values: `text/javascript`, `text/ecmascript`, `application/javascript`,
445 * and `application/ecmascript`
446 *
447 * Everything else should return false.
448 *
449 * @link https://developer.mozilla.org/en/docs/Web/HTML/Element/script#attr-type
450 *
451 * @param string $tag Script node & child(ren).
452 * @return bool
453 */
454 public function should_aggregate( $tag )
455 {
456 if ( empty( $tag ) ) {
457 return false;
458 }
459
460 // We're only interested in the type attribute of the <script> tag itself, not any possible
461 // inline code that might just contain the 'type=' string...
462 $tag_parts = array();
463 preg_match( '#<(script[^>]*)>#i', $tag, $tag_parts );
464 $tag_without_contents = null;
465 if ( ! empty( $tag_parts[1] ) ) {
466 $tag_without_contents = $tag_parts[1];
467 }
468
469 $has_type = ( strpos( $tag_without_contents, 'type' ) !== false );
470
471 $type_valid = false;
472 if ( $has_type ) {
473 $type_valid = (bool) preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $tag_without_contents );
474 }
475
476 $should_aggregate = false;
477 if ( ! $has_type || $type_valid ) {
478 $should_aggregate = true;
479 }
480
481 return $should_aggregate;
482 }
483
484 /**
485 * Joins and optimizes JS.
486 */
487 public function minify()
488 {
489 foreach ( $this->scripts as $script ) {
490 if ( empty( $script ) ) {
491 continue;
492 }
493
494 // TODO/FIXME: some duplicate code here, can be reduced/simplified.
495 if ( preg_match( '#^INLINE;#', $script ) ) {
496 // Inline script.
497 $script = preg_replace( '#^INLINE;#', '', $script );
498 $script = rtrim( $script, ";\n\t\r" ) . ';';
499 // Add try-catch?
500 if ( $this->trycatch ) {
501 $script = 'try{' . $script . '}catch(e){}';
502 }
503 $tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, '' );
504 if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscript ) ) {
505 $script = $tmpscript;
506 $this->alreadyminified = true;
507 }
508 $this->jscode .= "\n" . $script;
509 } else {
510 // External script.
511 if ( false !== $script && file_exists( $script ) && is_readable( $script ) ) {
512 $scriptsrc = file_get_contents( $script );
513 $scriptsrc = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $scriptsrc );
514 $scriptsrc = rtrim( $scriptsrc, ";\n\t\r" ) . ';';
515 // Add try-catch?
516 if ( $this->trycatch ) {
517 $scriptsrc = 'try{' . $scriptsrc . '}catch(e){}';
518 }
519 $tmpscriptsrc = apply_filters( 'autoptimize_js_individual_script', $scriptsrc, $script );
520 if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscriptsrc ) ) {
521 $scriptsrc = $tmpscriptsrc;
522 $this->alreadyminified = true;
523 } elseif ( $this->can_inject_late( $script ) ) {
524 $scriptsrc = self::build_injectlater_marker( $script, md5( $scriptsrc ) );
525 }
526 $this->jscode .= "\n" . $scriptsrc;
527 }
528 }
529 }
530
531 // Check for already-minified code.
532 $this->md5hash = md5( $this->jscode );
533 $ccheck = new autoptimizeCache( $this->md5hash, 'js' );
534 if ( $ccheck->check() ) {
535 $this->jscode = $ccheck->retrieve();
536 return true;
537 }
538 unset( $ccheck );
539
540 // $this->jscode has all the uncompressed code now.
541 if ( true !== $this->alreadyminified ) {
542 if ( apply_filters( 'autoptimize_js_do_minify', true ) ) {
543 $tmp_jscode = trim( JSMin::minify( $this->jscode ) );
544 if ( ! empty( $tmp_jscode ) ) {
545 $this->jscode = $tmp_jscode;
546 unset( $tmp_jscode );
547 }
548 $this->jscode = $this->inject_minified( $this->jscode );
549 $this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode );
550 return true;
551 } else {
552 $this->jscode = $this->inject_minified( $this->jscode );
553 return false;
554 }
555 }
556
557 $this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode );
558 return true;
559 }
560
561 /**
562 * Caches the JS in uncompressed, deflated and gzipped form.
563 */
564 public function cache()
565 {
566 $cache = new autoptimizeCache( $this->md5hash, 'js' );
567 if ( ! $cache->check() ) {
568 // Cache our code.
569 $cache->cache( $this->jscode, 'text/javascript' );
570 }
571 $this->url = AUTOPTIMIZE_CACHE_URL . $cache->getname();
572 $this->url = $this->url_replace_cdn( $this->url );
573 }
574
575 /**
576 * Returns the content.
577 */
578 public function getcontent()
579 {
580 // Restore the full content.
581 if ( ! empty( $this->restofcontent ) ) {
582 $this->content .= $this->restofcontent;
583 $this->restofcontent = '';
584 }
585
586 // Add the scripts taking forcehead/ deferred (default) into account.
587 if ( $this->forcehead ) {
588 $replace_tag = array( '</head>', 'before' );
589 $defer = '';
590 } else {
591 $replace_tag = array( '</body>', 'before' );
592 $defer = 'defer ';
593 }
594
595 $defer = apply_filters( 'autoptimize_filter_js_defer', $defer );
596 $type_js = '';
597 if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
598 $type_js = 'type="text/javascript" ';
599 }
600
601 $bodyreplacementpayload = '<script ' . $type_js . $defer . 'src="' . $this->url . '"></script>';
602 $bodyreplacementpayload = apply_filters( 'autoptimize_filter_js_bodyreplacementpayload', $bodyreplacementpayload );
603
604 $bodyreplacement = implode( '', $this->move['first'] );
605 $bodyreplacement .= $bodyreplacementpayload;
606 $bodyreplacement .= implode( '', $this->move['last'] );
607
608 $replace_tag = apply_filters( 'autoptimize_filter_js_replacetag', $replace_tag );
609
610 if ( strlen( $this->jscode ) > 0 ) {
611 $this->inject_in_html( $bodyreplacement, $replace_tag );
612 }
613
614 // Restore comments.
615 $this->content = $this->restore_comments( $this->content );
616
617 // Restore IE hacks.
618 $this->content = $this->restore_iehacks( $this->content );
619
620 // Restore noptimize.
621 $this->content = $this->restore_noptimize( $this->content );
622
623 // Return the modified HTML.
624 return $this->content;
625 }
626
627 /**
628 * Checks against the allow- and blocklists.
629 *
630 * @param string $tag JS tag.
631 */
632 private function ismergeable( $tag )
633 {
634 if ( empty( $tag ) || ! $this->aggregate ) {
635 return false;
636 }
637
638 if ( ! empty( $this->allowlist ) ) {
639 foreach ( $this->allowlist as $match ) {
640 if ( false !== strpos( $tag, $match ) ) {
641 return true;
642 }
643 }
644 // No match with allowlist.
645 return false;
646 } else {
647 foreach ( $this->domove as $match ) {
648 if ( false !== strpos( $tag, $match ) ) {
649 // Matched something.
650 return false;
651 }
652 }
653
654 if ( $this->movetolast( $tag ) ) {
655 return false;
656 }
657
658 foreach ( $this->dontmove as $match ) {
659 if ( false !== strpos( $tag, $match ) ) {
660 // Matched something.
661 return false;
662 }
663 }
664
665 // If we're here it's safe to merge.
666 return true;
667 }
668 }
669
670 /**
671 * Checks agains the blocklist.
672 *
673 * @param string $tag tag to check for blocklist (exclusions).
674 */
675 private function ismovable( $tag )
676 {
677 if ( empty( $tag ) || true !== $this->include_inline || apply_filters( 'autoptimize_filter_js_unmovable', true ) ) {
678 return false;
679 }
680
681 foreach ( $this->domove as $match ) {
682 if ( false !== strpos( $tag, $match ) ) {
683 // Matched something.
684 return true;
685 }
686 }
687
688 if ( $this->movetolast( $tag ) ) {
689 return true;
690 }
691
692 foreach ( $this->dontmove as $match ) {
693 if ( false !== strpos( $tag, $match ) ) {
694 // Matched something.
695 return false;
696 }
697 }
698
699 // If we're here it's safe to move.
700 return true;
701 }
702
703 private function movetolast( $tag )
704 {
705 if ( empty( $tag ) ) {
706 return false;
707 }
708
709 foreach ( $this->domovelast as $match ) {
710 if ( false !== strpos( $tag, $match ) ) {
711 // Matched, return true.
712 return true;
713 }
714 }
715
716 // Should be in 'first'.
717 return false;
718 }
719
720 /**
721 * Determines wheter a <script> $tag can be excluded from minification (as already minified) based on:
722 * - inject_min_late being active
723 * - filename ending in `min.js`
724 * - filename matching `js/jquery/jquery.js` (WordPress core jquery, is minified)
725 * - filename matching one passed in the consider minified filter
726 *
727 * @param string $js_path Path to JS file.
728 * @return bool
729 */
730 private function can_inject_late( $js_path ) {
731 $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
732 if ( true !== $this->inject_min_late ) {
733 // late-inject turned off.
734 return false;
735 } 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 ) ) {
736 // file not minified based on filename & filter.
737 return false;
738 } else {
739 // phew, all is safe, we can late-inject.
740 return true;
741 }
742 }
743
744 /**
745 * Returns whether we're doing aggregation or not.
746 *
747 * @return bool
748 */
749 public function aggregating()
750 {
751 return $this->aggregate;
752 }
753
754 /**
755 * Minifies a single local js file and returns its (cached) url.
756 *
757 * @param string $filepath Filepath.
758 * @param bool $cache_miss Optional. Force a cache miss. Default false.
759 *
760 * @return bool|string Url pointing to the minified js file or false.
761 */
762 public function minify_single( $filepath, $cache_miss = false )
763 {
764 $contents = $this->prepare_minify_single( $filepath );
765
766 if ( empty( $contents ) ) {
767 return false;
768 }
769
770 // Check cache.
771 $hash = 'single_' . md5( $contents );
772 $cache = new autoptimizeCache( $hash, 'js' );
773
774 // If not in cache already, minify...
775 if ( ! $cache->check() || $cache_miss ) {
776 $contents = trim( JSMin::minify( $contents ) );
777
778 // Check if minified cache content is empty.
779 if ( empty( $contents ) ) {
780 return false;
781 }
782
783 // Filter contents of excluded minified CSS.
784 $contents = apply_filters( 'autoptimize_filter_js_single_after_minify', $contents );
785
786 // Store in cache.
787 $cache->cache( $contents, 'text/javascript' );
788 }
789
790 $url = $this->build_minify_single_url( $cache );
791
792 return $url;
793 }
794 }
795