PluginProbe
Autoptimize / 2.7.3
Autoptimize v2.7.3
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.7.3, at classes/autoptimizeScripts.php

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