Fast Velocity Minify - Version 2.2.0

Version Description

[2017.08.13] = * fixed some debug notices * fixed the alternative html minification option

Download this release

Release Info

Developer Alignak
Plugin Icon 128x128 Fast Velocity Minify
Version 2.2.0
Comparing to
See all releases

Version 2.2.0

Files changed (39) hide show
  1. fvm.css +30 -0
  2. fvm.js +122 -0
  3. fvm.php +1771 -0
  4. inc/functions-admin.php +74 -0
  5. inc/functions-serverinfo.php +362 -0
  6. inc/functions.php +862 -0
  7. libs/index.html +0 -0
  8. libs/jar/index.html +0 -0
  9. libs/jar/yuicompressor-2.4.8.jar +0 -0
  10. libs/matthiasmullie/index.html +0 -0
  11. libs/matthiasmullie/minify/bin/index.html +0 -0
  12. libs/matthiasmullie/minify/bin/minifycss +45 -0
  13. libs/matthiasmullie/minify/bin/minifyjs +45 -0
  14. libs/matthiasmullie/minify/data/index.html +0 -0
  15. libs/matthiasmullie/minify/data/js/index.html +0 -0
  16. libs/matthiasmullie/minify/data/js/keywords_after.txt +7 -0
  17. libs/matthiasmullie/minify/data/js/keywords_before.txt +26 -0
  18. libs/matthiasmullie/minify/data/js/keywords_reserved.txt +63 -0
  19. libs/matthiasmullie/minify/data/js/operators.txt +46 -0
  20. libs/matthiasmullie/minify/data/js/operators_after.txt +44 -0
  21. libs/matthiasmullie/minify/data/js/operators_before.txt +43 -0
  22. libs/matthiasmullie/minify/index.html +0 -0
  23. libs/matthiasmullie/minify/src/CSS.php +690 -0
  24. libs/matthiasmullie/minify/src/Exception.php +12 -0
  25. libs/matthiasmullie/minify/src/Exceptions/BasicException.php +12 -0
  26. libs/matthiasmullie/minify/src/Exceptions/FileImportException.php +10 -0
  27. libs/matthiasmullie/minify/src/Exceptions/IOException.php +10 -0
  28. libs/matthiasmullie/minify/src/Exceptions/index.html +0 -0
  29. libs/matthiasmullie/minify/src/JS.php +529 -0
  30. libs/matthiasmullie/minify/src/Minify.php +434 -0
  31. libs/matthiasmullie/minify/src/index.html +0 -0
  32. libs/matthiasmullie/path-converter/index.html +0 -0
  33. libs/matthiasmullie/path-converter/src/Converter.php +195 -0
  34. libs/matthiasmullie/path-converter/src/ConverterInterface.php +24 -0
  35. libs/matthiasmullie/path-converter/src/NoConverter.php +23 -0
  36. libs/matthiasmullie/path-converter/src/index.html +0 -0
  37. libs/mrclay/HTML.php +241 -0
  38. libs/mrclay/index.html +0 -0
  39. readme.txt +365 -0
fvm.css ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #fastvelocity-min{margin-top:10px;}
2
+ #fastvelocity-min .processed{ min-height: 20px; margin: -4px 0 -2px 0; border: none !important; }
3
+ #fastvelocity-min .processed li{margin:0; padding:10px 0; border: none !important; }
4
+ #fastvelocity-min .processed > li:last-of-type { border: 0; }
5
+ #fastvelocity-min .processed li .filename{width: 50%;float:left;font-family:monospace;display:block;word-wrap:break-word;line-height:30px;}
6
+ #fastvelocity-min .processed li .accessed{display:block;}
7
+ #fastvelocity-min .processed li .error{color:red}
8
+ #fastvelocity-min .processed li .actions{display:block; padding:0; margin:0; text-align: right;}
9
+ #fastvelocity-min .processed li a{margin-left:10px}
10
+ #fastvelocity-min .processed pre{background:#EEE;max-height:300px;overflow:auto;display:none;margin-top:18px;margin-bottom:7px;padding:5px}
11
+ #fastvelocity-min .clear { clear:both; }
12
+ #fastvelocity-min #purgeall-row { padding-bottom: 2px; }
13
+ .fvm-settings label { font-size: 14px; color: #222; padding-top: 1px; }
14
+ .fvm-settings span.note-info { font-size: 14px; margin-left: 8px; color: #666; font-style: italic; }
15
+ .fvm-settings p.description { font-size: 14px; color: #666; font-style: italic; font-size: 14px; }
16
+ #tab-info h4{font-size: 14px; margin-bottom:-10px; }
17
+ h3.hndle { background: #F7F7F7; color: #000;}
18
+ .fvm-hide { max-height: 1px; overflow: none; position: absolute; top: -9999px; left: -9999px; visibility: hidden; }
19
+ .fvm-warning { font-weight: 600; color: #AA0000; }
20
+ .fvm-bold-green { font-weight: 600; color: #1196A3; }
21
+ .fvm-label-special { line-height: 38px; }
22
+ .fvm-label-pad { line-height: 21px; }
23
+
24
+ @media screen and (max-width:520px) {
25
+ #fastvelocity-min .processed li .filename{width: 100%;float:none;}
26
+ #fastvelocity-min .processed li .actions{text-align: left;margin-top: 4px}
27
+ #fastvelocity-min .processed li a{margin-left:0;margin-right:10px}
28
+ #fastvelocity-min .processed pre{margin-top:12px;margin-bottom:5px;}
29
+ .fvm-label-special { line-height: inherit; }
30
+ }
fvm.js ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function($){
2
+
3
+ $(function(){
4
+
5
+ // disable some checkboxes when some other is on or off
6
+ $('#fastvelocity_min_enable_defer_js').bind('click init', function() {
7
+ if( $(this).is(':checked')) {
8
+ $("#fastvelocity_min_exclude_defer_jquery, #fastvelocity_min_exclude_defer_login").prop("disabled", false);
9
+ } else {
10
+ $("#fastvelocity_min_exclude_defer_jquery, #fastvelocity_min_exclude_defer_login").prop("disabled", true);
11
+ }
12
+ }).trigger('init');
13
+
14
+ // disable some checkboxes when some other is on or off
15
+ $('#fastvelocity_min_skip_html_minification').bind('click init', function() {
16
+ if( $(this).is(':checked')) {
17
+ $("#fastvelocity_min_strip_htmlcomments").prop("disabled", true).prop('checked', false);
18
+ } else {
19
+ $("#fastvelocity_min_strip_htmlcomments").prop("disabled", false);
20
+ }
21
+ }).trigger('init');
22
+
23
+ // disable some checkboxes when some other is on or off
24
+ $('#fastvelocity_min_disable_css_merge').bind('click init', function() {
25
+ if( $(this).is(':checked')) {
26
+ $("#fastvelocity_min_disable_css_minification, #fastvelocity_min_force_inline_css").prop("disabled", true);
27
+ $("#fastvelocity_min_force_inline_css_footer").prop("disabled", true);
28
+ $("#fastvelocity_min_skip_cssorder, #fastvelocity_min_remove_print_mediatypes").prop("disabled", true);
29
+ } else {
30
+ $("#fastvelocity_min_disable_css_minification, #fastvelocity_min_force_inline_css").prop("disabled", false);
31
+ $("#fastvelocity_min_force_inline_css_footer").prop("disabled", false);
32
+ $("#fastvelocity_min_skip_cssorder, #fastvelocity_min_remove_print_mediatypes").prop("disabled", false);
33
+ }
34
+ }).trigger('init');
35
+
36
+ // disable some checkboxes when some other is on or off
37
+ $('#fastvelocity_min_disable_js_merge').bind('click init', function() {
38
+ if( $(this).is(':checked')) {
39
+ $("#fastvelocity_min_use_yui, #fastvelocity_min_disable_js_minification").prop("disabled", true);
40
+ $("#fastvelocity_min_enable_defer_js, #fastvelocity_min_defer_for_pagespeed").prop("disabled", true);
41
+ $("#fastvelocity_min_exclude_defer_jquery, #fastvelocity_min_exclude_defer_login").prop("disabled", true);
42
+ } else {
43
+ $("#fastvelocity_min_use_yui, #fastvelocity_min_disable_js_minification").prop("disabled", false);
44
+ $("#fastvelocity_min_enable_defer_js, #fastvelocity_min_defer_for_pagespeed").prop("disabled", false);
45
+ $("#fastvelocity_min_exclude_defer_jquery, #fastvelocity_min_exclude_defer_login").prop("disabled", false);
46
+ }
47
+ }).trigger('init');
48
+
49
+
50
+
51
+ // disable collapse
52
+ $('.postbox h3, .postbox .handlediv').unbind('click.postboxes');
53
+
54
+ // variables
55
+ var $fastvelocity_min_processed = $('#fastvelocity_min_processed'),
56
+ $fastvelocity_min_jsprocessed = $('#fastvelocity_min_jsprocessed',$fastvelocity_min_processed),
57
+ $fastvelocity_min_jsprocessed_ul = $('ul',$fastvelocity_min_jsprocessed),
58
+ $fastvelocity_min_cssprocessed = $('#fastvelocity_min_cssprocessed',$fastvelocity_min_processed),
59
+ $fastvelocity_min_cssprocessed_ul = $('ul',$fastvelocity_min_cssprocessed),
60
+ $fastvelocity_min_noprocessed = $('#fastvelocity_min_noprocessed'),
61
+ timeout = null,
62
+ stamp = null;
63
+
64
+ $($fastvelocity_min_processed).on('click','.log',function(e){
65
+ e.preventDefault();
66
+ $(this).parent().nextAll('pre').slideToggle();
67
+ });
68
+
69
+ function getFiles(extra) {
70
+ stamp = new Date().getTime();
71
+ var data = {
72
+ 'action': 'fastvelocity_min_files',
73
+ 'stamp': stamp
74
+ };
75
+ if(extra) {
76
+ for (var attrname in extra) { data[attrname] = extra[attrname]; }
77
+ }
78
+
79
+
80
+ $.post(ajaxurl, data, function(response) {
81
+
82
+ if(stamp == response.stamp) {
83
+ if(response.js.length > 0) {
84
+ $fastvelocity_min_jsprocessed.show();
85
+
86
+ $(response.js).each(function(){
87
+ var $li = $fastvelocity_min_jsprocessed_ul.find('li.'+this.hash);
88
+ if($li.length > 0) {
89
+ if($li.find('pre').html() != this.log) {
90
+ $li.find('pre').html(this.log);
91
+ }
92
+ } else {
93
+ $fastvelocity_min_jsprocessed_ul.append('<li class="'+this.hash+'"><span class="filename">'+this.filename+'</span> <span class="actions"><a href="#" class="log button button-primary">View Log</a></span><pre>'+this.log+'</pre></li><div class="clear"></div>');
94
+ }
95
+ });
96
+
97
+ }
98
+ if(response.css.length > 0) {
99
+
100
+ $(response.css).each(function(){
101
+ var $li = $fastvelocity_min_cssprocessed_ul.find('li.'+this.hash);
102
+ if($li.length > 0) {
103
+ if($li.find('pre').html() != this.log) {
104
+ $li.find('pre').html(this.log);
105
+ }
106
+ } else {
107
+ $fastvelocity_min_cssprocessed_ul.append('<li class="'+this.hash+'"><span class="filename">'+this.filename+'</span> <span class="actions"><a href="#" class="log button button-primary">View Log</a></span><pre>'+this.log+'</pre></li><div class="clear"></div>');
108
+ }
109
+ });
110
+ }
111
+
112
+ // check for new files
113
+ timeout = setTimeout(getFiles, 2500);
114
+ }
115
+ });
116
+ }
117
+
118
+ getFiles();
119
+
120
+ });
121
+
122
+ })(jQuery);
fvm.php ADDED
@@ -0,0 +1,1771 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: Fast Velocity Minify
4
+ Plugin URI: http://fastvelocity.com
5
+ Description: Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by merging and minifying CSS and JavaScript files into groups, compressing HTML and other speed optimizations.
6
+ Author: Raul Peixoto
7
+ Author URI: http://fastvelocity.com
8
+ Version: 2.2.0
9
+ License: GPL2
10
+
11
+ ------------------------------------------------------------------------
12
+ This program is free software; you can redistribute it and/or modify
13
+ it under the terms of the GNU General Public License as published by
14
+ the Free Software Foundation; either version 2 of the License, or
15
+ (at your option) any later version.
16
+
17
+ This program is distributed in the hope that it will be useful,
18
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ GNU General Public License for more details.
21
+
22
+ You should have received a copy of the GNU General Public License
23
+ along with this program; if not, write to the Free Software
24
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
+ */
26
+
27
+
28
+ # check for minimum requirements and prevent activation or disable if not fully compatible
29
+ function fvm_compat_checker() {
30
+ global $wp_version;
31
+
32
+ # defaults
33
+ $error = '';
34
+ $nwpv = implode('.', array_slice(explode('.', $wp_version), 0, 2)); # get 2 p only
35
+
36
+ # php version requirements
37
+ if (version_compare( PHP_VERSION, '5.4', '<' )) {
38
+ $error = 'Fast Velocity Minify requires PHP 5.4 or higher. You’re still on '. PHP_VERSION;
39
+ }
40
+
41
+ # php extension requirements
42
+ if (!extension_loaded('mbstring')) {
43
+ $error = 'Fast Velocity Minify requires the PHP mbstring module to be installed on the server.';
44
+ }
45
+
46
+ # wp version requirements
47
+ if ( $nwpv < '4.5' ) {
48
+ $error = 'Fast Velocity Minify requires WP 4.5 or higher. You’re still on ' . $wp_version;
49
+ }
50
+
51
+
52
+ if ((is_plugin_active(plugin_basename( __FILE__ )) && !empty($error)) || !empty($error)) {
53
+ if (isset($_GET['activate'])) { unset($_GET['activate']); }
54
+ deactivate_plugins( plugin_basename( __FILE__ ));
55
+ add_action('admin_notices', function() use ($error){
56
+ echo '<div class="notice notice-error is-dismissible"><p><strong>'.$error.'</strong></p></div>';
57
+ });
58
+ }
59
+ }
60
+ add_action('admin_init', 'fvm_compat_checker');
61
+
62
+
63
+ # get the plugin directory and include functions
64
+ $plugindir = plugin_dir_path( __FILE__ ); # with trailing slash
65
+ include($plugindir.'inc/functions.php');
66
+ include($plugindir.'inc/functions-serverinfo.php');
67
+
68
+ # get cache directories and urls
69
+ $cachepath = fvm_cachepath();
70
+ $tmpdir = $cachepath['tmpdir'];
71
+ $cachedir = $cachepath['cachedir'];
72
+ $cachedirurl = $cachepath['cachedirurl'];
73
+
74
+ # get the current wordpress installation url and path
75
+ $wp_home = site_url(); # get the current wordpress installation url
76
+ $wp_domain = trim(str_ireplace(array('http://', 'https://'), '', trim($wp_home, '/')));
77
+ $wp_home_path = ABSPATH;
78
+
79
+ # cleanup, delete any minification files older than 45 days (most probably unused files)
80
+ if ($handle = opendir($cachedir.'/')) {
81
+ while (false !== ($file = readdir($handle))) { $file = $cachedir.'/'.$file; if (is_file($file) && time() - filemtime($file) >= 86400 * 45) { unlink($file); } }
82
+ closedir($handle);
83
+ }
84
+
85
+ # default globals
86
+ $fastvelocity_min_global_js_done = array();
87
+
88
+
89
+ ###########################################
90
+ # build control panel pages ###############
91
+ ###########################################
92
+
93
+ # default options
94
+ $ignore = array(); # urls to exclude for merging and minification
95
+ $default_protocol = 'dynamic'; # use dynamic "//" protocols by default
96
+ $disable_js_merge = false; # disable JS merging? Default: false (if true, minification is also disabled)
97
+ $disable_css_merge = false; # disable CSS merging? Default: false (if true, minification is also disabled)
98
+ $disable_js_minification = false; # disable JS minification? Default: false
99
+ $disable_css_minification = false; # disable CSS minification? Default: false
100
+ $use_yui = false; # Use the YUI processor instead of PHP Minify? Default false
101
+ $remove_print_mediatypes = false; # remove CSS files of "print" mediatype
102
+ $skip_html_minification = false; # skip HTML minification? Default: false
103
+ $use_alt_html_minification = false; # use alt HTML minification? Default: false
104
+ $strip_htmlcomments = false; # whether to remove html comments on html minification
105
+ $skip_cssorder = false; # skip reordering CSS files by mediatype
106
+ $skip_google_fonts = false; # skip google fonts optimization? Default: false
107
+ $skip_emoji_removal = false; # skip removing emoji support? Default: false
108
+ $enable_defer_js = false; # Defer parsing of JavaScript? Default false
109
+ $exclude_defer_jquery = false; # Disable defer for jquery on the home
110
+ $use_google_closure = false; # Use Google closure (slower) instead of YUI processor? Default false
111
+ $use_php_minify = false; # Use PHP Minify instead of YUI or Closure? Default false
112
+ $force_inline_css = false; # Don't inline all css by default
113
+ $force_inline_css_footer = false; # Don't inline all css by default
114
+ $force_inline_googlefonts = false; # Don't inline all google fonts by default
115
+ $defer_for_pagespeed = false; # force defer for when we are being tested by pagespeed insights
116
+ $exclude_defer_login = false; # Disable defer js on the login page
117
+ $preload = array(); # urls to preload before anything else
118
+ $preconnect = array(); # domains to preconnect to
119
+ $fvm_fix_editor = false; # enable production mode by default?
120
+
121
+ # extra
122
+ $send_css_to_footer = false; # force defer for css
123
+ $critical_path_css = false; # critical path for homepage
124
+ $critical_css_tester = false; # will remove all css, except the critical path code
125
+ $generate_gulp_files = false; # will generate gulp files for gulp-uncss (spider the site first to generate the fvm cache files)
126
+ $generate_gulp_path = false; # path to working directory where gulp-uncss is installed
127
+ $used_css_files = array();
128
+
129
+ # add admin page and rewrite defaults
130
+ if(is_admin()) {
131
+ add_action('admin_menu', 'fastvelocity_min_admin_menu');
132
+ add_action('admin_enqueue_scripts', 'fastvelocity_min_load_admin_jscss');
133
+ add_action('wp_ajax_fastvelocity_min_files', 'fastvelocity_min_files_callback');
134
+ add_action('admin_init', 'fastvelocity_min_register_settings');
135
+ register_deactivation_hook( __FILE__, 'fastvelocity_min_plugin_deactivate');
136
+ } else {
137
+ # overwrite options from the database, false if not set
138
+ $ignore = array_map('trim', explode("\n", get_option('fastvelocity_min_ignore', '')));
139
+ $default_protocol = get_option('fastvelocity_min_default_protocol', 'dynamic');
140
+ $disable_js_merge = get_option('fastvelocity_min_disable_js_merge');
141
+ $disable_css_merge = get_option('fastvelocity_min_disable_css_merge');
142
+ $disable_js_minification = get_option('fastvelocity_min_disable_js_minification');
143
+ $disable_css_minification = get_option('fastvelocity_min_disable_css_minification');
144
+ $use_yui = get_option('fastvelocity_min_use_yui');
145
+ $remove_print_mediatypes = get_option('fastvelocity_min_remove_print_mediatypes');
146
+ $skip_html_minification = get_option('fastvelocity_min_skip_html_minification');
147
+ $use_alt_html_minification = get_option('fastvelocity_min_use_alt_html_minification');
148
+ $strip_htmlcomments = get_option('fastvelocity_min_strip_htmlcomments');
149
+ $skip_cssorder = get_option('fastvelocity_min_skip_cssorder');
150
+ $skip_google_fonts = get_option('fastvelocity_min_skip_google_fonts');
151
+ $skip_emoji_removal = get_option('fastvelocity_min_skip_emoji_removal');
152
+ $enable_defer_js = get_option('fastvelocity_min_enable_defer_js');
153
+ $exclude_defer_jquery = get_option('fastvelocity_min_exclude_defer_jquery');
154
+ $force_inline_css = get_option('fastvelocity_min_force_inline_css');
155
+ $force_inline_css_footer = get_option('fastvelocity_min_force_inline_css_footer');
156
+ $force_inline_googlefonts = get_option('fastvelocity_min_force_inline_googlefonts');
157
+ $defer_for_pagespeed = get_option('fastvelocity_min_defer_for_pagespeed');
158
+ $exclude_defer_login = get_option('fastvelocity_min_exclude_defer_login');
159
+ $preload = array_map('trim', explode("\n", get_option('fastvelocity_min_preload')));
160
+ $preconnect = array_map('trim', explode("\n", get_option('fastvelocity_min_preconnect')));
161
+ $fvm_fix_editor = get_option('fastvelocity_min_fvm_fix_editor');
162
+ $send_css_to_footer = get_option('fastvelocity_min_send_css_to_footer');
163
+ $critical_css_tester = get_option('fastvelocity_min_critical_css_tester');
164
+ $critical_path_css = get_option('fastvelocity_min_critical_path_css');
165
+ $generate_gulp_files = get_option('fastvelocity_min_generate_gulp_files');
166
+ $generate_gulp_path = get_option('fastvelocity_min_generate_gulp_path');
167
+
168
+
169
+ # skip on certain post_types or if there are specific keys on the url or if editor or admin
170
+ if(!fastvelocity_exclude_contents()) {
171
+
172
+ # actions for frontend only
173
+ if(!$disable_js_merge) {
174
+ add_action( 'wp_print_scripts', 'fastvelocity_min_merge_header_scripts', PHP_INT_MAX );
175
+ add_action( 'wp_print_footer_scripts', 'fastvelocity_min_merge_footer_scripts', 9.999999 );
176
+ }
177
+ if(!$disable_css_merge) {
178
+ add_action('wp_head', 'fvm_buffer_placeholder_top', 0);
179
+ add_action('wp_print_styles', 'fastvelocity_min_merge_header_css', PHP_INT_MAX );
180
+ add_action('wp_print_footer_scripts', 'fastvelocity_min_merge_footer_css', 9.999999 );
181
+ }
182
+ if(!$skip_emoji_removal) {
183
+ add_action( 'init', 'fastvelocity_min_disable_wp_emojicons' );
184
+ }
185
+
186
+ }
187
+ }
188
+
189
+
190
+
191
+ # exclude processing for editors and administrators (fix editors)
192
+ add_action( 'plugins_loaded', 'fastvelocity_fix_editor' );
193
+ function fastvelocity_fix_editor() {
194
+ global $fvm_fix_editor, $disable_js_merge, $disable_css_merge, $skip_emoji_removal;
195
+ if($fvm_fix_editor == true && is_user_logged_in() && (current_user_can('editor') || current_user_can('administrator'))) {
196
+ if(!$disable_js_merge) {
197
+ remove_action( 'wp_print_scripts', 'fastvelocity_min_merge_header_scripts', PHP_INT_MAX );
198
+ remove_action( 'wp_print_footer_scripts', 'fastvelocity_min_merge_footer_scripts', 9.999999 );
199
+ }
200
+ if(!$disable_css_merge) {
201
+ remove_action('wp_head', 'fvm_buffer_placeholder_top', 0);
202
+ remove_action('wp_print_styles', 'fastvelocity_min_merge_header_css', PHP_INT_MAX );
203
+ remove_action('wp_print_footer_scripts', 'fastvelocity_min_merge_footer_css', 9.999999 );
204
+ }
205
+ if(!$skip_emoji_removal) {
206
+ remove_action( 'init', 'fastvelocity_min_disable_wp_emojicons' );
207
+ }
208
+ }
209
+ }
210
+
211
+
212
+ # delete the cache when we deactivate the plugin
213
+ function fastvelocity_min_plugin_deactivate() { fvm_purge_all(); }
214
+
215
+
216
+ # create admin menu
217
+ function fastvelocity_min_admin_menu() {
218
+ add_options_page('Fast Velocity Minify Settings', 'Fast Velocity Minify', 'manage_options', 'fastvelocity-min', 'fastvelocity_min_settings');
219
+ }
220
+
221
+
222
+ # function to list all cache files
223
+ function fastvelocity_min_files_callback() {
224
+ global $cachedir;
225
+
226
+ # default
227
+ $return = array('js' => array(), 'css' => array(), 'stamp' => $_POST['stamp']);
228
+
229
+ # inspect directory with opendir, since glob might not be available in some systems
230
+ if ($handle = opendir($cachedir.'/')) {
231
+ while (false !== ($file = readdir($handle))) {
232
+ $file = $cachedir.'/'.$file;
233
+ $ext = pathinfo($file, PATHINFO_EXTENSION);
234
+ if (in_array($ext, array('js', 'css'))) {
235
+ $log = file_get_contents($file.'.txt');
236
+ $mincss = substr($file, 0, -4).'.min.css';
237
+ $minjs = substr($file, 0, -3).'.min.js';
238
+ $filename = basename($file);
239
+ if ($ext == 'css' && file_exists($mincss)) { $filename = basename($mincss); }
240
+ if ($ext == 'js' && file_exists($minjs)) { $filename = basename($minjs); }
241
+
242
+ # get location, hash, modified date
243
+ $info = explode('-', $filename);
244
+ $hash = $info['1'];
245
+ array_push($return[$ext], array('hash' => $hash, 'filename' => $filename, 'log' => $log));
246
+ }
247
+ }
248
+ closedir($handle);
249
+ }
250
+
251
+ header('Content-Type: application/json');
252
+ echo json_encode($return);
253
+ wp_die();
254
+ }
255
+
256
+
257
+ # load wp-admin css and js files
258
+ function fastvelocity_min_load_admin_jscss($hook) {
259
+ if ('settings_page_fastvelocity-min' != $hook) { return; }
260
+ wp_enqueue_script('postbox');
261
+ wp_enqueue_style('fastvelocity-min', plugins_url('fvm.css', __FILE__), array(), filemtime(plugin_dir_path( __FILE__).'fvm.css'));
262
+ wp_enqueue_script('fastvelocity-min', plugins_url('fvm.js', __FILE__), array('jquery'), filemtime(plugin_dir_path( __FILE__).'fvm.js'), true);
263
+ }
264
+
265
+
266
+ # register plugin settings
267
+ function fastvelocity_min_register_settings() {
268
+ register_setting('fvm-group', 'fastvelocity_min_ignore');
269
+ register_setting('fvm-group', 'fastvelocity_min_default_protocol');
270
+ register_setting('fvm-group', 'fastvelocity_min_disable_js_merge');
271
+ register_setting('fvm-group', 'fastvelocity_min_disable_css_merge');
272
+ register_setting('fvm-group', 'fastvelocity_min_disable_js_minification');
273
+ register_setting('fvm-group', 'fastvelocity_min_disable_css_minification');
274
+ register_setting('fvm-group', 'fastvelocity_min_use_yui');
275
+ register_setting('fvm-group', 'fastvelocity_min_remove_print_mediatypes');
276
+ register_setting('fvm-group', 'fastvelocity_min_skip_html_minification');
277
+ register_setting('fvm-group', 'fastvelocity_min_use_alt_html_minification');
278
+ register_setting('fvm-group', 'fastvelocity_min_strip_htmlcomments');
279
+ register_setting('fvm-group', 'fastvelocity_min_skip_cssorder');
280
+ register_setting('fvm-group', 'fastvelocity_min_skip_google_fonts');
281
+ register_setting('fvm-group', 'fastvelocity_min_skip_fontawesome_fonts');
282
+ register_setting('fvm-group', 'fastvelocity_min_skip_emoji_removal');
283
+ register_setting('fvm-group', 'fastvelocity_min_enable_defer_js');
284
+ register_setting('fvm-group', 'fastvelocity_min_exclude_defer_jquery');
285
+ register_setting('fvm-group', 'fastvelocity_min_force_inline_css');
286
+ register_setting('fvm-group', 'fastvelocity_min_force_inline_css_footer');
287
+ register_setting('fvm-group', 'fastvelocity_min_force_inline_googlefonts');
288
+ register_setting('fvm-group', 'fastvelocity_min_defer_for_pagespeed');
289
+ register_setting('fvm-group', 'fastvelocity_min_exclude_defer_login');
290
+ register_setting('fvm-group', 'fastvelocity_min_preload');
291
+ register_setting('fvm-group', 'fastvelocity_min_preconnect');
292
+ register_setting('fvm-group', 'fastvelocity_min_fvm_fix_editor');
293
+ register_setting('fvm-group', 'fastvelocity_min_fvm_enable_cdn');
294
+ register_setting('fvm-group', 'fastvelocity_min_fvm_cdn_url');
295
+
296
+ # pro version (for private usage... or if you know what you're doing)
297
+ register_setting('fvm-group-pro', 'fastvelocity_min_send_css_to_footer');
298
+ register_setting('fvm-group-pro', 'fastvelocity_min_critical_css_tester');
299
+ register_setting('fvm-group-pro', 'fastvelocity_min_critical_path_css');
300
+ register_setting('fvm-group-pro', 'fastvelocity_min_generate_gulp_files');
301
+ register_setting('fvm-group-pro', 'fastvelocity_min_generate_gulp_path');
302
+ register_setting('fvm-group-license', 'fastvelocity_min_license_code');
303
+ }
304
+
305
+
306
+
307
+ # add settings link on plugin page
308
+ function fastvelocity_min_settings_link($links) {
309
+ if (is_plugin_active(plugin_basename( __FILE__ ))) {
310
+ $settings_link = '<a href="options-general.php?page=fastvelocity-min">Settings</a>';
311
+ array_unshift($links, $settings_link);
312
+ }
313
+ return $links;
314
+ }
315
+ add_filter("plugin_action_links_".plugin_basename(__FILE__), 'fastvelocity_min_settings_link' );
316
+
317
+
318
+
319
+ # manage settings page
320
+ function fastvelocity_min_settings() {
321
+ if (!current_user_can('manage_options')) { wp_die( __('You do not have sufficient permissions to access this page.')); }
322
+
323
+ # tmp folder
324
+ global $tmpdir, $cachedir, $plugindir;
325
+
326
+ # get active tab, set default
327
+ $active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'status';
328
+
329
+ ?>
330
+ <div class="wrap">
331
+ <h1>Fast Velocity Minify</h1>
332
+
333
+ <?php
334
+
335
+ # purge all caches
336
+ if(isset($_POST['purgeall']) && $_POST['purgeall'] == 1) {
337
+ fvm_purge_all(); # purge all
338
+ echo __('<div class="notice notice-success is-dismissible"><p>The <strong>CSS and JS</strong> files have been purged!</p></div>');
339
+ echo fastvelocity_purge_others(); # purge third party caches
340
+ }
341
+ ?>
342
+
343
+ <h2 class="nav-tab-wrapper wp-clearfix">
344
+ <a href="?page=fastvelocity-min&tab=status" class="nav-tab <?php echo $active_tab == 'status' ? 'nav-tab-active' : ''; ?>">Status</a>
345
+ <a href="?page=fastvelocity-min&tab=settings" class="nav-tab <?php echo $active_tab == 'settings' ? 'nav-tab-active' : ''; ?>">Settings</a>
346
+ <?php
347
+ # this is still under development
348
+ $fvm_license = get_option('fastvelocity_min_license_code');
349
+ if($fvm_license != false && mb_strlen($fvm_license, 'UTF-8') == 32) {
350
+ ?>
351
+ <a href="?page=fastvelocity-min&tab=pro" class="nav-tab <?php echo $active_tab == 'pro' ? 'nav-tab-active' : ''; ?>">Extra</a>
352
+ <?php
353
+ }
354
+ ?>
355
+ <a href="?page=fastvelocity-min&tab=server" class="nav-tab <?php echo $active_tab == 'server' ? 'nav-tab-active' : ''; ?>">Server Info</a>
356
+ <a href="?page=fastvelocity-min&tab=help" class="nav-tab <?php echo $active_tab == 'help' ? 'nav-tab-active' : ''; ?>">Help</a>
357
+ </h2>
358
+
359
+
360
+ <?php if( $active_tab == 'status' ) { ?>
361
+
362
+ <div id="fastvelocity-min">
363
+ <div id="poststuff">
364
+ <div id="fastvelocity_min_processed" class="postbox-container">
365
+ <div class="meta-box">
366
+
367
+ <div class="postbox" id="tab-purge">
368
+ <h3 class="hndle"><span>Purge processed files</span></h3>
369
+ <div class="inside" id="fastvelocity_min_topbtns">
370
+ <ul class="processed">
371
+ <li id="purgeall-row">
372
+ <span class="filename">Purge processed CSS and JS files</span>
373
+ <span class="actions">
374
+ <form method="post" id="fastvelocity_min_clearall" action="<?php echo admin_url('options-general.php?page=fastvelocity-min&tab=status'); ?>">
375
+ <input type="hidden" name="purgeall" value="1" />
376
+ <?php submit_button('Delete', 'button-secondary', 'submit', false); ?>
377
+ </form>
378
+ </li>
379
+ </ul>
380
+ </div>
381
+ </div>
382
+
383
+ <div class="postbox" id="tab-js">
384
+ <h3 class="hndle"><span>List of processed JS files</span></h3>
385
+ <div class="inside" id="fastvelocity_min_jsprocessed">
386
+ <ul class="processed"></ul>
387
+ </div>
388
+ </div>
389
+
390
+ <div class="postbox" id="tab-css">
391
+ <h3 class="hndle"><span>List of processed CSS files</span></h3>
392
+ <div class="inside" id="fastvelocity_min_cssprocessed">
393
+ <ul class="processed"></ul>
394
+ </div>
395
+ </div>
396
+
397
+ </div>
398
+ </div>
399
+ </div>
400
+ </div>
401
+ <?php } ?>
402
+
403
+ <?php if( $active_tab == 'settings' ) { ?>
404
+ <form method="post" action="options.php">
405
+ <?php settings_fields('fvm-group'); do_settings_sections('fvm-group'); ?>
406
+
407
+ <div style="height: 20px;"></div>
408
+ <h2 class="title">Basic Options</h2>
409
+ <p class="fvm-bold-green">These options are generaly safe to edit as needed. If you use a cache plugin, kindly purge all your caches once you're done with the changes.</p>
410
+
411
+ <table class="form-table fvm-settings">
412
+ <tbody>
413
+
414
+ <tr>
415
+ <th scope="row">Troubleshooting</th>
416
+ <td><fieldset><legend class="screen-reader-text"><span>Troubleshooting</span></legend>
417
+ <label for="fastvelocity_min_fvm_fix_editor">
418
+ <input name="fastvelocity_min_fvm_fix_editor" type="checkbox" id="fastvelocity_min_fvm_fix_editor" value="1" <?php echo checked(1 == get_option('fastvelocity_min_fvm_fix_editor'), true, false); ?>>
419
+ Fix Page Editors <span class="note-info">[ If selected, logged in users with the "editor" role (and above) will bypass all optimizations ]</span></label>
420
+ </fieldset></td>
421
+ </tr>
422
+
423
+
424
+ <tr>
425
+ <th scope="row">URL Options</th>
426
+ <td>
427
+ <?php
428
+ # what to select
429
+ $sel = get_option('fastvelocity_min_default_protocol');
430
+ $a = ''; if($sel == 'dynamic' || empty($sel)) { $a = ' checked="checked"'; }
431
+ $b = ''; if($sel == 'http') { $b = ' checked="checked"'; }
432
+ $c = ''; if($sel == 'https') { $c = ' checked="checked"'; }
433
+ ?>
434
+ <fieldset><legend class="screen-reader-text"><span>URL Options</span></legend>
435
+ <label><input type="radio" name="fastvelocity_min_default_protocol" value="dynamic" <?php echo $a; ?>> Use the default dynamic "//" protocol</label><br>
436
+ <label><input type="radio" name="fastvelocity_min_default_protocol" value="http"<?php echo $b; ?>> Force HTTP urls</label><br>
437
+ <label><input type="radio" name="fastvelocity_min_default_protocol" value="https"<?php echo $c; ?>> Force HTTPS urls</span></label><br>
438
+ </fieldset>
439
+ </td>
440
+ </tr>
441
+
442
+ <tr>
443
+ <th scope="row">HTML Options</th>
444
+ <td><fieldset><legend class="screen-reader-text"><span>HTML Options</span></legend>
445
+ <label for="fastvelocity_min_skip_html_minification">
446
+ <input name="fastvelocity_min_skip_html_minification" type="checkbox" id="fastvelocity_min_skip_html_minification" value="1" <?php echo checked(1 == get_option('fastvelocity_min_skip_html_minification'), true, false); ?>>
447
+ Disable minification on HTML <span class="note-info">[ Normally, it's safe to leave HTML minification enabled ]</span></label>
448
+ <br />
449
+
450
+ <label for="fastvelocity_min_use_alt_html_minification">
451
+ <input name="fastvelocity_min_use_alt_html_minification" type="checkbox" id="fastvelocity_min_use_alt_html_minification" value="1" <?php echo checked(1 == get_option('fastvelocity_min_use_alt_html_minification'), true, false); ?>>
452
+ Use the alternative HTML minification <span class="note-info">[ Select this, if you have some problem with some spaces disappearing ]</span></label>
453
+ <br />
454
+
455
+ <label for="fastvelocity_min_strip_htmlcomments">
456
+ <input name="fastvelocity_min_strip_htmlcomments" type="checkbox" id="fastvelocity_min_strip_htmlcomments" value="1" <?php echo checked(1 == get_option('fastvelocity_min_strip_htmlcomments'), true, false); ?>>
457
+ Strip HTML comments <span class="note-info">[ Some plugins need HTML comments to work properly ]</span></label>
458
+ <br />
459
+
460
+ </fieldset></td>
461
+ </tr>
462
+
463
+
464
+ <tr>
465
+ <th scope="row">Fonts Options</th>
466
+ <td><fieldset><legend class="screen-reader-text"><span>Fonts Options</span></legend>
467
+ <label for="fastvelocity_min_skip_emoji_removal">
468
+ <input name="fastvelocity_min_skip_emoji_removal" type="checkbox" id="fastvelocity_min_skip_emoji_removal" class="jsprocessor" value="1" <?php echo checked(1 == get_option('fastvelocity_min_skip_emoji_removal'), true, false); ?> >
469
+ Stop removing Emojis and smileys <span class="note-info">[ If selected, Emojis will be left alone and won't be removed from wordpress ]</span></label>
470
+ <br />
471
+
472
+ <label for="fastvelocity_min_skip_google_fonts">
473
+ <input name="fastvelocity_min_skip_google_fonts" type="checkbox" id="fastvelocity_min_skip_google_fonts" value="1" <?php echo checked(1 == get_option('fastvelocity_min_skip_google_fonts'), true, false); ?> >
474
+ Disable Google Fonts merging <span class="note-info">[ If selected, Google Fonts will no longer be merged into one request ]</span></label>
475
+ <br />
476
+
477
+ <label for="fastvelocity_min_force_inline_googlefonts">
478
+ <input name="fastvelocity_min_force_inline_googlefonts" type="checkbox" id="fastvelocity_min_force_inline_googlefonts" value="1" <?php echo checked(1 == get_option('fastvelocity_min_force_inline_googlefonts'), true, false); ?> >
479
+ Inline Google Fonts CSS <span class="note-info">[ If selected, Google Fonts CSS code will be inlined using "*.woof" format - NOTE: IE9+ and <a target="_blank" href="http://caniuse.com/#feat=woff">modern browsers</a> only]</span></label>
480
+ <br />
481
+
482
+ </fieldset></td>
483
+ </tr>
484
+
485
+
486
+
487
+
488
+ <tr>
489
+ <th scope="row">CSS Options</th>
490
+ <td><fieldset><legend class="screen-reader-text"><span>CSS Options</span></legend>
491
+
492
+ <label for="fastvelocity_min_disable_css_merge">
493
+ <input name="fastvelocity_min_disable_css_merge" type="checkbox" id="fastvelocity_min_disable_css_merge" value="1" <?php echo checked(1 == get_option('fastvelocity_min_disable_css_merge'), true, false); ?>>
494
+ Disable CSS processing<span class="note-info">[ If selected, this plugin will ignore CSS files completely ]</span></label>
495
+ <br />
496
+ <label for="fastvelocity_min_disable_css_minification">
497
+ <input name="fastvelocity_min_disable_css_minification" type="checkbox" id="fastvelocity_min_disable_css_minification" value="1" <?php echo checked(1 == get_option('fastvelocity_min_disable_css_minification'), true, false); ?>>
498
+ Disable minification on CSS files <span class="note-info">[ If selected, CSS files will be merged but not minified ]</span></label>
499
+ <br />
500
+ <label for="fastvelocity_min_skip_cssorder">
501
+ <input name="fastvelocity_min_skip_cssorder" type="checkbox" id="fastvelocity_min_skip_cssorder" value="1" <?php echo checked(1 == get_option('fastvelocity_min_skip_cssorder'), true, false); ?> >
502
+ Preserve the order of CSS files <span class="note-info">[ If selected, you will have better CSS compatibility but possibly more CSS files]</span></label>
503
+ <br />
504
+ <label for="fastvelocity_min_remove_print_mediatypes">
505
+ <input name="fastvelocity_min_remove_print_mediatypes" type="checkbox" id="fastvelocity_min_remove_print_mediatypes" value="1" <?php echo checked(1 == get_option('fastvelocity_min_remove_print_mediatypes'), true, false); ?> >
506
+ Remove the "Print" related stylesheets <span class="note-info">[ If selected, CSS files of mediatype "print" will be removed from the site ]</span></label>
507
+ <br />
508
+ <label for="fastvelocity_min_force_inline_css">
509
+ <input name="fastvelocity_min_force_inline_css" type="checkbox" id="fastvelocity_min_force_inline_css" value="1" <?php echo checked(1 == get_option('fastvelocity_min_force_inline_css'), true, false); ?>>
510
+ Inline all header CSS files <span class="note-info">[ If selected, the header CSS will be inlined to avoid the "render blocking" on pagespeed insights tests ]</span></label>
511
+ <br />
512
+ <label for="fastvelocity_min_force_inline_css_footer">
513
+ <input name="fastvelocity_min_force_inline_css_footer" type="checkbox" id="fastvelocity_min_force_inline_css_footer" value="1" <?php echo checked(1 == get_option('fastvelocity_min_force_inline_css_footer'), true, false); ?>>
514
+ Inline all footer CSS files <span class="note-info">[ If selected, the footer CSS will be inlined to avoid the "render blocking" on pagespeed insights tests ]</span></label>
515
+ <br />
516
+ </fieldset></td>
517
+ </tr>
518
+
519
+
520
+ <tr>
521
+ <th scope="row">JavaScript Options</th>
522
+ <td><fieldset><legend class="screen-reader-text"><span>JavaScript Options</span></legend>
523
+ <label for="fastvelocity_min_disable_js_merge">
524
+ <input name="fastvelocity_min_disable_js_merge" type="checkbox" id="fastvelocity_min_disable_js_merge" value="1" <?php echo checked(1 == get_option('fastvelocity_min_disable_js_merge'), true, false); ?> >
525
+ Disable JavaScript processing <span class="note-info">[ If selected, this plugin will ignore JS files completely ]</span></label>
526
+ <br />
527
+
528
+ <?php
529
+ # check for exec + a supported java version
530
+ if(function_exists('exec') && exec('command -v java >/dev/null && echo "yes" || echo "no"') == 'yes') {
531
+ ?>
532
+ <label for="fastvelocity_min_use_yui">
533
+ <input name="fastvelocity_min_use_yui" type="checkbox" id="fastvelocity_min_use_yui" class="jsprocessor" value="1" <?php echo checked(1 == get_option('fastvelocity_min_use_yui'), true, false); ?> >
534
+ Minify with YUI Compressor <span class="note-info">[ If selected, it will try to use the YUI Compressor to minify JS files ]</span></label>
535
+ <br />
536
+ <?php } ?>
537
+
538
+ <label for="fastvelocity_min_disable_js_minification">
539
+ <input name="fastvelocity_min_disable_js_minification" type="checkbox" id="fastvelocity_min_disable_js_minification" value="1" <?php echo checked(1 == get_option('fastvelocity_min_disable_js_minification'), true, false); ?> >
540
+ Disable minification on JS files <span class="note-info">[ If selected, JS files will be merged but not minified ]</span></label>
541
+ <br />
542
+ </fieldset></td>
543
+ </tr>
544
+ </tbody></table>
545
+
546
+
547
+ <div style="height: 20px;"></div>
548
+ <h2 class="title">JS Advanced Options</h2>
549
+ <p class="fvm-bold-green">It's highly recommended that you only select the options below if you're an advanced user or developer and understand what these options mean.</p>
550
+
551
+ <table class="form-table fvm-settings">
552
+ <tbody>
553
+ <tr>
554
+ <th scope="row">Render-blocking JS</th>
555
+ <td><fieldset><legend class="screen-reader-text"><span>Render-blocking</span></legend>
556
+ <label for="fastvelocity_min_enable_defer_js">
557
+ <input name="fastvelocity_min_enable_defer_js" type="checkbox" id="fastvelocity_min_enable_defer_js" value="1" <?php echo checked(1 == get_option('fastvelocity_min_enable_defer_js'), true, false); ?>>
558
+ Enable defer parsing of JS files globally <span class="note-info">[ Not all browsers, themes or plugins support this. Beware of broken functionality and design ]</span></label>
559
+ <br />
560
+ <label for="fastvelocity_min_defer_for_pagespeed">
561
+ <input name="fastvelocity_min_defer_for_pagespeed" type="checkbox" id="fastvelocity_min_defer_for_pagespeed" value="1" <?php echo checked(1 == get_option('fastvelocity_min_defer_for_pagespeed'), true, false); ?>>
562
+ Enable defer of JS for Pagespeed Insights <span class="note-info">[ This will defer JS files for Pagespeed Insights only, <a target="_blank" href="https://www.chromestatus.com/feature/5718547946799104">except external scripts</a> loaded from other domains ]</span></label>
563
+ <br />
564
+
565
+ <label for="fastvelocity_min_exclude_defer_jquery">
566
+ <input name="fastvelocity_min_exclude_defer_jquery" type="checkbox" id="fastvelocity_min_exclude_defer_jquery" value="1" <?php echo checked(1 == get_option('fastvelocity_min_exclude_defer_jquery'), true, false); ?> >
567
+ Skip deferring the jQuery library <span class="note-info">[ Will fix "undefined jQuery" errors on the Google Chrome console log ]</span></label>
568
+ <br />
569
+ <label for="fastvelocity_min_exclude_defer_login">
570
+ <input name="fastvelocity_min_exclude_defer_login" type="checkbox" id="fastvelocity_min_exclude_defer_login" value="1" <?php echo checked(1 == get_option('fastvelocity_min_exclude_defer_login'), true, false); ?> >
571
+ Skip deferring completely on the login page <span class="note-info">[ If selected, will disable JS deferring on your login page ]</span></label>
572
+ <br />
573
+
574
+
575
+ </fieldset></td>
576
+ </tr>
577
+ </tbody></table>
578
+
579
+
580
+
581
+ <div style="height: 20px;"></div>
582
+ <h2 class="title">JS and CSS Exceptions</h2>
583
+ <p class="fvm-bold-green">You can use this section to exclude certain CSS or JS files from being processed in case of conflicts while merging.</p>
584
+
585
+ <table class="form-table fvm-settings">
586
+ <tbody>
587
+ <tr>
588
+ <th scope="row">Ignore List</th>
589
+ <td><fieldset><legend class="screen-reader-text"><span>Ignore List</span></legend>
590
+ <p><label for="blacklist_keys"><span class="fvm-label-pad">Ignore the following CSS and JS paths below:</span></label></p>
591
+ <p>
592
+ <textarea name="fastvelocity_min_ignore" rows="10" cols="50" id="fastvelocity_min_ignore" class="large-text code" placeholder="ex: /wp-includes/js/jquery/jquery.js"><?php echo get_option('fastvelocity_min_ignore'); ?></textarea>
593
+ </p>
594
+ <p class="description">[ Remove all query strings and use only relative or partial urls, ex: /wp-includes/js/jquery/jquery.js ]</p>
595
+ <br />
596
+ </fieldset></td>
597
+ </tr>
598
+ </tbody></table>
599
+
600
+
601
+ <div style="height: 20px;"></div>
602
+ <h2 class="title">CDN Options</h2>
603
+ <p class="fvm-bold-green">This rewrite our generated CSS and JS urls to use your cdn domain name.</p>
604
+
605
+ <table class="form-table fvm-settings">
606
+ <tbody>
607
+ <tr>
608
+ <th scope="row"><span class="fvm-label-special">CDN URL</span></th>
609
+ <td><fieldset><legend class="screen-reader-text"><span>CDN URL</span></legend>
610
+ <label for="fastvelocity_min_fvm_cdn_url">
611
+ <p><input type="text" name="fastvelocity_min_fvm_cdn_url" id="fastvelocity_min_fvm_cdn_url" value="<?php echo get_option('fastvelocity_min_fvm_cdn_url', ''); ?>" size="80" /></p>
612
+ <p class="description">[ Leave empty, or enter your CDN URL domain name here ]</p></label>
613
+ </fieldset></td>
614
+ </tr>
615
+ </tbody></table>
616
+
617
+
618
+ <div style="height: 20px;"></div>
619
+ <h2 class="title">Preconnect Optimization</h2>
620
+ <p class="fvm-bold-green">Please make sure you understand these options before using them.</p>
621
+
622
+ <table class="form-table fvm-settings">
623
+ <tbody>
624
+ <tr>
625
+ <th scope="row">Preconnect Headers</th>
626
+ <td><fieldset><legend class="screen-reader-text"><span>Preconnect</span></legend>
627
+ <label for="fastvelocity_min_preconnect"><span class="fvm-label-pad">Insert one domain name url per line:</span></label>
628
+ <p>
629
+ <textarea name="fastvelocity_min_preconnect" rows="10" cols="50" id="fastvelocity_min_preconnect" class="large-text code" placeholder="ex: //fonts.gstatic.com"><?php echo get_option('fastvelocity_min_preconnect'); ?></textarea>
630
+ </p>
631
+ <p class="description">[ Use only the necessary domain names, such as remote font domain names, ex: //fonts.gstatic.com ]</p>
632
+ </fieldset></td>
633
+ </tr>
634
+
635
+ </tbody></table>
636
+
637
+
638
+ <div style="height: 20px;"></div>
639
+ <h2 class="title">Homepage Optimization</h2>
640
+ <p class="fvm-bold-green">Prioritize visible content by preloading images with high priority, such as the first image on a slider that is shown above the folder.</p>
641
+
642
+ <table class="form-table fvm-settings">
643
+ <tbody>
644
+ <tr>
645
+ <th scope="row">Preload Images</th>
646
+ <td><fieldset><legend class="screen-reader-text"><span>Preload Images</span></legend>
647
+ <label for="fastvelocity_min_preload"><span class="fvm-label-pad">Insert one image url per line:</span></label>
648
+ <p>
649
+ <textarea name="fastvelocity_min_preload" rows="10" cols="50" id="fastvelocity_min_preload" class="large-text code" placeholder="ex: //yoursite.com/wp-content/plugins/some-slider/large.img"><?php echo get_option('fastvelocity_min_preload'); ?></textarea>
650
+ </p>
651
+ <p class="description">[ Use only for large images that first load above the fold. Read the Help section for more details. ]</p>
652
+ </fieldset></td>
653
+ </tr>
654
+ </tbody></table>
655
+
656
+
657
+ <p class="submit"><input type="submit" name="fastvelocity_min_save_options" id="fastvelocity_min_save_options" class="button button-primary" value="Save Changes"></p>
658
+ </form>
659
+ <?php } ?>
660
+
661
+
662
+ <?php if( $active_tab == 'pro' ) {
663
+
664
+ # this is still under development
665
+ $fvm_license = get_option('fastvelocity_min_license_code');
666
+ if($fvm_license != false && mb_strlen($fvm_license, 'UTF-8') == 32) {
667
+ ?>
668
+
669
+ <form method="post" action="options.php">
670
+ <?php settings_fields('fvm-group-pro'); do_settings_sections('fvm-group-pro'); ?>
671
+
672
+ <div style="height: 20px;"></div>
673
+ <h2 class="title">Extra Optimization</h2>
674
+ <p class="fvm-bold-green">These options are available for the professional version only.<br />
675
+ When deferring all generated css files, you must enqueue any inline css code that was supposed to load after the generated files.</p>
676
+
677
+
678
+ <table class="form-table fvm-settings">
679
+ <tbody>
680
+
681
+ <tr>
682
+ <th scope="row">Extra CSS Options</th>
683
+ <td><fieldset><legend class="screen-reader-text"><span>Extra CSS Options</span></legend>
684
+ <label for="fastvelocity_min_send_css_to_footer">
685
+ <input name="fastvelocity_min_send_css_to_footer" type="checkbox" id="fastvelocity_min_send_css_to_footer" value="1" <?php echo checked(1 == get_option('fastvelocity_min_send_css_to_footer'), true, false); ?>>
686
+ Async CSS with LoadCSS<span class="note-info">[ If selected, you will need a Critical Path CSS to Optimize CSS Delivery ]</span></label>
687
+ <br />
688
+ <label for="fastvelocity_min_critical_css_tester">
689
+ <input name="fastvelocity_min_critical_css_tester" type="checkbox" id="fastvelocity_min_critical_css_tester" value="1" <?php echo checked(1 == get_option('fastvelocity_min_critical_css_tester'), true, false); ?>>
690
+ Critical Path Tester<span class="note-info">[ If selected, only the critical path code will be shown and no css files will be enqueued ]</span></label>
691
+ </fieldset>
692
+ </td>
693
+ </tr>
694
+
695
+ <tr>
696
+ <th scope="row">Global Critical CSS</th>
697
+ <td>
698
+ <p>
699
+ <textarea name="fastvelocity_min_critical_path_css" rows="20" cols="50" id="fastvelocity_min_critical_path_css" class="large-text code" placeholder="your css code here"><?php echo get_option('fastvelocity_min_critical_path_css'); ?></textarea>
700
+ </p>
701
+ <p class="description">[ Please minify your CSS code manually and insert your Global Critical CSS here ]</p>
702
+ </td>
703
+ </tr>
704
+
705
+
706
+ <tr>
707
+ <th scope="row">Gulp Options</th>
708
+ <td><fieldset>
709
+ <label for="fastvelocity_min_generate_gulp_files">
710
+ <input name="fastvelocity_min_generate_gulp_files" type="checkbox" id="fastvelocity_min_generate_gulp_files" value="1" <?php echo checked(1 == get_option('fastvelocity_min_generate_gulp_files'), true, false); ?>>
711
+ Generate gulp-uncss files<span class="note-info">[ If selected, it will try to generate one gulp-uncss per pageview ]</span></label>
712
+ </fieldset>
713
+ </td>
714
+ </tr>
715
+
716
+ <tr>
717
+ <th scope="row">Gulp Directory</th>
718
+ <td>
719
+ <label for="fastvelocity_min_generate_gulp_path">
720
+ <input name="fastvelocity_min_generate_gulp_path" type="text" id="fastvelocity_min_generate_gulp_path" value="<?php echo get_option('fastvelocity_min_generate_gulp_path'); ?>" class="regular-text code">
721
+ <p class="description">The absolute path to your local "Gulp" working directory</p>
722
+ </td>
723
+ </tr>
724
+
725
+
726
+ </tbody></table>
727
+
728
+ <p class="submit"><input type="submit" name="fastvelocity_min_save_options" id="fastvelocity_min_save_options" class="button button-primary" value="Save Changes"></p>
729
+ </form>
730
+
731
+ <?php
732
+ } else {
733
+
734
+ ?>
735
+
736
+ <form method="post" action="options.php">
737
+ <?php settings_fields('fvm-group-license'); do_settings_sections('fvm-group-license'); ?>
738
+
739
+ <div style="height: 20px;"></div>
740
+ <h2 class="title">Extra Optimization</h2>
741
+ <p class="fvm-bold-green">These options are available for the professional version only.</p>
742
+
743
+ <table class="form-table fvm-settings">
744
+ <tbody>
745
+
746
+ <tr>
747
+ <th scope="row">License Code</th>
748
+ <td>
749
+ <input name="fastvelocity_min_license_code" type="text" id="fastvelocity_min_license_code" value="<?php echo get_option('fastvelocity_min_license_code'); ?>" class="regular-text">
750
+ <p class="description">Please insert the license code that has been provided.</p>
751
+ </td>
752
+ </tr>
753
+
754
+ </tbody></table>
755
+
756
+ <p class="submit"><input type="submit" name="fastvelocity_min_save_options" id="fastvelocity_min_save_options" class="button button-primary" value="Save Changes"></p>
757
+ </form>
758
+
759
+ <?php
760
+ }
761
+ }
762
+ ?>
763
+
764
+
765
+ <?php
766
+ if( $active_tab == 'server' ) {
767
+ fvm_get_generalinfo();
768
+ }
769
+ ?>
770
+
771
+ <?php if( $active_tab == 'help' ) { ?>
772
+
773
+ <div class="wrap" id="fastvelocity-min">
774
+ <div id="poststuff">
775
+ <div id="fastvelocity_min_processed" class="postbox-container">
776
+ <div class="meta-box-sortables ui-sortable">
777
+
778
+
779
+
780
+
781
+ <div class="postbox" id="tab-info">
782
+ <h3 class="hndle"><span>Frequently Asked Questions</span></h3>
783
+ <div class="inside"><? echo fastvelocity_min_readme($plugindir.'readme.txt'); ?></div>
784
+ </div>
785
+
786
+ </div>
787
+ </div>
788
+ </div>
789
+ </div>
790
+ <?php } ?>
791
+
792
+
793
+
794
+ </div>
795
+
796
+ <div class="clear"></div>
797
+
798
+ <?php
799
+ }
800
+
801
+
802
+ ###########################################
803
+ # process header javascript ###############
804
+ ###########################################
805
+ function fastvelocity_min_merge_header_scripts() {
806
+ global $wp_scripts, $wp_domain, $wp_home, $wp_home_path, $cachedir, $cachedirurl, $ignore, $disable_js_merge, $disable_js_minification, $enable_defer_js, $exclude_defer_jquery;
807
+ if(!is_object($wp_scripts)) { return false; }
808
+ $scripts = wp_clone($wp_scripts);
809
+ $scripts->all_deps($scripts->queue);
810
+ $ctime = get_option('fvm-last-cache-update', '0');
811
+ $header = array();
812
+
813
+ # mark as done (as we go)
814
+ $done = $scripts->done;
815
+
816
+ # add defaults to ignore list
817
+ $ignore = fastvelocity_default_ignore($ignore);
818
+
819
+ # get groups of handles
820
+ foreach( $scripts->to_do as $handle ) :
821
+
822
+ # is it a footer script?
823
+ $is_footer = 0; if (isset($wp_scripts->registered[$handle]->extra["group"]) || isset($wp_scripts->registered[$handle]->args)) { $is_footer = 1; }
824
+
825
+ # skip footer scripts for now
826
+ if($is_footer != 1) {
827
+
828
+ # get full url
829
+ $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
830
+
831
+ # IE only files don't increment things
832
+ $ieonly = fastvelocity_ie_blacklist($hurl);
833
+ if($ieonly == true) { continue; }
834
+
835
+ # skip ignore list, scripts with conditionals, external scripts
836
+ if ((!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && fvm_internal_url($hurl, $wp_home)) || empty($hurl)) {
837
+
838
+ # process
839
+ if(isset($header[count($header)-1]['handle']) || count($header) == 0) {
840
+ array_push($header, array('handles'=>array()));
841
+ }
842
+
843
+ # push it to the array
844
+ array_push($header[count($header)-1]['handles'], $handle);
845
+
846
+ # external and ignored scripts
847
+ } else {
848
+ array_push($header, array('handle'=>$handle));
849
+ }
850
+
851
+ # make sure that the scripts skipped here, show up in the footer
852
+ } else {
853
+ $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
854
+ wp_enqueue_script($handle, $hurl, array(), null, true);
855
+ }
856
+ endforeach;
857
+
858
+
859
+ # loop through header scripts and merge
860
+ for($i=0,$l=count($header);$i<$l;$i++) {
861
+ if(!isset($header[$i]['handle'])) {
862
+
863
+ # static cache file info + done
864
+ $done = array_merge($done, $header[$i]['handles']);
865
+ $hash = 'header-'.hash('adler32',implode('',$header[$i]['handles']));
866
+
867
+ # create cache files and urls
868
+ $file = $cachedir.'/'.$hash.'-'.$ctime.'.min.js';
869
+ $file_url = fvm_get_protocol($cachedirurl.'/'.$hash.'-'.$ctime.'.min.js');
870
+
871
+ # generate a new cache file
872
+ if (!file_exists($file)) {
873
+
874
+ # code and log initialization
875
+ $log = '';
876
+ $code = '';
877
+
878
+ # minify and write to file
879
+ foreach($header[$i]['handles'] as $handle) :
880
+ if(!empty($wp_scripts->registered[$handle]->src)) {
881
+
882
+ # get hurl per handle
883
+ $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
884
+ $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
885
+
886
+ # get css from hurl, if available and valid
887
+ $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
888
+ $newcode = false; $newcode = get_transient($tkey);
889
+ if ( $newcode === false) {
890
+ $res = fvm_download_and_cache($hurl, $tkey, null, $disable_js_minification, 'js');
891
+ if(is_array($res)) {
892
+ $newcode = $res['code'];
893
+ $newlog = $res['log'];
894
+ }
895
+ } else {
896
+ $newlog = "$printurl --- Debug: Fetched from transients cache with key $tkey ---\n";
897
+ }
898
+
899
+ # append
900
+ if($newcode !== false) {
901
+ $code.= $newcode;
902
+ $log.= $newlog;
903
+ }
904
+
905
+ # consider dependencies on handles with an empty src
906
+ } else {
907
+ wp_dequeue_script($handle); wp_enqueue_script($handle);
908
+ }
909
+ endforeach;
910
+
911
+ # prepare log
912
+ $log = "PROCESSED on ".date('r')."\n".$log."PROCESSED from ".home_url(add_query_arg( NULL, NULL ))."\n";
913
+
914
+ # generate cache, write log
915
+ file_put_contents($file.'.txt', $log);
916
+ file_put_contents($file, $code);
917
+ file_put_contents($file.'.gz', gzencode(file_get_contents($file), 9));
918
+ }
919
+
920
+ # register minified file
921
+ wp_register_script("fvm-header-$i", $file_url, array(), null, false);
922
+
923
+ # add all extra data from wp_localize_script
924
+ $data = array();
925
+ foreach($header[$i]['handles'] as $handle) {
926
+ if(isset($wp_scripts->registered[$handle]->extra['data'])) { $data[] = $wp_scripts->registered[$handle]->extra['data']; }
927
+ }
928
+ if(count($data) > 0) { $wp_scripts->registered["fvm-header-$i"]->extra['data'] = implode("\n", $data); }
929
+
930
+ # enqueue file
931
+ wp_enqueue_script("fvm-header-$i");
932
+
933
+ # other scripts need to be requeued for the order of files to be kept
934
+ } else {
935
+ wp_dequeue_script($header[$i]['handle']); wp_enqueue_script($header[$i]['handle']);
936
+ }
937
+ }
938
+
939
+ # remove from queue
940
+ $wp_scripts->done = $done;
941
+ }
942
+ ###########################################
943
+
944
+
945
+
946
+ ###########################################
947
+ # process js in the footer ################
948
+ ###########################################
949
+ function fastvelocity_min_merge_footer_scripts() {
950
+ global $wp_scripts, $wp_domain, $wp_home, $wp_home_path, $cachedir, $cachedirurl, $ignore, $disable_js_merge, $disable_js_minification, $enable_defer_js, $exclude_defer_jquery;
951
+ if(!is_object($wp_scripts)) { return false; }
952
+ $ctime = get_option('fvm-last-cache-update', '0');
953
+ $scripts = wp_clone($wp_scripts);
954
+ $scripts->all_deps($scripts->queue);
955
+ $footer = array();
956
+
957
+ # mark as done (as we go)
958
+ $done = $scripts->done;
959
+
960
+ # add defaults to ignore list
961
+ $ignore = fastvelocity_default_ignore($ignore);
962
+
963
+
964
+ # get groups of handles
965
+ foreach( $scripts->to_do as $handle ) :
966
+
967
+ # get full url
968
+ $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
969
+
970
+ # IE only files don't increment things
971
+ $ieonly = fastvelocity_ie_blacklist($hurl);
972
+ if($ieonly == true) { continue; }
973
+
974
+ # skip ignore list, scripts with conditionals, external scripts
975
+ if ((!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && fvm_internal_url($hurl, $wp_home)) || empty($hurl)) {
976
+
977
+ # process
978
+ if(isset($footer[count($footer)-1]['handle']) || count($footer) == 0) {
979
+ array_push($footer, array('handles'=>array()));
980
+ }
981
+
982
+ # push it to the array
983
+ array_push($footer[count($footer)-1]['handles'], $handle);
984
+
985
+ # external and ignored scripts
986
+ } else {
987
+ array_push($footer, array('handle'=>$handle));
988
+ }
989
+ endforeach;
990
+
991
+ # loop through footer scripts and merge
992
+ for($i=0,$l=count($footer);$i<$l;$i++) {
993
+ if(!isset($footer[$i]['handle'])) {
994
+
995
+ # static cache file info + done
996
+ $done = array_merge($done, $footer[$i]['handles']);
997
+ $hash = 'footer-'.hash('adler32',implode('',$footer[$i]['handles']));
998
+
999
+ # create cache files and urls
1000
+ $file = $cachedir.'/'.$hash.'-'.$ctime.'.min.js';
1001
+ $file_url = fvm_get_protocol($cachedirurl.'/'.$hash.'-'.$ctime.'.min.js');
1002
+
1003
+ # generate a new cache file
1004
+ if (!file_exists($file)) {
1005
+
1006
+ # code and log initialization
1007
+ $log = '';
1008
+ $code = '';
1009
+
1010
+ # minify and write to file
1011
+ foreach($footer[$i]['handles'] as $handle) :
1012
+ if(!empty($wp_scripts->registered[$handle]->src)) {
1013
+
1014
+ # get hurl per handle
1015
+ $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
1016
+ $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
1017
+
1018
+ # get css from hurl, if available and valid
1019
+ $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
1020
+ $newcode = false; $newcode = get_transient($tkey);
1021
+ if ( $newcode === false) {
1022
+ $res = fvm_download_and_cache($hurl, $tkey, null, $disable_js_minification, 'js');
1023
+ if(is_array($res)) {
1024
+ $newcode = $res['code'];
1025
+ $newlog = $res['log'];
1026
+ }
1027
+ } else {
1028
+ $newlog = "$printurl --- Debug: Fetched from transients cache with key $tkey ---\n";
1029
+ }
1030
+
1031
+ # append
1032
+ if($newcode !== false) {
1033
+ $code.= $newcode;
1034
+ $log.= $newlog;
1035
+ }
1036
+
1037
+ # consider dependencies on handles with an empty src
1038
+ } else {
1039
+ wp_dequeue_script($handle); wp_enqueue_script($handle);
1040
+ }
1041
+ endforeach;
1042
+
1043
+ # prepare log
1044
+ $log = "PROCESSED on ".date('r')."\n".$log."PROCESSED from ".home_url(add_query_arg( NULL, NULL ))."\n";
1045
+
1046
+ # generate cache, write log
1047
+ file_put_contents($file.'.txt', $log);
1048
+ file_put_contents($file, $code);
1049
+ file_put_contents($file.'.gz', gzencode(file_get_contents($file), 9));
1050
+ }
1051
+
1052
+ # register minified file
1053
+ wp_register_script("fvm-footer-$i", $file_url, array(), null, false);
1054
+
1055
+ # add all extra data from wp_localize_script
1056
+ $data = array();
1057
+ foreach($footer[$i]['handles'] as $handle) {
1058
+ if(isset($wp_scripts->registered[$handle]->extra['data'])) { $data[] = $wp_scripts->registered[$handle]->extra['data']; }
1059
+ }
1060
+ if(count($data) > 0) { $wp_scripts->registered["fvm-footer-$i"]->extra['data'] = implode("\n", $data); }
1061
+
1062
+ # enqueue file
1063
+ wp_enqueue_script("fvm-footer-$i");
1064
+
1065
+ # other scripts need to be requeued for the order of files to be kept
1066
+ } else {
1067
+ wp_dequeue_script($footer[$i]['handle']); wp_enqueue_script($footer[$i]['handle']);
1068
+ }
1069
+ }
1070
+
1071
+ # remove from queue
1072
+ $wp_scripts->done = $done;
1073
+ }
1074
+ ##############################
1075
+
1076
+
1077
+
1078
+ ###########################################
1079
+ # enable defer for JavaScript (WP 4.1 and above) and remove query strings for ignored files
1080
+ ###########################################
1081
+ function fastvelocity_min_defer_js($tag, $handle, $src) {
1082
+ global $ignore, $enable_defer_js, $defer_for_pagespeed, $wp_domain, $exclude_defer_login;
1083
+
1084
+ # no query strings
1085
+ if (stripos($src, '?ver') !== false) {
1086
+ $srcf = stristr($src, '?ver', true);
1087
+ $tag = str_ireplace($src, $srcf, $tag);
1088
+ $src = $srcf;
1089
+ }
1090
+
1091
+ # should we exclude defer on the login page?
1092
+ if($exclude_defer_login == true && stripos($_SERVER["SCRIPT_NAME"], strrchr(wp_login_url(), '/')) !== false){ return $tag; }
1093
+
1094
+ # reprocess the ignore list to remove the /fvm/cache/ from the list (else won't defer)
1095
+ if(is_array($ignore)) { $nignore = array(); foreach ($ignore as $i) { if($i != '/fvm/cache/') { $nignore[] = $i; } } }
1096
+
1097
+ # when to defer, order matters
1098
+ $defer = 0; if($enable_defer_js == true) { $defer = 1; }
1099
+ if (fastvelocity_min_in_arrayi($src, $nignore)) { $defer = 0; }
1100
+
1101
+ # get available nodes and add create with defer tag (if not async)
1102
+ $dom = new DOMDocument();
1103
+ libxml_use_internal_errors(true);
1104
+ @$dom->loadHTML($tag);
1105
+ $nodes = $dom->getElementsByTagName('script');
1106
+ $tagdefer = '';
1107
+ if ($nodes->length != 0) {
1108
+ $node = $dom->getElementsByTagName('script')->item(0);
1109
+ if (!$node->hasAttribute('async')) { $node->setAttribute('defer','defer'); };
1110
+ $tagdefer = $dom->saveHTML($node);
1111
+ }
1112
+
1113
+ # skip the nignore list by default, defer the rest
1114
+ if ($defer == 0) {
1115
+
1116
+ # no defer
1117
+ if ($defer_for_pagespeed != true) { return $tag; } else {
1118
+
1119
+ # return if external script url https://www.chromestatus.com/feature/5718547946799104
1120
+ if (stripos($src, $wp_domain) === false) { return $tag; }
1121
+
1122
+ # print code or return
1123
+ if(!empty($tagdefer)) {
1124
+ $deferinsights = '<script type="text/javascript">if(navigator.userAgent.match(/speed|gtmetrix|x11.*firefox\/53|x11.*chrome\/39/i)){document.write('.json_encode($tagdefer).');}else{document.write('.json_encode($tag).');}</script>';
1125
+ return preg_replace('#<script(.*?)>(.*?)</script>#is', $deferinsights, $tag);
1126
+ }
1127
+
1128
+ return $tag;
1129
+ }
1130
+
1131
+ # normal defer enabled
1132
+ } else { return $tagdefer; }
1133
+ }
1134
+ ###########################################
1135
+
1136
+
1137
+
1138
+
1139
+ ###########################################
1140
+ # process header css ######################
1141
+ ###########################################
1142
+ function fastvelocity_min_merge_header_css() {
1143
+ global $wp_styles, $wp_domain, $wp_home, $wp_home_path, $cachedir, $cachedirurl, $ignore, $disable_css_merge, $disable_css_minification, $skip_google_fonts, $skip_cssorder, $remove_print_mediatypes, $force_inline_css, $force_inline_googlefonts, $send_css_to_footer, $critical_path_css, $critical_css_tester;
1144
+ if(!is_object($wp_styles)) { return false; }
1145
+ $ctime = get_option('fvm-last-cache-update', '0');
1146
+ $styles = wp_clone($wp_styles);
1147
+ $styles->all_deps($styles->queue);
1148
+ $done = $styles->done;
1149
+ $header = array();
1150
+ $google_fonts = array();
1151
+ $process = array();
1152
+ $inline_css = array();
1153
+
1154
+ # add defaults to ignore list
1155
+ $ignore = fastvelocity_default_ignore($ignore);
1156
+
1157
+
1158
+ # get list of handles to process, dequeue duplicate css urls and keep empty source handles (for dependencies)
1159
+ $uniq = array(); $gfonts = array();
1160
+ foreach( $styles->to_do as $handle):
1161
+
1162
+ # conditionals
1163
+ $conditional = NULL; if(isset($wp_styles->registered[$handle]->extra["conditional"])) {
1164
+ $conditional = $wp_styles->registered[$handle]->extra["conditional"]; # such as ie7, ie8, ie9, etc
1165
+ }
1166
+
1167
+ # mediatype
1168
+ $mediatype = isset($wp_styles->registered[$handle]->args) ? $wp_styles->registered[$handle]->args : 'all'; # such as all, print, mobile, etc
1169
+ if ($mediatype == 'screen' || $mediatype == 'screen, print') { $mediatype = 'all'; }
1170
+
1171
+ # full url or empty
1172
+ $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
1173
+
1174
+ # mark duplicates as done and remove from the queue
1175
+ if(!empty($hurl)) {
1176
+ $key = hash('adler32', $hurl);
1177
+ if (isset($uniq[$key])) { $done = array_merge($done, array($handle)); continue; } else { $uniq[$key] = $handle; }
1178
+ }
1179
+
1180
+ # array of info to save
1181
+ $arr = array('handle'=>$handle, 'url'=>$hurl, 'conditional'=>$conditional, 'mediatype'=>$mediatype);
1182
+
1183
+ # google fonts to the top (collect and skip process array)
1184
+ if (stripos($hurl, 'fonts.googleapis.com') !== false) {
1185
+ if(!$skip_google_fonts) { $google_fonts[$handle] = $hurl; } else { wp_enqueue_style($handle); } # skip google fonts optimization?
1186
+ continue;
1187
+ }
1188
+
1189
+ # all else
1190
+ $process[$handle] = $arr;
1191
+
1192
+ endforeach;
1193
+
1194
+
1195
+ # concat google fonts, if enabled
1196
+ if(!$skip_google_fonts && count($google_fonts) > 0) {
1197
+ $concat_google_fonts = fastvelocity_min_concatenate_google_fonts($google_fonts);
1198
+ foreach ($google_fonts as $h=>$a) { $done = array_merge($done, array($h)); } # mark as done
1199
+ if($force_inline_googlefonts == false) {
1200
+ wp_enqueue_style('header-fvm-fonts', fvm_get_protocol($concat_google_fonts), array(), null, 'all');
1201
+ } else {
1202
+
1203
+ # google fonts download and inlining, ignore logs
1204
+ $tkey = 'fvm-cache-'.$ctime.hash('adler32', $concat_google_fonts);
1205
+ $newcode = false; $newcode = get_transient($tkey);
1206
+ if ( $newcode === false) {
1207
+ $res = fvm_download_and_cache($concat_google_fonts, $tkey, null, $disable_minification, 'css');
1208
+ if(is_array($res)) { $newcode = $res['code']; }
1209
+ }
1210
+
1211
+ # inline css or fail
1212
+ if($newcode !== false) {
1213
+ echo '<style type="text/css" media="all">'.$newcode.'</style>'."\n";
1214
+ } else {
1215
+ echo "<!-- GOOGLE FONTS REQUEST FAILED for $concat_google_fonts -->\n"; # log if failed
1216
+ }
1217
+ }
1218
+ }
1219
+
1220
+
1221
+
1222
+
1223
+ # get groups of handles
1224
+ foreach( $styles->to_do as $handle ) :
1225
+
1226
+ # skip already processed google fonts and empty dependencies
1227
+ if(isset($google_fonts[$handle])) { continue; } # skip google fonts
1228
+ if(empty($wp_styles->registered[$handle]->src)) { continue; } # skip empty src
1229
+ if (fastvelocity_min_in_arrayi($handle, $done)) { continue; } # skip if marked as done before
1230
+ if (!isset($process[$handle])) { continue; } # skip if not on our unique process list
1231
+
1232
+ # get full url
1233
+ $hurl = $process[$handle]['url'];
1234
+ $conditional = $process[$handle]['conditional'];
1235
+ $mediatype = $process[$handle]['mediatype'];
1236
+
1237
+ # IE only files don't increment things
1238
+ $ieonly = fastvelocity_ie_blacklist($hurl);
1239
+ if($ieonly == true) { continue; }
1240
+
1241
+ # skip ignore list, conditional css, external css
1242
+ if ((!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($conditional) && fvm_internal_url($hurl, $wp_home)) || empty($hurl)) {
1243
+
1244
+ # colect inline css for this handle
1245
+ if(isset($wp_styles->registered[$handle]->extra['after']) && is_array($wp_styles->registered[$handle]->extra['after'])) {
1246
+ $inline_css[$handle] = fastvelocity_min_minify_css_string(implode('', $wp_styles->registered[$handle]->extra['after'])); # save
1247
+ $wp_styles->registered[$handle]->extra['after'] = null; # dequeue
1248
+ }
1249
+
1250
+ # process
1251
+ if(isset($header[count($header)-1]['handle']) || count($header) == 0 || $header[count($header)-1]['media'] != $mediatype) {
1252
+ array_push($header, array('handles'=>array(), 'media'=>$mediatype));
1253
+ }
1254
+
1255
+ # push it to the array
1256
+ array_push($header[count($header)-1]['handles'], $handle);
1257
+
1258
+ # collect generated file urls, except print
1259
+ if ($mediatype != 'print') { $GLOBALS['used_css_files'][] = $hurl; }
1260
+
1261
+ # external and ignored css
1262
+ } else {
1263
+
1264
+ # collect generated file urls for gulp, except print and conditionals
1265
+ if ($mediatype != 'print' && !isset($conditional)) { $GLOBALS['used_css_files'][] = $hurl; }
1266
+
1267
+ # normal enqueuing
1268
+ array_push($header, array('handle'=>$handle));
1269
+ }
1270
+ endforeach;
1271
+
1272
+
1273
+ # reorder CSS by mediatypes
1274
+ if(!$skip_cssorder) {
1275
+ if(count($header) > 0) {
1276
+
1277
+ # get unique mediatypes
1278
+ $allmedia = array();
1279
+ foreach($header as $array) {
1280
+ if(isset($array['media'])) { $allmedia[$array['media']] = ''; }
1281
+ }
1282
+
1283
+ # extract handles by mediatype
1284
+ $grouphandles = array();
1285
+ foreach ($allmedia as $md=>$var) {
1286
+ foreach($header as $array) {
1287
+ if (isset($array['media']) && $array['media'] == $md) {
1288
+ foreach($array['handles'] as $h) { $grouphandles[$md][] = $h; }
1289
+ }
1290
+ }
1291
+ }
1292
+
1293
+ # reset and reorder header by mediatypes
1294
+ $newheader = array();
1295
+ foreach ($allmedia as $md=>$var) { $newheader[] = array('handles' => $grouphandles[$md], 'media'=>$md); }
1296
+ if(count($newheader) > 0) { $header = $newheader; }
1297
+ }
1298
+ }
1299
+
1300
+
1301
+ # critical path
1302
+ if(!empty($critical_path_css) && $critical_path_css != false) {
1303
+ if($send_css_to_footer != false) {
1304
+ echo '<style id="critical-path-global" type="text/css" media="all">'.$critical_path_css.'</style>'."\n";
1305
+ }
1306
+ }
1307
+
1308
+
1309
+ # loop through header css and merge
1310
+ for($i=0,$l=count($header);$i<$l;$i++) {
1311
+ if(!isset($header[$i]['handle'])) {
1312
+
1313
+ # get has for the inline css in this group
1314
+ $inline_css_group = array();
1315
+ foreach($header[$i]['handles'] as $h) { if(isset($inline_css[$h]) && !empty($inline_css[$h])) { $inline_css_group[] = $inline_css[$h]; } }
1316
+ $inline_css_hash = md5(implode('',$inline_css_group));
1317
+
1318
+ # static cache file info + done
1319
+ $done = array_merge($done, $header[$i]['handles']);
1320
+ $hash = 'header-'.hash('adler32',implode('',$header[$i]['handles']).$inline_css_hash);
1321
+
1322
+ # create cache files and urls
1323
+ $file = $cachedir.'/'.$hash.'-'.$ctime.'.min.css';
1324
+ $file_url = fvm_get_protocol($cachedirurl.'/'.$hash.'-'.$ctime.'.min.css');
1325
+
1326
+ # generate a new cache file
1327
+ if (!file_exists($file)) {
1328
+
1329
+ # code and log initialization
1330
+ $log = '';
1331
+ $code = '';
1332
+
1333
+ # minify and write to file
1334
+ foreach($header[$i]['handles'] as $handle) :
1335
+ if(!empty($wp_styles->registered[$handle]->src)) {
1336
+
1337
+ # get hurl per handle
1338
+ $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
1339
+ $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
1340
+
1341
+ # get css from hurl, if available and valid
1342
+ $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
1343
+ $newcode = false; $newcode = get_transient($tkey);
1344
+ if ( $newcode === false) {
1345
+ $res = fvm_download_and_cache($hurl, $tkey, null, $disable_css_minification, 'css');
1346
+ if(is_array($res)) {
1347
+ $newcode = $res['code'];
1348
+ $newlog = $res['log'];
1349
+ }
1350
+ } else {
1351
+ $newlog = "$printurl --- Debug: Fetched from transients cache with key $tkey ---\n";
1352
+ }
1353
+
1354
+ # append
1355
+ if($newcode !== false) {
1356
+ $code.= $newcode;
1357
+ if(isset($inline_css[$handle]) && !empty($inline_css[$handle])) { $code.= $inline_css[$handle]; } # add inline css on the fly
1358
+ $log.= $newlog;
1359
+ }
1360
+
1361
+ # consider dependencies on handles with an empty src
1362
+ } else {
1363
+ wp_dequeue_script($handle); wp_enqueue_script($handle);
1364
+ }
1365
+ endforeach;
1366
+
1367
+ # prepare log
1368
+ $log = "PROCESSED on ".date('r')."\n".$log."PROCESSED from ".home_url(add_query_arg( NULL, NULL ))."\n";
1369
+
1370
+ # generate cache, write log
1371
+ file_put_contents($file.'.txt', $log);
1372
+ file_put_contents($file, $code);
1373
+ file_put_contents($file.'.gz', gzencode(file_get_contents($file), 9));
1374
+
1375
+ }
1376
+
1377
+ # register and enqueue minified file, consider excluding of mediatype "print" and inline css
1378
+ if ($remove_print_mediatypes != 1 || ($remove_print_mediatypes == 1 && $header[$i]['media'] != 'print')) {
1379
+ if($force_inline_css != false) {
1380
+ echo '<style type="text/css" media="'.$header[$i]['media'].'">'.file_get_contents($file).'</style>';
1381
+ } else {
1382
+
1383
+ # move CSS to footer ?
1384
+ if($send_css_to_footer != false) {
1385
+ if($critical_css_tester != true) {
1386
+ echo '<link rel="preload" href="'.$file_url.'" as="style" media="'.$header[$i]['media'].'" onload="this.rel=\'stylesheet\'">';
1387
+ echo '<noscript><link rel="stylesheet" type="text/css" media="'.$header[$i]['media'].'" href="'.$file_url.'"></noscript>';
1388
+ echo '<!--[if IE]><link rel="stylesheet" type="text/css" media="'.$header[$i]['media'].'" href="'.$file_url.'"><![endif]-->';
1389
+ }
1390
+ } else {
1391
+
1392
+ # default
1393
+ wp_register_style("fvm-header-$i", $file_url, array(), null, $header[$i]['media']);
1394
+ wp_enqueue_style("fvm-header-$i");
1395
+
1396
+ }
1397
+ }
1398
+ }
1399
+
1400
+ # other css need to be requeued for the order of files to be kept
1401
+ } else {
1402
+ wp_dequeue_style($header[$i]['handle']); wp_enqueue_style($header[$i]['handle']);
1403
+ }
1404
+ }
1405
+
1406
+ # remove from queue
1407
+ $wp_styles->done = $done;
1408
+
1409
+ }
1410
+ ###########################################
1411
+
1412
+
1413
+ ###########################################
1414
+ # process css in the footer ###############
1415
+ ###########################################
1416
+ function fastvelocity_min_merge_footer_css() {
1417
+ global $wp_styles, $wp_domain, $wp_home, $wp_home_path, $cachedir, $cachedirurl, $ignore, $disable_css_merge, $disable_css_minification, $skip_google_fonts, $skip_cssorder, $remove_print_mediatypes, $force_inline_css_footer, $force_inline_googlefonts, $send_css_to_footer, $critical_css_tester;
1418
+ if(!is_object($wp_styles)) { return false; }
1419
+ $ctime = get_option('fvm-last-cache-update', '0');
1420
+ $styles = wp_clone($wp_styles);
1421
+ $styles->all_deps($styles->queue);
1422
+ $done = $styles->done;
1423
+ $footer = array();
1424
+ $google_fonts = array();
1425
+ $inline_css = array();
1426
+
1427
+ # add defaults to ignore list
1428
+ $ignore = fastvelocity_default_ignore($ignore);
1429
+
1430
+
1431
+ # google fonts to the top
1432
+ foreach( $styles->to_do as $handle ) :
1433
+ # dequeue and get a list of google fonts, or requeue external
1434
+ $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
1435
+ if (stripos($hurl, 'fonts.googleapis.com') !== false) {
1436
+ wp_dequeue_style($handle);
1437
+ if(!$skip_google_fonts) { $google_fonts[$handle] = $hurl; } else { wp_enqueue_style($handle); } # skip google fonts optimization?
1438
+ } else {
1439
+ wp_dequeue_style($handle); wp_enqueue_style($handle); # failsafe
1440
+ }
1441
+ endforeach;
1442
+
1443
+
1444
+ # concat google fonts, if enabled
1445
+ if(!$skip_google_fonts && count($google_fonts) > 0) {
1446
+ $concat_google_fonts = fastvelocity_min_concatenate_google_fonts($google_fonts);
1447
+ foreach ($google_fonts as $h=>$a) { $done = array_merge($done, array($h)); } # mark as done
1448
+ if($force_inline_googlefonts == false) {
1449
+ wp_enqueue_style('footer-fvm-fonts', fvm_get_protocol($concat_google_fonts), array(), null, 'all');
1450
+ } else {
1451
+
1452
+ # google fonts download and inlining, ignore logs
1453
+ $tkey = 'fvm-cache-'.$ctime.hash('adler32', $concat_google_fonts);
1454
+ $newcode = false; $newcode = get_transient($tkey);
1455
+ if ( $newcode === false) {
1456
+ $res = fvm_download_and_cache($concat_google_fonts, $tkey, null, $disable_css_minification, 'css');
1457
+ if(is_array($res)) { $newcode = $res['code']; }
1458
+ }
1459
+
1460
+ # inline css or fail
1461
+ if($newcode !== false) {
1462
+ echo '<style type="text/css" media="all">'.$newcode.'</style>';
1463
+ } else {
1464
+ echo "<!-- GOOGLE FONTS REQUEST FAILED for $concat_google_fonts -->\n"; # log if failed
1465
+ }
1466
+ }
1467
+ }
1468
+
1469
+
1470
+ # get groups of handles
1471
+ foreach( $styles->to_do as $handle ) :
1472
+
1473
+ # skip already processed google fonts
1474
+ if(isset($google_fonts[$handle])) { continue; }
1475
+
1476
+ # get full url
1477
+ $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
1478
+
1479
+ # media type
1480
+ $media = isset($wp_styles->registered[$handle]->args) ? $wp_styles->registered[$handle]->args : 'all';
1481
+
1482
+ # IE only files don't increment things
1483
+ $ieonly = fastvelocity_ie_blacklist($hurl);
1484
+ if($ieonly == true) { continue; }
1485
+
1486
+ # conditionals
1487
+ $conditional = NULL; if(isset($wp_styles->registered[$handle]->extra["conditional"])) {
1488
+ $conditional = $wp_styles->registered[$handle]->extra["conditional"]; # such as ie7, ie8, ie9, etc
1489
+ }
1490
+
1491
+ # skip ignore list, conditional css, external css
1492
+ if ((!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($conditional) && fvm_internal_url($hurl, $wp_home)) || empty($hurl)) {
1493
+
1494
+ # colect inline css for this handle
1495
+ if(isset($wp_styles->registered[$handle]->extra['after']) && is_array($wp_styles->registered[$handle]->extra['after'])) {
1496
+ $inline_css[$handle] = fastvelocity_min_minify_css_string(implode('', $wp_styles->registered[$handle]->extra['after'])); # save
1497
+ $wp_styles->registered[$handle]->extra['after'] = null; # dequeue
1498
+ }
1499
+
1500
+ # process
1501
+ if(isset($footer[count($footer)-1]['handle']) || count($footer) == 0 || $footer[count($footer)-1]['media'] != $wp_styles->registered[$handle]->args) {
1502
+ array_push($footer, array('handles'=>array(),'media'=>$media));
1503
+ }
1504
+
1505
+ # push it to the array get latest modified time
1506
+ array_push($footer[count($footer)-1]['handles'], $handle);
1507
+
1508
+ # collect generated file urls for gulp, except print
1509
+ if ($media != 'print') { $GLOBALS['used_css_files'][] = $hurl; }
1510
+
1511
+ # external and ignored css
1512
+ } else {
1513
+
1514
+ # collect generated file urls for gulp, except print and conditionals
1515
+ if ($media != 'print' && !isset($conditional)) { $GLOBALS['used_css_files'][] = $hurl; }
1516
+
1517
+ # normal enqueueing
1518
+ array_push($footer, array('handle'=>$handle));
1519
+ }
1520
+ endforeach;
1521
+
1522
+
1523
+ # reorder CSS by mediatypes
1524
+ if(!$skip_cssorder) {
1525
+ if(count($footer) > 0) {
1526
+
1527
+ # get unique mediatypes
1528
+ $allmedia = array();
1529
+ foreach($footer as $key=>$array) {
1530
+ if(isset($array['media'])) { $allmedia[$array['media']] = ''; }
1531
+ }
1532
+
1533
+ # extract handles by mediatype
1534
+ $grouphandles = array();
1535
+ foreach ($allmedia as $md=>$var) {
1536
+ foreach($footer as $array) {
1537
+ if (isset($array['media']) && $array['media'] == $md) {
1538
+ foreach($array['handles'] as $h) { $grouphandles[$md][] = $h; }
1539
+ }
1540
+ }
1541
+ }
1542
+
1543
+ # reset and reorder footer by mediatypes
1544
+ $newfooter = array();
1545
+ foreach ($allmedia as $md=>$var) { $newfooter[] = array('handles' => $grouphandles[$md], 'media'=>$md); }
1546
+ if(count($newfooter) > 0) { $footer = $newfooter; }
1547
+ }
1548
+ }
1549
+
1550
+ # loop through footer css and merge
1551
+ for($i=0,$l=count($footer);$i<$l;$i++) {
1552
+ if(!isset($footer[$i]['handle'])) {
1553
+
1554
+ # get has for the inline css in this group
1555
+ $inline_css_group = array();
1556
+ foreach($footer[$i]['handles'] as $h) { if(isset($inline_css[$h]) && !empty($inline_css[$h])) { $inline_css_group[] = $inline_css[$h]; } }
1557
+ $inline_css_hash = md5(implode('',$inline_css_group));
1558
+
1559
+ # static cache file info + done
1560
+ $done = array_merge($done, $footer[$i]['handles']);
1561
+ $hash = 'footer-'.hash('adler32',implode('',$footer[$i]['handles']).$inline_css_hash);
1562
+
1563
+ # create cache files and urls
1564
+ $file = $cachedir.'/'.$hash.'-'.$ctime.'.min.css';
1565
+ $file_url = fvm_get_protocol($cachedirurl.'/'.$hash.'-'.$ctime.'.min.css');
1566
+
1567
+ # generate a new cache file
1568
+ if (!file_exists($file)) {
1569
+
1570
+ # code and log initialization
1571
+ $log = '';
1572
+ $code = '';
1573
+
1574
+ # minify and write to file
1575
+ foreach($footer[$i]['handles'] as $handle) :
1576
+ if(!empty($wp_styles->registered[$handle]->src)) {
1577
+
1578
+ # get hurl per handle
1579
+ $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
1580
+ $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
1581
+
1582
+ # get css from hurl, if available and valid
1583
+ $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
1584
+ $newcode = false; $newcode = get_transient($tkey);
1585
+ if ( $newcode === false) {
1586
+ $res = fvm_download_and_cache($hurl, $tkey, null, $disable_css_minification, 'css');
1587
+ if(is_array($res)) {
1588
+ $newcode = $res['code'];
1589
+ $newlog = $res['log'];
1590
+ }
1591
+ } else {
1592
+ $newlog = "$printurl --- Debug: Fetched from transients cache with key $tkey ---\n";
1593
+ }
1594
+
1595
+ # append
1596
+ if($newcode !== false) {
1597
+ $code.= $newcode;
1598
+ if(isset($inline_css[$handle]) && !empty($inline_css[$handle])) { $code.= $inline_css[$handle]; } # add inline css on the fly
1599
+ $log.= $newlog;
1600
+ }
1601
+
1602
+ # consider dependencies on handles with an empty src
1603
+ } else {
1604
+ wp_dequeue_script($handle); wp_enqueue_script($handle);
1605
+ }
1606
+ endforeach;
1607
+
1608
+ # prepare log
1609
+ $log = "PROCESSED on ".date('r')."\n".$log."PROCESSED from ".home_url(add_query_arg( NULL, NULL ))."\n";
1610
+
1611
+ # generate cache, add inline css, write log
1612
+ file_put_contents($file.'.txt', $log);
1613
+ file_put_contents($file, $code); # preserve style tags
1614
+ file_put_contents($file.'.gz', gzencode(file_get_contents($file), 9));
1615
+
1616
+ }
1617
+
1618
+ # register and enqueue minified file, consider excluding of mediatype "print" and inline css
1619
+ if ($remove_print_mediatypes != 1 || ($remove_print_mediatypes == 1 && $footer[$i]['media'] != 'print')) {
1620
+ if($force_inline_css_footer != false) {
1621
+ echo '<style type="text/css" media="'.$footer[$i]['media'].'">'.file_get_contents($file).'</style>';
1622
+ } else {
1623
+
1624
+ # move CSS to footer
1625
+ if($send_css_to_footer != false) {
1626
+
1627
+ if($critical_css_tester != true) {
1628
+ echo '<link rel="preload" href="'.$file_url.'" as="style" media="'.$footer[$i]['media'].'" onload="this.rel=\'stylesheet\'">';
1629
+ echo '<noscript><link rel="stylesheet" type="text/css" media="'.$footer[$i]['media'].'" href="'.$file_url.'"></noscript>';
1630
+ echo '<!--[if IE]><link rel="stylesheet" type="text/css" media="'.$footer[$i]['media'].'" href="'.$file_url.'"><![endif]-->';
1631
+ }
1632
+
1633
+ } else {
1634
+
1635
+ # default
1636
+ wp_register_style("fvm-footer-$i", $file_url, array(), null, $footer[$i]['media']);
1637
+ wp_enqueue_style("fvm-footer-$i");
1638
+
1639
+ }
1640
+ }
1641
+ }
1642
+
1643
+ # other css need to be requeued for the order of files to be kept
1644
+ } else {
1645
+ wp_dequeue_style($footer[$i]['handle']); wp_enqueue_style($footer[$i]['handle']);
1646
+ }
1647
+ }
1648
+
1649
+ # remove from queue
1650
+ $wp_styles->done = $done;
1651
+ }
1652
+ ###########################################
1653
+
1654
+
1655
+
1656
+
1657
+ ###########################################
1658
+ # defer CSS globally from the header (order matters)
1659
+ ###########################################
1660
+ function fvm_movecss_to_footer_header() {
1661
+ global $send_css_to_footer;
1662
+ if($send_css_to_footer != false) {
1663
+
1664
+ # echo LoadCSS scripts
1665
+ echo '<script>
1666
+ /*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
1667
+ !function(a){"use strict";var b=function(b,c,d){function e(a){return h.body?a():void setTimeout(function(){e(a)})}function f(){i.addEventListener&&i.removeEventListener("load",f),i.media=d||"all"}var g,h=a.document,i=h.createElement("link");if(c)g=c;else{var j=(h.body||h.getElementsByTagName("head")[0]).childNodes;g=j[j.length-1]}var k=h.styleSheets;i.rel="stylesheet",i.href=b,i.media="only x",e(function(){g.parentNode.insertBefore(i,c?g:g.nextSibling)});var l=function(a){for(var b=i.href,c=k.length;c--;)if(k[c].href===b)return a();setTimeout(function(){l(a)})};return i.addEventListener&&i.addEventListener("load",f),i.onloadcssdefined=l,l(f),i};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b}("undefined"!=typeof global?global:this);
1668
+ /*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
1669
+ !function(a){if(a.loadCSS){var b=loadCSS.relpreload={};if(b.support=function(){try{return a.document.createElement("link").relList.supports("preload")}catch(b){return!1}},b.poly=function(){for(var b=a.document.getElementsByTagName("link"),c=0;c<b.length;c++){var d=b[c];"preload"===d.rel&&"style"===d.getAttribute("as")&&(a.loadCSS(d.href,d,d.getAttribute("media")),d.rel=null)}},!b.support()){b.poly();var c=a.setInterval(b.poly,300);a.addEventListener&&a.addEventListener("load",function(){b.poly(),a.clearInterval(c)}),a.attachEvent&&a.attachEvent("onload",function(){a.clearInterval(c)})}}}(this);
1670
+ </script>';
1671
+
1672
+ } }
1673
+
1674
+ if (!is_admin()) { add_action('wp_head', 'fvm_movecss_to_footer_header', PHP_INT_MAX); }
1675
+ ###########################################
1676
+
1677
+
1678
+
1679
+ ###########################################
1680
+ # generate gulp-uncss files for this page (dev only)
1681
+ ###########################################
1682
+ function fvm_generate_gulp_uncss() {
1683
+ global $wp, $generate_gulp_files, $generate_gulp_path;
1684
+
1685
+ if(isset($GLOBALS['used_css_files']) && $generate_gulp_files != false && !empty($generate_gulp_path)) {
1686
+
1687
+ # some paths and urls
1688
+ $currenturl = fvm_get_protocol($_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]);
1689
+ $file = sanitize_title($currenturl);
1690
+ $save = get_stylesheet_directory().'/criticalcss/css/'.$file.'.css';
1691
+ $generate_gulp_path = rtrim(trim($generate_gulp_path), '/');
1692
+ $generate_gulp_path_out = $generate_gulp_path.'/out/';
1693
+ if(!is_dir($generate_gulp_path_out) && is_writable($generate_gulp_path_out)) { mkdir($generate_gulp_path_out); }
1694
+ $gulpfile = "$generate_gulp_path/$file.js";
1695
+
1696
+ # process
1697
+ if(!file_exists($save) && !file_exists($gulpfile) && is_dir($generate_gulp_path_out)) {
1698
+
1699
+ $cssfiles = json_encode($GLOBALS['used_css_files'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
1700
+
1701
+ # gulp tasks
1702
+ $gulptask = <<<EOF
1703
+ var gulp = require('gulp');
1704
+ var uncss = require('gulp-uncss');
1705
+ var concat = require('gulp-concat');
1706
+ var nano = require('gulp-cssnano');
1707
+ var remoteSrc = require('gulp-remote-src');
1708
+ gulp.task('uncss', function() {
1709
+ remoteSrc($cssfiles, { base: null })
1710
+ .pipe(concat('$file.css'))
1711
+ .pipe(uncss({ html: ['$currenturl'] }))
1712
+ .pipe(nano())
1713
+ .pipe(gulp.dest('$generate_gulp_path/out/'));
1714
+ });
1715
+ EOF;
1716
+
1717
+ # write gulp file
1718
+ file_put_contents($gulpfile, $gulptask);
1719
+
1720
+ }
1721
+ }
1722
+ }
1723
+
1724
+ if (!is_admin()) { add_action('wp_footer', 'fvm_generate_gulp_uncss', PHP_INT_MAX); }
1725
+
1726
+
1727
+
1728
+ ###########################################
1729
+ # add preconnect and preload headers
1730
+ ###########################################
1731
+ function fvm_buffer_placeholder_top() {
1732
+ global $preload, $preconnect;
1733
+
1734
+ # defaults
1735
+ $meta = array();
1736
+
1737
+ # preconnect resources
1738
+ # https://css-tricks.com/prefetching-preloading-prebrowsing/
1739
+ if(count($preconnect) > 0) {
1740
+ $a = array(); foreach ($preconnect as $b) {
1741
+ if(!empty($b)) { $a[] = '<link href="'.str_ireplace(array('http://', 'https://'), '//', $b).'" rel="preconnect">'; }
1742
+ }
1743
+ if(count($a) > 0) { $meta[] = implode('', $a); }
1744
+ }
1745
+
1746
+ # preload resources (beta: homepage only)
1747
+ # https://css-tricks.com/prefetching-preloading-prebrowsing/
1748
+ if((is_home() || is_front_page()) && count($preload) > 0) {
1749
+ $a = array();
1750
+ foreach ($preload as $b) {
1751
+ if(!empty($b)) {
1752
+ $meta[] = '<link rel="preload" as="image" href="'.str_ireplace(array('http://', 'https://'), '//', $b).'">';
1753
+ }
1754
+ }
1755
+ }
1756
+
1757
+ # output on top
1758
+ echo implode('', $meta);
1759
+ }
1760
+
1761
+ # remove query from static assets and process defering (if enabled)
1762
+ if (!is_admin()) {
1763
+ add_filter('script_loader_tag', 'fastvelocity_min_defer_js', 10, 3);
1764
+ add_filter('style_loader_src', 'fastvelocity_remove_cssjs_ver', 10, 2);
1765
+ }
1766
+
1767
+ # enable html minification
1768
+ if(!$skip_html_minification && !is_admin()) {
1769
+ add_action('get_header', 'fastvelocity_min_html_compression_start', PHP_INT_MAX);
1770
+ }
1771
+
inc/functions-admin.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ in development...
5
+ */
6
+
7
+ # add admin toolbar
8
+ function fastvelocity_load_toolbar() {
9
+ if(current_user_can('manage_options')) { add_action( 'admin_bar_menu', 'fastvelocity_add_toolbar', 100 ); }
10
+ }
11
+
12
+ # admin toolbar processing
13
+ function fastvelocity_add_toolbar() {
14
+ global $wp_admin_bar;
15
+
16
+ # get cache size
17
+ $stats = fastvelocity_get_cachestats();
18
+ $count = $stats['count'];
19
+ $size = $stats['size'];
20
+
21
+ # Create or add new items into the Admin Toolbar.
22
+ $wp_admin_bar->add_node( array(
23
+ 'id' => 'fvm',
24
+ 'title' => '<span class="ab-icon"></span><span class="ab-label">' . __("Fast Velocity",'fvm') . '</span>',
25
+ 'href' => admin_url( 'options-general.php?page=fastvelocity-min' ),
26
+ 'meta' => array( 'class' => 'bullet-green')
27
+ ));
28
+
29
+ # Cache Info node
30
+ $wp_admin_bar->add_node( array(
31
+ 'id' => 'fvm-cache-info',
32
+ 'title' => '<p class="bold">' . __( "Cache Info", 'fvm' ) . '</p>' .
33
+ '<table>' .
34
+ '<tr><td>' . __( "Size", 'fvm' ) . ':</td><td class="size green">' . $size . '</td></tr>' .
35
+ '<tr><td>' . __( "Files", 'fvm' ) . ':</td><td class="files white">' . $count . '</td></tr>' .
36
+ '</table>',
37
+ 'parent'=> 'fvm'
38
+ ));
39
+
40
+ # Delete Cache node
41
+ $wp_admin_bar->add_node( array(
42
+ 'id' => 'fvm-delete-cache',
43
+ 'title' => __("Purge Cache",'fvm'),
44
+ 'parent'=> 'fvm'
45
+ ));
46
+
47
+ }
48
+
49
+
50
+
51
+ # get cache size and count
52
+ function fastvelocity_get_cachestats() {
53
+
54
+ # info
55
+ $cachepath = fvm_cachepath();
56
+ $tmpdir = $cachepath['tmpdir'];
57
+ $cachedir = $cachepath['cachedir'];
58
+ $search = array($tmpdir, $cachedir);
59
+ $size = 0;
60
+ $count = 0;
61
+
62
+ foreach ($search as $dir) {
63
+ if(is_dir(rtrim($dir, '/'))) {
64
+ if ($handle = opendir($dir.'/')) {
65
+ while (false !== ($file = readdir($handle))) { $f = $dir.'/'.$file; $size = $size + filesize($f); $count++; }
66
+ closedir($handle);
67
+ }
68
+ }
69
+ }
70
+
71
+ return array('size'=>fastvelocity_format_filesize($size), 'count'=>$count);
72
+ }
73
+
74
+
inc/functions-serverinfo.php ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+
5
+ ### Get General Information
6
+ function fvm_get_generalinfo() {
7
+ global $is_IIS;
8
+ if( is_rtl() ) : ?>
9
+ <style type="text/css">
10
+ #GeneralOverview table,
11
+ #GeneralOverview th,
12
+ #GeneralOverview td {
13
+ direction: ltr;
14
+ text-align: left;
15
+ }
16
+ #GeneralOverview h2 {
17
+ padding: 0.5em 0 0;
18
+ }
19
+ </style>
20
+ <?php endif;
21
+ ?>
22
+ <div class="wrap" id="GeneralOverview">
23
+ <h2><?php _e('Server Info','fvm-serverinfo'); ?></h2>
24
+ <br />
25
+ <table class="widefat">
26
+ <thead>
27
+ <tr>
28
+ <th><?php _e('Variable Name', 'fvm-serverinfo'); ?></th>
29
+ <th><?php _e('Value', 'fvm-serverinfo'); ?></th>
30
+ <th><?php _e('Variable Name', 'fvm-serverinfo'); ?></th>
31
+ <th><?php _e('Value', 'fvm-serverinfo'); ?></th>
32
+ </tr>
33
+ </thead>
34
+ <tbody>
35
+ <tr>
36
+ <td><?php _e('OS', 'fvm-serverinfo'); ?></td>
37
+ <td><?php echo PHP_OS; ?></td>
38
+ <td><?php _e('Database Data Disk Usage', 'fvm-serverinfo'); ?></td>
39
+ <td><?php echo fvm_format_filesize(fvm_get_mysql_data_usage()); ?></td>
40
+ </tr>
41
+ <tr class="alternate">
42
+ <td><?php _e('Server', 'fvm-serverinfo'); ?></td>
43
+ <td><?php echo $_SERVER["SERVER_SOFTWARE"]; ?></td>
44
+ <td><?php _e('Database Index Disk Usage', 'fvm-serverinfo'); ?></td>
45
+ <td><?php echo fvm_format_filesize(fvm_get_mysql_index_usage()); ?></td>
46
+ </tr>
47
+ <tr>
48
+ <td>PHP</td>
49
+ <td>v<?php echo PHP_VERSION; ?></td>
50
+ <td><?php _e('MYSQL Maximum Packet Size', 'fvm-serverinfo'); ?></td>
51
+ <td><?php echo fvm_format_filesize(fvm_get_mysql_max_allowed_packet()); ?></td>
52
+ </tr>
53
+ <tr class="alternate">
54
+ <td>MYSQL</td>
55
+ <td>v<?php echo fvm_get_mysql_version(); ?></td>
56
+ <td><?php _e('MYSQL Maximum No. Connection', 'fvm-serverinfo'); ?></td>
57
+ <td><?php echo number_format_i18n(fvm_get_mysql_max_allowed_connections()); ?></td>
58
+ </tr>
59
+ <tr>
60
+ <td><?php _e('Server Load', 'fvm-serverinfo'); ?></td>
61
+ <td><?php echo fvm_get_serverload(); ?></td>
62
+ <td><?php _e( 'MYSQL Query Cache Size', 'fvm-serverinfo' ); ?></td>
63
+ <td><?php echo fvm_format_filesize( fvm_get_mysql_query_cache_size() ); ?></td>
64
+ </tr>
65
+ <tr class="alternate">
66
+ <td><?php _e("Server CPU's", 'fvm-serverinfo'); ?></td>
67
+ <td><?php echo fvm_get_servercpu(); ?></td>
68
+ <td><?php _e('PHP Short Tag', 'fvm-serverinfo'); ?></td>
69
+ <td><?php echo fvm_get_php_short_tag(); ?></td>
70
+ </tr>
71
+ <tr>
72
+ <td><?php _e('Server Hostname', 'fvm-serverinfo'); ?></td>
73
+ <td><?php echo $_SERVER['SERVER_NAME']; ?></td>
74
+ <td><?php _e('PHP Magic Quotes GPC', 'fvm-serverinfo'); ?></td>
75
+ <td><?php echo fvm_get_php_magic_quotes_gpc(); ?></td>
76
+ </tr>
77
+ <tr class="alternate">
78
+ <td><?php _e('Server Document Root','fvm-serverinfo'); ?></td>
79
+ <td><?php echo $_SERVER['DOCUMENT_ROOT']; ?></td>
80
+ <td><?php _e('PHP Memory Limit', 'fvm-serverinfo'); ?></td>
81
+ <td><?php echo fvm_format_php_size(fvm_get_php_memory_limit()); ?></td>
82
+ </tr>
83
+ <tr>
84
+ <td><?php _e('Site Url', 'fvm-serverinfo'); ?></td>
85
+ <td><?php echo site_url(); ?></td>
86
+ <td><?php _e('PHP Max Upload Size', 'fvm-serverinfo'); ?></td>
87
+ <td><?php echo fvm_format_php_size(fvm_get_php_upload_max()); ?></td>
88
+ </tr>
89
+ <tr class="alternate">
90
+ <td><?php _e('Home Url', 'fvm-serverinfo'); ?></td>
91
+ <td><?php echo home_url(); ?></td>
92
+ <td><?php _e('PHP Max Post Size', 'fvm-serverinfo'); ?></td>
93
+ <td><?php echo fvm_format_php_size(fvm_get_php_post_max()); ?></td>
94
+ </tr>
95
+ <tr>
96
+ <td><?php _e('MySQL Date/Time', 'fvm-serverinfo'); ?></td>
97
+ <td><?php echo mysql2date(sprintf(__('%s @ %s', 'wp-postratings'), get_option('date_format'), get_option('time_format')), current_time('mysql', 1)). ' GMT'; ?></td>
98
+ <td><?php _e('PHP Max Script Execute Time', 'fvm-serverinfo'); ?></td>
99
+ <td><?php echo fvm_get_php_max_execution(); ?>s</td>
100
+ </tr>
101
+ </tbody>
102
+ </table>
103
+ </div>
104
+ <?php
105
+ }
106
+
107
+
108
+ ### Function: Format Bytes Into TiB/GiB/MiB/KiB/Bytes
109
+ if(!function_exists('fvm_format_filesize')) {
110
+ function fvm_format_filesize($rawSize) {
111
+ if($rawSize / 1099511627776 > 1) {
112
+ return number_format_i18n($rawSize/1099511627776, 1).' '.__('TiB', 'fvm-serverinfo');
113
+ } elseif($rawSize / 1073741824 > 1) {
114
+ return number_format_i18n($rawSize/1073741824, 1).' '.__('GiB', 'fvm-serverinfo');
115
+ } elseif($rawSize / 1048576 > 1) {
116
+ return number_format_i18n($rawSize/1048576, 1).' '.__('MiB', 'fvm-serverinfo');
117
+ } elseif($rawSize / 1024 > 1) {
118
+ return number_format_i18n($rawSize/1024, 1).' '.__('KiB', 'fvm-serverinfo');
119
+ } elseif($rawSize > 1) {
120
+ return number_format_i18n($rawSize, 0).' '.__('bytes', 'fvm-serverinfo');
121
+ } else {
122
+ return __('unknown', 'fvm-serverinfo');
123
+ }
124
+ }
125
+ }
126
+
127
+ ### Function: Convert PHP Size Format to Localized
128
+ function fvm_format_php_size($size) {
129
+ if (!is_numeric($size)) {
130
+ if (strpos($size, 'M') !== false) {
131
+ $size = intval($size)*1024*1024;
132
+ } elseif (strpos($size, 'K') !== false) {
133
+ $size = intval($size)*1024;
134
+ } elseif (strpos($size, 'G') !== false) {
135
+ $size = intval($size)*1024*1024*1024;
136
+ }
137
+ }
138
+ return is_numeric($size) ? fvm_format_filesize($size) : $size;
139
+ }
140
+
141
+ ### Function: Get PHP Short Tag
142
+ if(!function_exists('fvm_get_php_short_tag')) {
143
+ function fvm_get_php_short_tag() {
144
+ if(ini_get('short_open_tag')) {
145
+ $short_tag = __('On', 'fvm-serverinfo');
146
+ } else {
147
+ $short_tag = __('Off', 'fvm-serverinfo');
148
+ }
149
+ return $short_tag;
150
+ }
151
+ }
152
+
153
+
154
+ ### Function: Get PHP Magic Quotes GPC
155
+ if(!function_exists('fvm_get_php_magic_quotes_gpc')) {
156
+ function fvm_get_php_magic_quotes_gpc() {
157
+ if(get_magic_quotes_gpc()) {
158
+ $magic_quotes_gpc = __('On', 'fvm-serverinfo');
159
+ } else {
160
+ $magic_quotes_gpc = __('Off', 'fvm-serverinfo');
161
+ }
162
+ return $magic_quotes_gpc;
163
+ }
164
+ }
165
+
166
+
167
+ ### Function: Get PHP Max Upload Size
168
+ if(!function_exists('fvm_get_php_upload_max')) {
169
+ function fvm_get_php_upload_max() {
170
+ if(ini_get('upload_max_filesize')) {
171
+ $upload_max = ini_get('upload_max_filesize');
172
+ } else {
173
+ $upload_max = __('N/A', 'fvm-serverinfo');
174
+ }
175
+ return $upload_max;
176
+ }
177
+ }
178
+
179
+
180
+ ### Function: Get PHP Max Post Size
181
+ if(!function_exists('fvm_get_php_post_max')) {
182
+ function fvm_get_php_post_max() {
183
+ if(ini_get('post_max_size')) {
184
+ $post_max = ini_get('post_max_size');
185
+ } else {
186
+ $post_max = __('N/A', 'fvm-serverinfo');
187
+ }
188
+ return $post_max;
189
+ }
190
+ }
191
+
192
+
193
+ ### Function: PHP Maximum Execution Time
194
+ if(!function_exists('fvm_get_php_max_execution')) {
195
+ function fvm_get_php_max_execution() {
196
+ if(ini_get('max_execution_time')) {
197
+ $max_execute = ini_get('max_execution_time');
198
+ } else {
199
+ $max_execute = __('N/A', 'fvm-serverinfo');
200
+ }
201
+ return $max_execute;
202
+ }
203
+ }
204
+
205
+
206
+ ### Function: PHP Memory Limit
207
+ if(!function_exists('fvm_get_php_memory_limit')) {
208
+ function fvm_get_php_memory_limit() {
209
+ if(ini_get('memory_limit')) {
210
+ $memory_limit = ini_get('memory_limit');
211
+ } else {
212
+ $memory_limit = __('N/A', 'fvm-serverinfo');
213
+ }
214
+ return $memory_limit;
215
+ }
216
+ }
217
+
218
+
219
+ ### Function: Get MYSQL Version
220
+ if(!function_exists('fvm_get_mysql_version')) {
221
+ function fvm_get_mysql_version() {
222
+ global $wpdb;
223
+ return $wpdb->get_var("SELECT VERSION() AS version");
224
+ }
225
+ }
226
+
227
+
228
+ ### Function: Get MYSQL Data Usage
229
+ if(!function_exists('fvm_get_mysql_data_usage')) {
230
+ function fvm_get_mysql_data_usage() {
231
+ global $wpdb;
232
+ $data_usage = '';
233
+ $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
234
+ foreach($tablesstatus as $tablestatus) {
235
+ $data_usage += $tablestatus->Data_length;
236
+ }
237
+ if (!$data_usage) {
238
+ $data_usage = __('N/A', 'fvm-serverinfo');
239
+ }
240
+ return $data_usage;
241
+ }
242
+ }
243
+
244
+
245
+ ### Function: Get MYSQL Index Usage
246
+ if(!function_exists('fvm_get_mysql_index_usage')) {
247
+ function fvm_get_mysql_index_usage() {
248
+ global $wpdb;
249
+ $index_usage = '';
250
+ $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
251
+ foreach($tablesstatus as $tablestatus) {
252
+ $index_usage += $tablestatus->Index_length;
253
+ }
254
+ if (!$index_usage){
255
+ $index_usage = __('N/A', 'fvm-serverinfo');
256
+ }
257
+ return $index_usage;
258
+ }
259
+ }
260
+
261
+
262
+ ### Function: Get MYSQL Max Allowed Packet
263
+ if(!function_exists('fvm_get_mysql_max_allowed_packet')) {
264
+ function fvm_get_mysql_max_allowed_packet() {
265
+ global $wpdb;
266
+ $packet_max_query = $wpdb->get_row("SHOW VARIABLES LIKE 'max_allowed_packet'");
267
+ $packet_max = $packet_max_query->Value;
268
+ if(!$packet_max) {
269
+ $packet_max = __('N/A', 'fvm-serverinfo');
270
+ }
271
+ return $packet_max;
272
+ }
273
+ }
274
+
275
+
276
+ ### Function:Get MYSQL Max Allowed Connections
277
+ if(!function_exists('fvm_get_mysql_max_allowed_connections')) {
278
+ function fvm_get_mysql_max_allowed_connections() {
279
+ global $wpdb;
280
+ $connection_max_query = $wpdb->get_row("SHOW VARIABLES LIKE 'max_connections'");
281
+ $connection_max = $connection_max_query->Value;
282
+ if(!$connection_max) {
283
+ $connection_max = __('N/A', 'fvm-serverinfo');
284
+ }
285
+ return $connection_max;
286
+ }
287
+ }
288
+
289
+ ### Function:Get MYSQL Query Cache Size
290
+ if(!function_exists('fvm_get_mysql_query_cache_size')) {
291
+ function fvm_get_mysql_query_cache_size() {
292
+ global $wpdb;
293
+ $query_cache_size_query = $wpdb->get_row( "SHOW VARIABLES LIKE 'query_cache_size'" );
294
+ $query_cache_size = $query_cache_size_query->Value;
295
+ if ( empty( $query_cache_size ) ) {
296
+ $query_cache_size = __( 'N/A', 'fvm-serverinfo' );
297
+ }
298
+ return $query_cache_size;
299
+ }
300
+ }
301
+
302
+
303
+ ### Function: Get The Server Load
304
+ if(!function_exists('fvm_get_serverload')) {
305
+ function fvm_get_serverload() {
306
+ $server_load = '';
307
+ $numCpus = 'N/A';
308
+ if(PHP_OS != 'WINNT' && PHP_OS != 'WIN32') {
309
+ if(@file_exists('/proc/loadavg') ) {
310
+ if ($fh = @fopen( '/proc/loadavg', 'r' )) {
311
+ $data = @fread( $fh, 6 );
312
+ @fclose( $fh );
313
+ $load_avg = explode( " ", $data );
314
+ $server_load = trim($load_avg[0]);
315
+ }
316
+ } else if ('WIN' == strtoupper(substr(PHP_OS, 0, 3))) {
317
+ $process = @popen('wmic cpu get NumberOfCores', 'rb');
318
+ if (false !== $process && null !== $process) {
319
+ fgets($process);
320
+ $numCpus = intval(fgets($process));
321
+ pclose($process);
322
+ }
323
+ } else {
324
+ $data = @system('uptime');
325
+ preg_match('/(.*):{1}(.*)/', $data, $matches);
326
+ $load_arr = explode(',', $matches[2]);
327
+ $server_load = trim($load_arr[0]);
328
+ }
329
+ }
330
+ if(empty($server_load)) {
331
+ $server_load = __('N/A', 'fvm-serverinfo');
332
+ }
333
+ return $server_load;
334
+ }
335
+ }
336
+
337
+
338
+ ### Function: Get The Server CPU's
339
+ if(!function_exists('fvm_get_servercpu')) {
340
+ function fvm_get_servercpu() {
341
+ $numCpus = '';
342
+ if(PHP_OS != 'WINNT' && PHP_OS != 'WIN32') {
343
+ if (is_file('/proc/cpuinfo')) {
344
+ $cpuinfo = file_get_contents('/proc/cpuinfo');
345
+ preg_match_all('/^processor/m', $cpuinfo, $matches);
346
+ $numCpus = count($matches[0]);
347
+ } else {
348
+ $process = @popen('sysctl -a', 'rb');
349
+ if (false !== $process && null !== $process) {
350
+ $output = stream_get_contents($process);
351
+ preg_match('/hw.ncpu: (\d+)/', $output, $matches);
352
+ if ($matches) { $numCpus = intval($matches[1][0]); }
353
+ pclose($process);
354
+ }
355
+ }
356
+ }
357
+ if(empty($numCpus)) {
358
+ $numCpus = __('N/A', 'fvm-serverinfo');
359
+ }
360
+ return $numCpus;
361
+ }
362
+ }
inc/functions.php ADDED
@@ -0,0 +1,862 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ # handle better utf-8 and unicode encoding
4
+ if(function_exists('mb_internal_encoding')) { mb_internal_encoding('UTF-8'); }
5
+
6
+ # Consider fallback to PHP Minify [2017.07.01] from https://github.com/matthiasmullie/minify (must be defined on the outer scope)
7
+ $path = $plugindir . 'libs/matthiasmullie';
8
+ require_once $path . '/minify/src/Minify.php';
9
+ require_once $path . '/minify/src/CSS.php';
10
+ require_once $path . '/minify/src/JS.php';
11
+ require_once $path . '/minify/src/Exception.php';
12
+ require_once $path . '/minify/src/Exceptions/BasicException.php';
13
+ require_once $path . '/minify/src/Exceptions/FileImportException.php';
14
+ require_once $path . '/minify/src/Exceptions/IOException.php';
15
+ require_once $path . '/path-converter/src/ConverterInterface.php';
16
+ require_once $path . '/path-converter/src/Converter.php';
17
+ use MatthiasMullie\Minify;
18
+
19
+ # use HTML minification
20
+ require_once ($plugindir . 'libs/mrclay/HTML.php');
21
+
22
+ # get cache directories and urls
23
+ function fvm_cachepath() {
24
+
25
+ # create
26
+ $upload = wp_upload_dir();
27
+ $cachebase = rtrim($upload['basedir'], '/').'/fvm';
28
+ $cachedir = rtrim($upload['basedir'], '/').'/fvm/cache';
29
+ $tmpdir = rtrim($upload['basedir'], '/').'/fvm/temp';
30
+ $cachedirurl = rtrim($upload['baseurl'], '/').'/fvm/cache';
31
+ if(!is_dir($cachebase)) { mkdir($cachebase); }
32
+ if(!is_dir($cachedir)) { mkdir($cachedir); }
33
+ if(!is_dir($tmpdir)) { mkdir($tmpdir); }
34
+ $return = array('tmpdir'=>$tmpdir, 'cachedir'=>$cachedir, 'cachedirurl'=>$cachedirurl);
35
+
36
+ # save or update
37
+ $save = get_option('fvm-cachepath', array());
38
+ if($save != $return) {
39
+ update_option('fvm-cachepath', $return);
40
+ }
41
+
42
+ return $return;
43
+ }
44
+
45
+
46
+ # functions, get hurl info
47
+ function fastvelocity_min_get_hurl($src, $wp_domain, $wp_home) {
48
+
49
+ # preserve empty source handles
50
+ $hurl = trim($src); if(empty($hurl)) { return $hurl; }
51
+
52
+ # some fixes
53
+ $hurl = str_ireplace(array('&#038;', '&amp;'), '&', $hurl);
54
+
55
+ # get current protocol scheme with cloudflare support
56
+ if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') { $protocol = 'https://'; } else { $protocol = 'http://'; }
57
+
58
+ #make sure wp_home doesn't have a forward slash
59
+ $wp_home = rtrim($wp_home, '/');
60
+
61
+ # apply some filters
62
+ if (substr($hurl, 0, 2) === "//") { $hurl = 'http://'.ltrim($hurl, "/"); } # protocol only
63
+ if (substr($hurl, 0, 4) === "http" && stripos($hurl, $wp_domain) === false) { return $hurl; } # return if external domain
64
+ if (substr($hurl, 0, 4) !== "http" && stripos($hurl, $wp_domain) !== false) { $hurl = $wp_home.'/'.ltrim($hurl, "/"); } # protocol + home
65
+
66
+ # prevent double forward slashes in the middle
67
+ $hurl = str_ireplace('###', '://', str_ireplace('//', '/', str_ireplace('://', '###', $hurl)));
68
+
69
+ # consider different wp-content directory
70
+ $proceed = 0; if(!empty($wp_home)) {
71
+ $alt_wp_content = basename($wp_home);
72
+ if(substr($hurl, 0, strlen($alt_wp_content)) === $alt_wp_content) { $proceed = 1; }
73
+ }
74
+
75
+ # protocol + home for relative paths
76
+ if (substr($hurl, 0, 12) === "/wp-includes" || substr($hurl, 0, 9) === "/wp-admin" || substr($hurl, 0, 11) === "/wp-content" || $proceed == 1) {
77
+ $hurl = $wp_home.'/'.ltrim($hurl, "/"); }
78
+
79
+ # make sure there is a protocol prefix as required
80
+ $hurl = $protocol.str_ireplace(array('http://', 'https://'), '', $hurl); # enforce protocol
81
+
82
+ # no query strings
83
+ if (stripos($hurl, '.js?v') !== false) { $hurl = stristr($hurl, '.js?v', true).'.js'; } # no query strings
84
+ if (stripos($hurl, '.css?v') !== false) { $hurl = stristr($hurl, '.css?v', true).'.css'; } # no query strings
85
+
86
+ return $hurl;
87
+ }
88
+
89
+
90
+ # check if it's an internal url or not
91
+ function fvm_internal_url($hurl, $wp_home) {
92
+ if (substr($hurl, 0, strlen($wp_home)) === $wp_home) { return true; }
93
+ if (stripos($hurl, $wp_home) !== false) { return true; }
94
+ if (isset($_SERVER['HTTP_HOST']) && stripos($hurl, preg_replace('/:\d+$/', '', $_SERVER['HTTP_HOST'])) !== false) { return true; }
95
+ if (isset($_SERVER['SERVER_NAME']) && stripos($hurl, preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME'])) !== false) { return true; }
96
+ if (isset($_SERVER['SERVER_ADDR']) && stripos($hurl, preg_replace('/:\d+$/', '', $_SERVER['SERVER_ADDR'])) !== false) { return true; }
97
+ return false;
98
+ }
99
+
100
+
101
+ # case-insensitive in_array() wrapper
102
+ function fastvelocity_min_in_arrayi($hurl, $ignore){
103
+ $hurl = str_ireplace(array('http://', 'https://'), '//', $hurl); # better compatibility
104
+ $hurl = strtok(urldecode(rawurldecode($hurl)), '?'); # no query string, decode entities
105
+
106
+ if (!empty($hurl) && is_array($ignore)) {
107
+ foreach ($ignore as $i) {
108
+ $i = str_ireplace(array('http://', 'https://'), '//', $i); # better compatibility
109
+ $i = strtok(urldecode(rawurldecode($i)), '?'); # no query string, decode entities
110
+ $i = trim(trim($i, '/'), '*'); # wildcard removal
111
+ if (stripos($hurl, $i) !== false) { return true; }
112
+ }
113
+ }
114
+ return false;
115
+ }
116
+
117
+
118
+ # better compatibility urls, fix bootstrap 4 svg images https://www.w3.org/TR/SVG/intro.html#NamespaceAndDTDIdentifiers
119
+ function fvm_compat_urls($code) {
120
+ $code = str_ireplace(array('http://', 'https://'), '//', $code);
121
+ $code = str_ireplace('//www.w3.org/2000/svg', 'http://www.w3.org/2000/svg', $code);
122
+ return $code;
123
+ }
124
+
125
+
126
+ # minify css string with PHP Minify
127
+ function fastvelocity_min_minify_css_string($css) {
128
+ $minifier = new Minify\CSS($css);
129
+ $minifier->setMaxImportSize(15); # [css only] embed assets up to 15 Kb (default 5Kb) - processes gif, png, jpg, jpeg, svg & woff
130
+ $min = $minifier->minify();
131
+ if($min !== false) { return fvm_compat_urls($min); }
132
+ return fvm_compat_urls($css);
133
+ }
134
+
135
+
136
+ # find if we are running windows
137
+ function fvm_server_is_windows() {
138
+ if(defined('PHP_OS_FAMILY') && strtolower(PHP_OS_FAMILY) == 'windows') { return true; } # PHP 7.2.0+
139
+ if (stripos(php_uname('s'), 'Windows') !== false) { return true; }# fallback
140
+ return false;
141
+ }
142
+
143
+
144
+
145
+ # minify js on demand (one file at one time, for compatibility)
146
+ function fastvelocity_min_get_js($url, $js, $disable_js_minification) {
147
+
148
+ # exclude minification on already minified files + jquery (because minification might break those)
149
+ $excl = array('jquery.js', '.min.js', '-min.js', '/uploads/fusion-scripts/');
150
+ foreach($excl as $e) { if (stripos(basename($url), $e) !== false) { $disable_js_minification = true; break; } }
151
+
152
+ # remove BOM
153
+ $js = fastvelocity_min_remove_utf8_bom($js);
154
+
155
+ # minify JS
156
+ if(!$disable_js_minification) {
157
+ $js = fastvelocity_min_minify_js_string($js);
158
+ } else {
159
+ $js = fvm_compat_urls($js);
160
+ }
161
+
162
+ # needed when merging js files
163
+ $js = $js."\r\n;";
164
+
165
+ # return html
166
+ return $js;
167
+ }
168
+
169
+
170
+ # minify JS string with PHP Minify or YUI Compressors
171
+ function fastvelocity_min_minify_js_string($js) {
172
+ global $tmpdir, $plugindir;
173
+
174
+ # check for exec + a supported java version
175
+ $use_yui = get_option('fastvelocity_min_use_yui');
176
+ if($use_yui == true && function_exists('exec') && exec('command -v java >/dev/null && echo "yes" || echo "no"') == 'yes') {
177
+
178
+ # create temp files
179
+ $tmpname = hash('adler32', $js);
180
+ $tmpin = $tmpdir.'/'.$tmpname.'.js';
181
+ file_put_contents($tmpin, $js);
182
+ $tmpout = $tmpdir.'/'.$tmpname.'.min.js';
183
+
184
+ # define jar path and command
185
+ $cmd = 'java -jar '.$plugindir.'libs/jar/yuicompressor-2.4.8.jar'.' --preserve-semi --nomunge '.$tmpin.' -o '.$tmpout;
186
+
187
+ # run local compiler
188
+ exec($cmd . ' 2>&1', $output);
189
+ if(count($output) == 0 && file_exists($tmpout)) {
190
+ $min = file_get_contents($tmpout);
191
+ if($min !== false) { return fvm_compat_urls($min); }
192
+ exit();
193
+ }
194
+ }
195
+
196
+ # PHP Minify [2016.08.01] from https://github.com/matthiasmullie/minify
197
+ $minifier = new Minify\JS($js);
198
+ $min = $minifier->minify();
199
+ if($min !== false && (strlen(trim($js)) == strlen(trim($min)) || strlen(trim($min)) > 0)) { return fvm_compat_urls($min); }
200
+
201
+ # if we are here, something went wrong and minification didn't work
202
+ $js = "\n/*! Fast Velocity Minify: Minification of the following section has failed, so it has been merged instead. */\n".$js;
203
+ return fvm_compat_urls($js);
204
+ }
205
+
206
+ # functions, minify html
207
+ function fastvelocity_min_minify_html($html) {
208
+ return trim(fastvelocity_min_Minify_HTML::minify($html));
209
+ }
210
+
211
+ # functions to minify HTML
212
+ function fastvelocity_min_html_compression_finish($html) { return fastvelocity_min_minify_html($html); }
213
+ function fastvelocity_min_html_compression_start() {
214
+
215
+ $use_alt_html_minification = get_option('fastvelocity_min_use_alt_html_minification', '0');
216
+ if($use_alt_html_minification == '1') { ob_start('fastvelocity_min_minify_alt_html'); }
217
+ else { ob_start('fastvelocity_min_html_compression_finish'); }
218
+
219
+ }
220
+
221
+
222
+ # alternative html minification, minimal
223
+ function fastvelocity_min_minify_alt_html($html) {
224
+ $html = trim(preg_replace('/\v(?:[\t\v\h]+)/', "\n", $html));
225
+ $html = trim(preg_replace('/\t(?:[\t\v\h]+)/', ' ', $html));
226
+ $html = trim(preg_replace('/\h(?:[\t\v\h]+)/', ' ', $html));
227
+ return $html;
228
+ }
229
+
230
+ # remove all cache files
231
+ function fastvelocity_rrmdir($dir) {
232
+ if(is_dir(rtrim($dir, '/'))) {
233
+ if ($handle = opendir($dir.'/')) {
234
+ while (false !== ($file = readdir($handle))) {
235
+ @unlink($dir.'/'.$file);
236
+ }
237
+ closedir($handle); }
238
+ }
239
+ }
240
+
241
+
242
+
243
+
244
+ # return size in human format
245
+ function fastvelocity_format_filesize($bytes, $decimals = 2) {
246
+ $units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' );
247
+ for ($i = 0; ($bytes / 1024) > 0.9; $i++, $bytes /= 1024) {}
248
+ return sprintf( "%1.{$decimals}f %s", round( $bytes, $decimals ), $units[$i] );
249
+ }
250
+
251
+
252
+ # get cache size and count
253
+ function fastvelocity_get_cachestats() {
254
+
255
+ # info
256
+ $cachepath = fvm_cachepath();
257
+ $tmpdir = $cachepath['tmpdir'];
258
+ $cachedir = $cachepath['cachedir'];
259
+ $search = array($tmpdir, $cachedir);
260
+ $size = 0;
261
+ $count = 0;
262
+
263
+ foreach ($search as $dir) {
264
+ if(is_dir(rtrim($dir, '/'))) {
265
+ if ($handle = opendir($dir.'/')) {
266
+ while (false !== ($file = readdir($handle))) { $f = $dir.'/'.$file; $size = $size + filesize($f); $count++; }
267
+ closedir($handle);
268
+ }
269
+ }
270
+ }
271
+
272
+ return array('size'=>fastvelocity_format_filesize($size), 'count'=>$count);
273
+ }
274
+
275
+
276
+
277
+
278
+ # minify css on demand (one file at one time, for compatibility)
279
+ function fastvelocity_min_get_css($url, $css, $disable_css_minification) {
280
+ global $wp_domain;
281
+
282
+ # remove BOM
283
+ $css = fastvelocity_min_remove_utf8_bom($css);
284
+
285
+ # fix url paths
286
+ if(!empty($url)) { $css = preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css); }
287
+
288
+ # remove query strings from fonts (for better seo, but add a small cache buster based on most recent updates)
289
+ $ctime = get_option('fvm-last-cache-update', '0'); # last update or zero
290
+ $css = preg_replace('/(.eot|.woff2|.woff|.ttf)+[?+](.+?)(\)|\'|\")/ui', "$1"."#".$ctime."$3", $css); # fonts cache buster
291
+
292
+ # minify CSS
293
+ if(!$disable_css_minification) {
294
+ $css = fastvelocity_min_minify_css_string($css);
295
+ } else {
296
+ $css = fvm_compat_urls($css);
297
+ }
298
+
299
+ # cdn urls
300
+ $fvm_cdn_url = get_option('fastvelocity_min_fvm_cdn_url');
301
+ if(!empty($fvm_cdn_url) && filter_var($fvm_cdn_url, FILTER_VALIDATE_URL) !== FALSE) {
302
+ $fvm_cdn_url = trim(trim(str_ireplace(array('http://', 'https://'), '', trim($fvm_cdn_url, '/'))), '/');
303
+ $css = str_ireplace($wp_domain, $fvm_cdn_url, $css);
304
+ }
305
+
306
+ # return html
307
+ return $css;
308
+ }
309
+
310
+
311
+
312
+ # get remote urls with curl
313
+ function fvm_file_get_contents_curl($url, $uagent=NULL) {
314
+ $ch = curl_init();
315
+ if(isset($uagent) && !empty($uagent)) { curl_setopt($ch,CURLOPT_USERAGENT, $uagent); }
316
+ curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE);
317
+ curl_setopt($ch, CURLOPT_HEADER, 0);
318
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
319
+ curl_setopt($ch, CURLOPT_URL, $url);
320
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
321
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , 10);
322
+ curl_setopt($ch, CURLOPT_TIMEOUT, 15);
323
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
324
+ $data = curl_exec($ch);
325
+ curl_close($ch);
326
+ return $data;
327
+ }
328
+
329
+
330
+
331
+ # download and cache css and js files
332
+ function fvm_download_and_cache($hurl, $tkey, $inline=null, $disable_minification=false, $type=null){
333
+ global $cachedir, $cachedirurl, $wp_domain, $wp_home, $wp_home_path;
334
+
335
+ # filters and defaults
336
+ $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
337
+
338
+ # try to open locally first, but skip if we are on windows
339
+ if(fvm_server_is_windows() === false) {
340
+ if (stripos($hurl, $wp_domain) !== false) {
341
+ # default
342
+ $f = str_ireplace(rtrim($wp_home, '/'), rtrim($wp_home_path, '/'), $hurl);
343
+ if (file_exists($f)) {
344
+ if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
345
+ else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
346
+ set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
347
+ fvm_update_transient_keys($tkey); # keep track
348
+ $log = "$printurl --- Debug: File was opened from $f ---\n";
349
+ return array('log'=>$log, 'code'=>$code);
350
+ }
351
+
352
+ # failover when home_url != site_url
353
+ $nhurl = str_ireplace(site_url(), home_url(), $hurl);
354
+ $f = str_ireplace(rtrim($wp_home, '/'), rtrim($wp_home_path, '/'), $nhurl);
355
+ if (file_exists($f)) {
356
+ if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
357
+ else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
358
+ set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
359
+ fvm_update_transient_keys($tkey); # keep track
360
+ $log = "$printurl --- Debug: File was opened from $f ---\n";
361
+ return array('log'=>$log, 'code'=>$code);
362
+ }
363
+ }
364
+ }
365
+
366
+
367
+ # else, fallback to remote urls (or windows)
368
+ $ttl = 3600 * 24 * 7; # 7 days
369
+ $code = fastvelocity_download($hurl, $tkey, $ttl);
370
+ if($code !== false) {
371
+ if($type == 'js') { $code = fastvelocity_min_get_js($hurl, $code, $disable_minification); }
372
+ else { $code = fastvelocity_min_get_css($hurl, $code.$inline, $disable_minification); }
373
+ set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
374
+ fvm_update_transient_keys($tkey); # keep track
375
+ $log = "$printurl --- Debug: Fetched url at $hurl \n";
376
+ return array('log'=>$log, 'code'=>$code);
377
+ }
378
+
379
+
380
+ # fallback when home_url != site_url
381
+ if(stripos($hurl, $wp_domain) !== false && home_url() != site_url()) {
382
+ $nhurl = str_ireplace(site_url(), home_url(), $hurl);
383
+ $code = fastvelocity_download($nhurl, $tkey, $ttl);
384
+ if($code !== false) {
385
+ if($type == 'js') { $code = fastvelocity_min_get_js($hurl, $code, $disable_minification); }
386
+ else { $code = fastvelocity_min_get_css($hurl, $code.$inline, $disable_minification); }
387
+ set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
388
+ fvm_update_transient_keys($tkey); # keep track
389
+ $log = "$printurl --- Debug: Fetched url at $hurl \n";
390
+ return array('log'=>$log, 'code'=>$code);
391
+ }
392
+ }
393
+
394
+
395
+ # if remote urls failed... try to open locally again, regardless of OS in use
396
+ if (stripos($hurl, $wp_domain) !== false) {
397
+ # default
398
+ $f = str_ireplace(rtrim($wp_home, '/'), rtrim($wp_home_path, '/'), $hurl);
399
+ if (file_exists($f)) {
400
+ if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
401
+ else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
402
+ set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
403
+ fvm_update_transient_keys($tkey); # keep track
404
+ $log = "$printurl --- Debug: File was opened from $f ---\n";
405
+ return array('log'=>$log, 'code'=>$code);
406
+ }
407
+
408
+ # failover when home_url != site_url
409
+ $nhurl = str_ireplace(site_url(), home_url(), $hurl);
410
+ $f = str_ireplace(rtrim($wp_home, '/'), rtrim($wp_home_path, '/'), $nhurl);
411
+ if (file_exists($f)) {
412
+ if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
413
+ else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
414
+ set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
415
+ fvm_update_transient_keys($tkey); # keep track
416
+ $log = "$printurl --- Debug: File was opened from $f ---\n";
417
+ return array('log'=>$log, 'code'=>$code);
418
+ }
419
+ }
420
+
421
+
422
+ # else fail
423
+ $code = false;
424
+ $log = " - FAILED --- Debug: Tried to fetch via wp_remote_get, curl and also to open it locally. URL: $hurl ---\n";
425
+ return array('log'=>$log, 'code'=>$code);
426
+ }
427
+
428
+
429
+
430
+ # Concatenate Google Fonts tags (http://fonts.googleapis.com/css?...)
431
+ function fastvelocity_min_concatenate_google_fonts($array) {
432
+
433
+ # extract unique font families
434
+ $families = array(); foreach ($array as $font) {
435
+
436
+ # get fonts name, type and subset, remove wp query strings
437
+ $font = explode('family=', htmlspecialchars_decode(rawurldecode(urldecode($font))));
438
+ $a = explode('&v', end($font)); $font = trim(trim(trim(current($a)), ','));
439
+
440
+ # reprocess if fonts are already concatenated in this url
441
+ if(stristr($font, '|') !== FALSE) {
442
+ $multiple = explode('|', $font); if (count($multiple) > 0) { foreach ($multiple as $f) { $families[] = trim($f); } }
443
+ } else { $families[] = $font; }
444
+ }
445
+
446
+ # process types, subsets, merge, etc
447
+ $fonts = array();
448
+ foreach ($families as $font) {
449
+
450
+ # if no type or subset
451
+ if(stristr($font, ':') === FALSE) {
452
+ $fonts[] = array('name'=>$font, 'type'=>'', 'sub'=>'');
453
+ } else {
454
+
455
+ # get type and subset
456
+ $name = stristr($font, ':', true); # font name, before :
457
+ $ftype = trim(stristr($font, ':'), ':'); # second part of the string, after :
458
+
459
+ # get font types and subset
460
+ if(stristr($ftype, '&subset=') === FALSE) {
461
+ $fonts[] = array('name'=>$name, 'type'=>$ftype, 'sub'=>'');
462
+ } else {
463
+ $newftype = stristr($ftype, '&', true); # font type, before &
464
+ $subset = trim(str_ireplace('&subset=', '', stristr($ftype, '&'))); # second part of the string, after &
465
+ $fonts[] = array('name'=>$name, 'type'=>$newftype, 'sub'=>$subset);
466
+ }
467
+
468
+ }
469
+ }
470
+
471
+ # make sure we have unique font names, types and subsets
472
+ $ufonts = array(); foreach ($fonts as $f) { $ufonts[$f['name']] = $f['name']; } # unique font names
473
+ $usubsets = array(); foreach ($fonts as $f) { if(!empty($f['sub'])) { $usubsets[$f['sub']] = $f['sub']; } } # unique subsets
474
+
475
+ # prepare
476
+ $fonts_and_types = $ufonts;
477
+
478
+ # get unique types and subsets for each unique font name
479
+ foreach ($ufonts as $uf) {
480
+
481
+ # types
482
+ $utypes = array();
483
+ foreach ($fonts as $f) {
484
+ if($f['name'] == $uf && !empty($f['type'])) { $utypes = array_merge($utypes, explode(',', $f['type'])); }
485
+ }
486
+
487
+ # filter types
488
+ $utypes = array_unique($utypes);
489
+ sort($utypes);
490
+ $ntype = ''; if(count($utypes) > 0) { $ntype = ':'.implode(',', $utypes); } # types to append to the font name
491
+
492
+ # generate font url queries
493
+ $fonts_and_types[$uf] = str_ireplace(' ', '+', $uf).$ntype;
494
+ }
495
+
496
+ # concat fonts, generate unique google fonts url
497
+ if(count($fonts_and_types) > 0) {
498
+ $msubsets = ''; if(count($usubsets) > 0 && implode(',', $usubsets) != 'latin') { $msubsets = "&subset=".implode(',', $usubsets); } # merge subsets
499
+ return trim('https://fonts.googleapis.com/css?family='.implode('|', $fonts_and_types).$msubsets); # process
500
+ }
501
+
502
+ return false;
503
+ }
504
+
505
+
506
+
507
+ # readme parser
508
+ function fastvelocity_min_readme($url) {
509
+
510
+ # read file
511
+ $file = @file_get_contents( $url );
512
+ if (empty($file)) { return '<strong>Readme Parser: readme.txt not found!</strong>'; }
513
+
514
+ // line end to \n
515
+ $file = preg_replace("/(\n\r|\r\n|\r|\n)/", "\n", $file);
516
+
517
+ // headlines
518
+ $s = array('===','==','=' );
519
+ $r = array('h2' ,'h3','h4');
520
+ for ( $x = 0; $x < sizeof($s); $x++ ) {
521
+ $file = preg_replace('/(.*?)'.$s[$x].'(?!\")(.*?)'.$s[$x].'(.*?)/', '$1<'.$r[$x].'>$2</'.$r[$x].'>$3', $file);
522
+ }
523
+
524
+ // inline
525
+ $s = array('\*\*','\`');
526
+ $r = array('b' ,'code');
527
+ for ( $x = 0; $x < sizeof($s); $x++ ) {
528
+ $file = preg_replace('/(.*?)'.$s[$x].'(?!\s)(.*?)(?!\s)'.$s[$x].'(.*?)/', '$1<'.$r[$x].'>$2</'.$r[$x].'>$3', $file);
529
+ }
530
+
531
+ // ' _italic_ '
532
+ $file = preg_replace('/(\s)_(\S.*?\S)_(\s|$)/', ' <em>$2</em> ', $file);
533
+
534
+ // ul lists
535
+ $s = array('\*','\+','\-');
536
+ for ( $x = 0; $x < sizeof($s); $x++ )
537
+ $file = preg_replace('/^['.$s[$x].'](\s)(.*?)(\n|$)/m', '<li>$2</li>', $file);
538
+ $file = preg_replace('/\n<li>(.*?)/', '<ul><li>$1', $file);
539
+ $file = preg_replace('/(<\/li>)(?!<li>)/', '$1</ul>', $file);
540
+
541
+ // ol lists
542
+ $file = preg_replace('/(\d{1,2}\.)\s(.*?)(\n|$)/', '<li>$2</li>', $file);
543
+ $file = preg_replace('/\n<li>(.*?)/', '<ol><li>$1', $file);
544
+ $file = preg_replace('/(<\/li>)(?!(\<li\>|\<\/ul\>))/', '$1</ol>', $file);
545
+
546
+ // ol screenshots style
547
+ $file = preg_replace('/(?=Screenshots)(.*?)<ol>/', '$1<ol class="readme-parser-screenshots">', $file);
548
+
549
+ // line breaks
550
+ $file = preg_replace('/(.*?)(\n)/', "<p>$1</p>", $file);
551
+ $file = preg_replace('/(1|2|3|4)(><br\/>)/', '$1>', $file);
552
+ $file = str_ireplace('</ul><br/>', '</ul>', $file);
553
+
554
+ # cleanup
555
+ $file = str_ireplace('<p></p>', '', $file);
556
+ $file = str_ireplace('<p><h4>', '<h4>', $file);
557
+ $file = str_ireplace('</h4></p>', '</h4>', $file);
558
+
559
+ // urls
560
+ $file = str_replace('http://www.', 'www.', $file);
561
+ $file = str_replace('www.', 'http://www.', $file);
562
+ $file = preg_replace('#(^|[^\"=]{1})(http://|ftp://|mailto:|https://)([^\s<>]+)([\s\n<>]|$)#', '$1<a target="_blank" href="$2$3">$2$3</a>$4', $file);
563
+
564
+ # extract faqs
565
+ $prefix = "Frequently Asked Questions";
566
+ $faq = substr($file, strpos($file, $prefix) + strlen($prefix));
567
+ $faq = substr($faq, 0, strpos($faq, '<p><h3>'));
568
+
569
+
570
+ return trim($faq);
571
+ }
572
+
573
+
574
+ # remove emoji support
575
+ function fastvelocity_min_disable_wp_emojicons() {
576
+ remove_action('wp_head', 'print_emoji_detection_script', 7);
577
+ remove_action('wp_print_styles', 'print_emoji_styles');
578
+ remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
579
+ remove_action( 'admin_print_styles', 'print_emoji_styles' );
580
+ }
581
+
582
+ # escape double quotes
583
+ function fastvelocity_escape_double($string) {
584
+ return str_ireplace(array('"', '\\"', '\\\"'), '\"', $string);
585
+ }
586
+
587
+
588
+ # remove UTF8 BOM
589
+ function fastvelocity_min_remove_utf8_bom($text) {
590
+ $bom = pack('H*','EFBBBF');
591
+ $text = preg_replace("/^$bom/ui", '', $text);
592
+ return $text;
593
+ }
594
+
595
+
596
+ # Remove query string from static css files
597
+ function fastvelocity_remove_cssjs_ver( $src ) {
598
+ if(stripos($src, '?ver=')) { $src = remove_query_arg('ver', $src); }
599
+ return $src;
600
+ }
601
+
602
+
603
+ # rewrite cache files to http, https or dynamic
604
+ function fvm_get_protocol($url) {
605
+ $url = ltrim(str_ireplace(array('http://', 'https://'), '', $url), '/'); # better compatibility
606
+
607
+ # cdn support
608
+ $fvm_cdn_url = get_option('fastvelocity_min_fvm_cdn_url');
609
+ if(!empty($fvm_cdn_url) && filter_var($fvm_cdn_url, FILTER_VALIDATE_URL) !== FALSE) {
610
+ $fvm_cdn_url = trim(trim(str_ireplace(array('http://', 'https://'), '', trim($fvm_cdn_url, '/'))), '/');
611
+ $url = str_ireplace($wp_domain, $fvm_cdn_url, $url);
612
+ }
613
+
614
+ # enforce protocol if needed
615
+ $fp = get_option('fastvelocity_min_default_protocol', 'dynamic');
616
+ if($fp == 'http') { $url = 'http://'.$url; } elseif($fp == 'https') { $url = 'https://'.$url; } else { $url = '//'.$url; }
617
+
618
+ # return
619
+ return $url;
620
+ }
621
+
622
+ # keep track of transients, whenever we save a transient.
623
+ function fvm_update_transient_keys($new_transient_key) {
624
+ $transient_keys = get_option('fvm_transient_keys'); # get
625
+ $transient_keys[]= $new_transient_key; # append
626
+ update_option( 'fvm_transient_keys', $transient_keys); # save
627
+ }
628
+
629
+ # keep track of transients, dump our plugin transients as needed
630
+ function fvm_transients_purge() {
631
+ update_option('fvm-last-cache-update', time()); # last cache update to now
632
+ $transient_keys = get_option( 'fvm_transient_keys' ); # get
633
+ update_option( 'fvm_transient_keys', array()); # reset
634
+ foreach( $transient_keys as $t ) { delete_transient( $t ); } # delete
635
+ }
636
+
637
+
638
+ # purge all caches
639
+ function fvm_purge_all() {
640
+
641
+ # get cache directories and urls
642
+ $cachepath = fvm_cachepath();
643
+ $tmpdir = $cachepath['tmpdir'];
644
+ $cachedir = $cachepath['cachedir'];
645
+ $cachedirurl = $cachepath['cachedirurl'];
646
+
647
+ # delete minification files and transients
648
+ if(!is_dir($cachedir)) { return false; }
649
+ fastvelocity_rrmdir($cachedir);
650
+ fvm_transients_purge();
651
+
652
+ return true;
653
+ }
654
+
655
+
656
+
657
+ # exclude processing from some pages / posts / contents
658
+ function fastvelocity_exclude_contents() {
659
+
660
+ # customizer preview, visual composer
661
+ $arr = array('customize_theme', 'preview_id', 'preview');
662
+ foreach ($arr as $a) { if(isset($_GET[$a])) { return true; } }
663
+
664
+ # Thrive plugins and other post_types
665
+ $arr = array('tve_form_type', 'tve_lead_shortcode', 'tqb_splash');
666
+ foreach ($arr as $a) { if(isset($_GET['post_type']) && $_GET['post_type'] == $a) { return true; } }
667
+
668
+ # default
669
+ return false;
670
+ }
671
+
672
+ # Know files that should always be ignored
673
+ function fastvelocity_default_ignore($ignore) {
674
+ if(is_array($ignore)) {
675
+
676
+ # defaults, to be overwriten by api or transient
677
+ $exc = array('/fvm/cache/', '/Avada/assets/js/main.min.js', '/woocommerce-product-search/js/product-search.js', '/includes/builder/scripts/frontend-builder-scripts.js', '/assets/js/jquery.themepunch.tools.min.js');
678
+
679
+ # info and api call
680
+ $api = 'https://fastvelocity.com/api/fvm/ignore.txt';
681
+ $ctime = get_option('fvm-last-cache-update', '0');
682
+ $tkey = 'fvm-api-ignore-'.$ctime;
683
+ $ttl = 24 * 3600; # one day, in seconds
684
+ $json = fastvelocity_download($api, $tkey, $ttl);
685
+ if($json !== false) {
686
+ $data = json_decode($json, true);
687
+ if (json_last_error() === JSON_ERROR_NONE && is_array($data)) { $exc = $data; }
688
+ }
689
+
690
+ # should we exclude jquery when defer is enabled?
691
+ $exclude_defer_jquery = get_option('fastvelocity_min_exclude_defer_jquery');
692
+ $enable_defer_js = get_option('fastvelocity_min_enable_defer_js');
693
+ if($enable_defer_js == true && $exclude_defer_jquery == true) {
694
+ $exc[] = '/jquery.js';
695
+ $exc[] = '/jquery.min.js';
696
+ }
697
+
698
+ # make sure it's unique and not empty
699
+ $uniq = array();
700
+ foreach ($ignore as $i) { $k = hash('adler32', $i); if(!empty($i)) { $uniq[$k] = $i; } }
701
+ foreach ($exc as $e) { $k = hash('adler32', $e); if(!empty($e)) { $uniq[$k] = $e; } }
702
+
703
+ # merge and return
704
+ return $uniq;
705
+ } else { return $ignore; }
706
+ }
707
+
708
+
709
+ # IE only files that should always be ignored, without incrementing our groups
710
+ function fastvelocity_ie_blacklist($url) {
711
+
712
+ # defaults, to be overwriten by api or transient
713
+ $exc = array('/html5shiv.js', '/excanvas.js', '/avada-ie9.js', '/respond.js', '/respond.min.js', '/selectivizr.js', '/Avada/assets/css/ie.css', '/html5.js', '/IE9.js', '/fusion-ie9.js', '/vc_lte_ie9.min.css', '/old-ie.css', '/ie.css', '/vc-ie8.min.css', '/mailchimp-for-wp/assets/js/third-party/placeholders.min.js');
714
+
715
+ # info and api call
716
+ $api = 'https://fastvelocity.com/api/fvm/ie_blacklist.txt';
717
+ $ctime = get_option('fvm-last-cache-update', '0');
718
+ $tkey = 'fvm-api-ie-blacklist-'.$ctime;
719
+ $ttl = 24 * 3600; # one day, in seconds
720
+ $json = fastvelocity_download($api, $tkey, $ttl);
721
+ if($json !== false) {
722
+ $data = json_decode($json, true);
723
+ if (json_last_error() === JSON_ERROR_NONE && is_array($data)) { $exc = $data; }
724
+ }
725
+
726
+ # is the url on our list and return
727
+ $res = fastvelocity_min_in_arrayi($url, $exc);
728
+ if($res == true) { return true; } else { return false; }
729
+ }
730
+
731
+
732
+ # download function with cache support and fallback
733
+ function fastvelocity_download($url, $tkey, $ttl) {
734
+
735
+ # rate limit requests, prevent slowdowns
736
+ $rtlim = false; $rtlim = get_transient($tkey.'_access');
737
+ if ( $rtlim !== false) { return false; }
738
+
739
+ # info (needed for google fonts woff files)
740
+ $uagent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12';
741
+ $data = false; $data = get_transient($tkey);
742
+ if ( $data === false) {
743
+
744
+ # get updated list from our api and cache it for 24 hours
745
+ $response = wp_remote_get($url, array('user-agent'=>$uagent, 'timeout' => 7, 'httpversion' => '1.1', 'sslverify'=>false));
746
+ $res_code = wp_remote_retrieve_response_code($response);
747
+ if($res_code == '200') {
748
+ $data = wp_remote_retrieve_body($response);
749
+ if(strlen($data) > 1 && $ttl > 0) {
750
+ set_transient($tkey, $data, $ttl);
751
+ fvm_update_transient_keys($tkey); # keep track
752
+ return $data;
753
+ }
754
+ }
755
+
756
+ # fallback, let's try curl if available
757
+ if(function_exists('curl_version')) {
758
+ $curl = fvm_file_get_contents_curl($url, $uagent);
759
+ if(!empty($curl) && strlen($curl) > 1 && $ttl > 0) {
760
+ set_transient($tkey, $data, $ttl);
761
+ fvm_update_transient_keys($tkey); # keep track
762
+ return $data;
763
+ }
764
+ }
765
+
766
+ # error
767
+ set_transient($tkey.'_access', "Failed to fetch: $url on ".current_time('timestamp'), $ttl);
768
+ return false;
769
+ }
770
+
771
+ # return transient
772
+ return $data;
773
+ }
774
+
775
+
776
+ # Purge Godaddy Managed WordPress Hosting (Varnish)
777
+ # https://github.com/wp-media/wp-rocket/blob/master/inc/3rd-party/hosting/godaddy.php
778
+ function fastvelocity_godaddy_request( $method, $url = null ) {
779
+ $url = empty( $url ) ? home_url() : $url;
780
+ $host = parse_url( $url, PHP_URL_HOST );
781
+ $url = set_url_scheme( str_replace( $host, WPaas\Plugin::vip(), $url ), 'http' );
782
+ wp_cache_flush();
783
+ update_option( 'gd_system_last_cache_flush', time() ); # purge apc
784
+ wp_remote_request( esc_url_raw( $url ), array('method' => $method, 'blocking' => false, 'headers' => array('Host' => $host)) );
785
+ }
786
+
787
+
788
+ function fastvelocity_purge_others(){
789
+
790
+ # wodpress default cache
791
+ if (function_exists('wp_cache_flush')) {
792
+ wp_cache_flush();
793
+ }
794
+
795
+ # Purge all W3 Total Cache
796
+ if (function_exists('w3tc_pgcache_flush')) {
797
+ w3tc_pgcache_flush();
798
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>W3 Total Cache</strong> have also been purged.</p></div>');
799
+ }
800
+
801
+ # Purge WP Super Cache
802
+ if (function_exists('wp_cache_clear_cache')) {
803
+ wp_cache_clear_cache();
804
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>WP Super Cache</strong> have also been purged.</p></div>');
805
+ }
806
+
807
+ # Purge WP Rocket
808
+ if (function_exists('rocket_clean_domain')) {
809
+ rocket_clean_domain();
810
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>WP Rocket</strong> have also been purged.</p></div>');
811
+ }
812
+
813
+ # Purge Wp Fastest Cache
814
+ if(isset($GLOBALS['wp_fastest_cache']) && method_exists($GLOBALS['wp_fastest_cache'], 'deleteCache')){
815
+ $GLOBALS['wp_fastest_cache']->deleteCache();
816
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>Wp Fastest Cache</strong> have also been purged.</p></div>');
817
+ }
818
+
819
+ # Purge Cachify
820
+ if (function_exists('cachify_flush_cache')) {
821
+ cachify_flush_cache();
822
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>Cachify</strong> have also been purged.</p></div>');
823
+ }
824
+
825
+ # Purge Comet Cache
826
+ if ( class_exists("comet_cache") ) {
827
+ comet_cache::clear();
828
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>Comet Cache</strong> have also been purged.</p></div>');
829
+ }
830
+
831
+ # Purge Zen Cache
832
+ if ( class_exists("zencache") ) {
833
+ zencache::clear();
834
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>Comet Cache</strong> have also been purged.</p></div>');
835
+ }
836
+
837
+ # Purge LiteSpeed Cache
838
+ if (class_exists('LiteSpeed_Cache_Tags')) {
839
+ LiteSpeed_Cache_Tags::add_purge_tag('*');
840
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>LiteSpeed Cache</strong> have also been purged.</p></div>');
841
+ }
842
+
843
+ # Purge SG Optimizer
844
+ if (function_exists('sg_cachepress_purge_cache')) {
845
+ sg_cachepress_purge_cache();
846
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>SG Optimizer</strong> have also been purged.</p></div>');
847
+ }
848
+
849
+ # Purge Godaddy Managed WordPress Hosting (Varnish + APC)
850
+ if (class_exists('WPaaS\Plugin')) {
851
+ fastvelocity_godaddy_request('BAN');
852
+ return __('<div class="notice notice-info is-dismissible"><p>All caches from <strong>WP Engine</strong> have also been purged.</p></div>');
853
+ }
854
+
855
+ # Purge WP Engine
856
+ if (class_exists("WpeCommon")) {
857
+ if (method_exists('WpeCommon', 'purge_memcached')) { WpeCommon::purge_memcached(); }
858
+ if (method_exists('WpeCommon', 'clear_maxcdn_cache')) { WpeCommon::clear_maxcdn_cache(); }
859
+ if (method_exists('WpeCommon', 'purge_varnish_cache')) { WpeCommon::purge_varnish_cache(); }
860
+ }
861
+
862
+ }
libs/index.html ADDED
File without changes
libs/jar/index.html ADDED
File without changes
libs/jar/yuicompressor-2.4.8.jar ADDED
Binary file
libs/matthiasmullie/index.html ADDED
File without changes
libs/matthiasmullie/minify/bin/index.html ADDED
File without changes
libs/matthiasmullie/minify/bin/minifycss ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env php
2
+ <?php
3
+ use MatthiasMullie\Minify;
4
+
5
+ // command line utility to minify CSS
6
+ if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
+ // if composer install
8
+ require_once __DIR__ . '/../../../autoload.php';
9
+ } else {
10
+ require_once __DIR__ . '/../src/Minify.php';
11
+ require_once __DIR__ . '/../src/CSS.php';
12
+ require_once __DIR__ . '/../src/Exception.php';
13
+ }
14
+
15
+ error_reporting(E_ALL);
16
+ // check PHP setup for cli arguments
17
+ if (!isset($_SERVER['argv']) && !isset($argv)) {
18
+ fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
+ exit(1);
20
+ } elseif (!isset($argv)) {
21
+ $argv = $_SERVER['argv'];
22
+ }
23
+ // check if path to file given
24
+ if (!isset($argv[1])) {
25
+ fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
+ exit(1);
27
+ }
28
+ // check if script run in cli environment
29
+ if ('cli' !== php_sapi_name()) {
30
+ fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
+ exit(1);
32
+ }
33
+ // check if source file exists
34
+ if (!file_exists($argv[1])) {
35
+ fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
+ exit(1);
37
+ }
38
+
39
+ try {
40
+ $minifier = new Minify\CSS($argv[1]);
41
+ echo $minifier->minify();
42
+ } catch (Exception $e) {
43
+ fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
+ exit(1);
45
+ }
libs/matthiasmullie/minify/bin/minifyjs ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env php
2
+ <?php
3
+ use MatthiasMullie\Minify;
4
+
5
+ // command line utility to minify JS
6
+ if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
+ // if composer install
8
+ require_once __DIR__ . '/../../../autoload.php';
9
+ } else {
10
+ require_once __DIR__ . '/../src/Minify.php';
11
+ require_once __DIR__ . '/../src/JS.php';
12
+ require_once __DIR__ . '/../src/Exception.php';
13
+ }
14
+
15
+ error_reporting(E_ALL);
16
+ // check PHP setup for cli arguments
17
+ if (!isset($_SERVER['argv']) && !isset($argv)) {
18
+ fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
+ exit(1);
20
+ } elseif (!isset($argv)) {
21
+ $argv = $_SERVER['argv'];
22
+ }
23
+ // check if path to file given
24
+ if (!isset($argv[1])) {
25
+ fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
+ exit(1);
27
+ }
28
+ // check if script run in cli environment
29
+ if ('cli' !== php_sapi_name()) {
30
+ fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
+ exit(1);
32
+ }
33
+ // check if source file exists
34
+ if (!file_exists($argv[1])) {
35
+ fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
+ exit(1);
37
+ }
38
+
39
+ try {
40
+ $minifier = new Minify\JS($argv[1]);
41
+ echo $minifier->minify();
42
+ } catch (Exception $e) {
43
+ fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
+ exit(1);
45
+ }
libs/matthiasmullie/minify/data/index.html ADDED
File without changes
libs/matthiasmullie/minify/data/js/index.html ADDED
File without changes
libs/matthiasmullie/minify/data/js/keywords_after.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ in
2
+ public
3
+ extends
4
+ private
5
+ protected
6
+ implements
7
+ instanceof
libs/matthiasmullie/minify/data/js/keywords_before.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ do
2
+ in
3
+ let
4
+ new
5
+ var
6
+ case
7
+ else
8
+ enum
9
+ void
10
+ with
11
+ class
12
+ const
13
+ yield
14
+ delete
15
+ export
16
+ import
17
+ public
18
+ static
19
+ typeof
20
+ extends
21
+ package
22
+ private
23
+ function
24
+ protected
25
+ implements
26
+ instanceof
libs/matthiasmullie/minify/data/js/keywords_reserved.txt ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ do
2
+ if
3
+ in
4
+ for
5
+ let
6
+ new
7
+ try
8
+ var
9
+ case
10
+ else
11
+ enum
12
+ eval
13
+ null
14
+ this
15
+ true
16
+ void
17
+ with
18
+ break
19
+ catch
20
+ class
21
+ const
22
+ false
23
+ super
24
+ throw
25
+ while
26
+ yield
27
+ delete
28
+ export
29
+ import
30
+ public
31
+ return
32
+ static
33
+ switch
34
+ typeof
35
+ default
36
+ extends
37
+ finally
38
+ package
39
+ private
40
+ continue
41
+ debugger
42
+ function
43
+ arguments
44
+ interface
45
+ protected
46
+ implements
47
+ instanceof
48
+ abstract
49
+ boolean
50
+ byte
51
+ char
52
+ double
53
+ final
54
+ float
55
+ goto
56
+ int
57
+ long
58
+ native
59
+ short
60
+ synchronized
61
+ throws
62
+ transient
63
+ volatile
libs/matthiasmullie/minify/data/js/operators.txt ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ ~
22
+ <<
23
+ >>
24
+ >>>
25
+ ==
26
+ ===
27
+ !=
28
+ !==
29
+ >
30
+ <
31
+ >=
32
+ <=
33
+ &&
34
+ ||
35
+ !
36
+ .
37
+ [
38
+ ]
39
+ ?
40
+ :
41
+ ,
42
+ ;
43
+ (
44
+ )
45
+ {
46
+ }
libs/matthiasmullie/minify/data/js/operators_after.txt ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ ~
22
+ <<
23
+ >>
24
+ >>>
25
+ ==
26
+ ===
27
+ !=
28
+ !==
29
+ >
30
+ <
31
+ >=
32
+ <=
33
+ &&
34
+ ||
35
+ .
36
+ [
37
+ ]
38
+ ?
39
+ :
40
+ ,
41
+ ;
42
+ (
43
+ )
44
+ }
libs/matthiasmullie/minify/data/js/operators_before.txt ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ ~
22
+ <<
23
+ >>
24
+ >>>
25
+ ==
26
+ ===
27
+ !=
28
+ !==
29
+ >
30
+ <
31
+ >=
32
+ <=
33
+ &&
34
+ ||
35
+ !
36
+ .
37
+ [
38
+ ?
39
+ :
40
+ ,
41
+ ;
42
+ (
43
+ {
libs/matthiasmullie/minify/index.html ADDED
File without changes
libs/matthiasmullie/minify/src/CSS.php ADDED
@@ -0,0 +1,690 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\Minify;
4
+
5
+ use MatthiasMullie\Minify\Exceptions\FileImportException;
6
+ use MatthiasMullie\PathConverter\ConverterInterface;
7
+ use MatthiasMullie\PathConverter\Converter;
8
+
9
+ /**
10
+ * CSS minifier.
11
+ *
12
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
13
+ *
14
+ * @author Matthias Mullie <minify@mullie.eu>
15
+ * @author Tijs Verkoyen <minify@verkoyen.eu>
16
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
17
+ * @license MIT License
18
+ */
19
+ class CSS extends Minify
20
+ {
21
+ /**
22
+ * @var int
23
+ */
24
+ protected $maxImportSize = 5;
25
+
26
+ /**
27
+ * @var string[]
28
+ */
29
+ protected $importExtensions = array(
30
+ 'gif' => 'data:image/gif',
31
+ 'png' => 'data:image/png',
32
+ 'jpe' => 'data:image/jpeg',
33
+ 'jpg' => 'data:image/jpeg',
34
+ 'jpeg' => 'data:image/jpeg',
35
+ 'svg' => 'data:image/svg+xml',
36
+ 'woff' => 'data:application/x-font-woff',
37
+ 'tif' => 'image/tiff',
38
+ 'tiff' => 'image/tiff',
39
+ 'xbm' => 'image/x-xbitmap',
40
+ );
41
+
42
+ /**
43
+ * Set the maximum size if files to be imported.
44
+ *
45
+ * Files larger than this size (in kB) will not be imported into the CSS.
46
+ * Importing files into the CSS as data-uri will save you some connections,
47
+ * but we should only import relatively small decorative images so that our
48
+ * CSS file doesn't get too bulky.
49
+ *
50
+ * @param int $size Size in kB
51
+ */
52
+ public function setMaxImportSize($size)
53
+ {
54
+ $this->maxImportSize = $size;
55
+ }
56
+
57
+ /**
58
+ * Set the type of extensions to be imported into the CSS (to save network
59
+ * connections).
60
+ * Keys of the array should be the file extensions & respective values
61
+ * should be the data type.
62
+ *
63
+ * @param string[] $extensions Array of file extensions
64
+ */
65
+ public function setImportExtensions(array $extensions)
66
+ {
67
+ $this->importExtensions = $extensions;
68
+ }
69
+
70
+ /**
71
+ * Move any import statements to the top.
72
+ *
73
+ * @param string $content Nearly finished CSS content
74
+ *
75
+ * @return string
76
+ */
77
+ protected function moveImportsToTop($content)
78
+ {
79
+ if (preg_match_all('/@import[^;]+;/', $content, $matches)) {
80
+ // remove from content
81
+ foreach ($matches[0] as $import) {
82
+ $content = str_replace($import, '', $content);
83
+ }
84
+
85
+ // add to top
86
+ $content = implode('', $matches[0]).$content;
87
+ }
88
+
89
+ return $content;
90
+ }
91
+
92
+ /**
93
+ * Combine CSS from import statements.
94
+ *
95
+ * @import's will be loaded and their content merged into the original file,
96
+ * to save HTTP requests.
97
+ *
98
+ * @param string $source The file to combine imports for
99
+ * @param string $content The CSS content to combine imports for
100
+ * @param string[] $parents Parent paths, for circular reference checks
101
+ *
102
+ * @return string
103
+ *
104
+ * @throws FileImportException
105
+ */
106
+ protected function combineImports($source, $content, $parents)
107
+ {
108
+ $importRegexes = array(
109
+ // @import url(xxx)
110
+ '/
111
+ # import statement
112
+ @import
113
+
114
+ # whitespace
115
+ \s+
116
+
117
+ # open url()
118
+ url\(
119
+
120
+ # (optional) open path enclosure
121
+ (?P<quotes>["\']?)
122
+
123
+ # fetch path
124
+ (?P<path>.+?)
125
+
126
+ # (optional) close path enclosure
127
+ (?P=quotes)
128
+
129
+ # close url()
130
+ \)
131
+
132
+ # (optional) trailing whitespace
133
+ \s*
134
+
135
+ # (optional) media statement(s)
136
+ (?P<media>[^;]*)
137
+
138
+ # (optional) trailing whitespace
139
+ \s*
140
+
141
+ # (optional) closing semi-colon
142
+ ;?
143
+
144
+ /ix',
145
+
146
+ // @import 'xxx'
147
+ '/
148
+
149
+ # import statement
150
+ @import
151
+
152
+ # whitespace
153
+ \s+
154
+
155
+ # open path enclosure
156
+ (?P<quotes>["\'])
157
+
158
+ # fetch path
159
+ (?P<path>.+?)
160
+
161
+ # close path enclosure
162
+ (?P=quotes)
163
+
164
+ # (optional) trailing whitespace
165
+ \s*
166
+
167
+ # (optional) media statement(s)
168
+ (?P<media>[^;]*)
169
+
170
+ # (optional) trailing whitespace
171
+ \s*
172
+
173
+ # (optional) closing semi-colon
174
+ ;?
175
+
176
+ /ix',
177
+ );
178
+
179
+ // find all relative imports in css
180
+ $matches = array();
181
+ foreach ($importRegexes as $importRegex) {
182
+ if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) {
183
+ $matches = array_merge($matches, $regexMatches);
184
+ }
185
+ }
186
+
187
+ $search = array();
188
+ $replace = array();
189
+
190
+ // loop the matches
191
+ foreach ($matches as $match) {
192
+ // get the path for the file that will be imported
193
+ $importPath = dirname($source).'/'.$match['path'];
194
+
195
+ // only replace the import with the content if we can grab the
196
+ // content of the file
197
+ if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
198
+ continue;
199
+ }
200
+
201
+ // check if current file was not imported previously in the same
202
+ // import chain.
203
+ if (in_array($importPath, $parents)) {
204
+ throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
205
+ }
206
+
207
+ // grab referenced file & minify it (which may include importing
208
+ // yet other @import statements recursively)
209
+ $minifier = new static($importPath);
210
+ $importContent = $minifier->execute($source, $parents);
211
+
212
+ // check if this is only valid for certain media
213
+ if (!empty($match['media'])) {
214
+ $importContent = '@media '.$match['media'].'{'.$importContent.'}';
215
+ }
216
+
217
+ // add to replacement array
218
+ $search[] = $match[0];
219
+ $replace[] = $importContent;
220
+ }
221
+
222
+ // replace the import statements
223
+ return str_replace($search, $replace, $content);
224
+ }
225
+
226
+ /**
227
+ * Import files into the CSS, base64-ized.
228
+ *
229
+ * @url(image.jpg) images will be loaded and their content merged into the
230
+ * original file, to save HTTP requests.
231
+ *
232
+ * @param string $source The file to import files for
233
+ * @param string $content The CSS content to import files for
234
+ *
235
+ * @return string
236
+ */
237
+ protected function importFiles($source, $content)
238
+ {
239
+ $regex = '/url\((["\']?)(.+?)\\1\)/i';
240
+ if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
241
+ $search = array();
242
+ $replace = array();
243
+
244
+ // loop the matches
245
+ foreach ($matches as $match) {
246
+ $extension = substr(strrchr($match[2], '.'), 1);
247
+ if ($extension && !array_key_exists($extension, $this->importExtensions)) {
248
+ continue;
249
+ }
250
+
251
+ // get the path for the file that will be imported
252
+ $path = $match[2];
253
+ $path = dirname($source).'/'.$path;
254
+
255
+ // only replace the import with the content if we're able to get
256
+ // the content of the file, and it's relatively small
257
+ if ($this->canImportFile($path) && $this->canImportBySize($path)) {
258
+ // grab content && base64-ize
259
+ $importContent = $this->load($path);
260
+ $importContent = base64_encode($importContent);
261
+
262
+ // build replacement
263
+ $search[] = $match[0];
264
+ $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
265
+ }
266
+ }
267
+
268
+ // replace the import statements
269
+ $content = str_replace($search, $replace, $content);
270
+ }
271
+
272
+ return $content;
273
+ }
274
+
275
+ /**
276
+ * Minify the data.
277
+ * Perform CSS optimizations.
278
+ *
279
+ * @param string[optional] $path Path to write the data to
280
+ * @param string[] $parents Parent paths, for circular reference checks
281
+ *
282
+ * @return string The minified data
283
+ */
284
+ public function execute($path = null, $parents = array())
285
+ {
286
+ $content = '';
287
+
288
+ // loop CSS data (raw data and files)
289
+ foreach ($this->data as $source => $css) {
290
+ /*
291
+ * Let's first take out strings & comments, since we can't just
292
+ * remove whitespace anywhere. If whitespace occurs inside a string,
293
+ * we should leave it alone. E.g.:
294
+ * p { content: "a test" }
295
+ */
296
+ $this->extractStrings();
297
+ $this->stripComments();
298
+ $css = $this->replace($css);
299
+
300
+ $css = $this->stripWhitespace($css);
301
+ $css = $this->shortenHex($css);
302
+ $css = $this->shortenZeroes($css);
303
+ $css = $this->shortenFontWeights($css);
304
+ $css = $this->stripEmptyTags($css);
305
+
306
+ // restore the string we've extracted earlier
307
+ $css = $this->restoreExtractedData($css);
308
+
309
+ $source = is_int($source) ? '' : $source;
310
+ $parents = $source ? array_merge($parents, array($source)) : $parents;
311
+ $css = $this->combineImports($source, $css, $parents);
312
+ $css = $this->importFiles($source, $css);
313
+
314
+ /*
315
+ * If we'll save to a new path, we'll have to fix the relative paths
316
+ * to be relative no longer to the source file, but to the new path.
317
+ * If we don't write to a file, fall back to same path so no
318
+ * conversion happens (because we still want it to go through most
319
+ * of the move code, which also addresses url() & @import syntax...)
320
+ */
321
+ $converter = $this->getPathConverter($source, $path ?: $source);
322
+ $css = $this->move($converter, $css);
323
+
324
+ // combine css
325
+ $content .= $css;
326
+ }
327
+
328
+ $content = $this->moveImportsToTop($content);
329
+
330
+ return $content;
331
+ }
332
+
333
+ /**
334
+ * Moving a css file should update all relative urls.
335
+ * Relative references (e.g. ../images/image.gif) in a certain css file,
336
+ * will have to be updated when a file is being saved at another location
337
+ * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
338
+ *
339
+ * @param ConverterInterface $converter Relative path converter
340
+ * @param string $content The CSS content to update relative urls for
341
+ *
342
+ * @return string
343
+ */
344
+ protected function move(ConverterInterface $converter, $content)
345
+ {
346
+ /*
347
+ * Relative path references will usually be enclosed by url(). @import
348
+ * is an exception, where url() is not necessary around the path (but is
349
+ * allowed).
350
+ * This *could* be 1 regular expression, where both regular expressions
351
+ * in this array are on different sides of a |. But we're using named
352
+ * patterns in both regexes, the same name on both regexes. This is only
353
+ * possible with a (?J) modifier, but that only works after a fairly
354
+ * recent PCRE version. That's why I'm doing 2 separate regular
355
+ * expressions & combining the matches after executing of both.
356
+ */
357
+ $relativeRegexes = array(
358
+ // url(xxx)
359
+ '/
360
+ # open url()
361
+ url\(
362
+
363
+ \s*
364
+
365
+ # open path enclosure
366
+ (?P<quotes>["\'])?
367
+
368
+ # fetch path
369
+ (?P<path>.+?)
370
+
371
+ # close path enclosure
372
+ (?(quotes)(?P=quotes))
373
+
374
+ \s*
375
+
376
+ # close url()
377
+ \)
378
+
379
+ /ix',
380
+
381
+ // @import "xxx"
382
+ '/
383
+ # import statement
384
+ @import
385
+
386
+ # whitespace
387
+ \s+
388
+
389
+ # we don\'t have to check for @import url(), because the
390
+ # condition above will already catch these
391
+
392
+ # open path enclosure
393
+ (?P<quotes>["\'])
394
+
395
+ # fetch path
396
+ (?P<path>.+?)
397
+
398
+ # close path enclosure
399
+ (?P=quotes)
400
+
401
+ /ix',
402
+ );
403
+
404
+ // find all relative urls in css
405
+ $matches = array();
406
+ foreach ($relativeRegexes as $relativeRegex) {
407
+ if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
408
+ $matches = array_merge($matches, $regexMatches);
409
+ }
410
+ }
411
+
412
+ $search = array();
413
+ $replace = array();
414
+
415
+ // loop all urls
416
+ foreach ($matches as $match) {
417
+ // determine if it's a url() or an @import match
418
+ $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
419
+
420
+ $url = $match['path'];
421
+ if ($this->canImportByPath($url)) {
422
+ // attempting to interpret GET-params makes no sense, so let's discard them for awhile
423
+ $params = strrchr($url, '?');
424
+ $url = $params ? substr($url, 0, -strlen($params)) : $url;
425
+
426
+ // fix relative url
427
+ $url = $converter->convert($url);
428
+
429
+ // now that the path has been converted, re-apply GET-params
430
+ $url .= $params;
431
+ }
432
+
433
+ /*
434
+ * Urls with control characters above 0x7e should be quoted.
435
+ * According to Mozilla's parser, whitespace is only allowed at the
436
+ * end of unquoted urls.
437
+ * Urls with `)` (as could happen with data: uris) should also be
438
+ * quoted to avoid being confused for the url() closing parentheses.
439
+ * And urls with a # have also been reported to cause issues.
440
+ *
441
+ * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
442
+ * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
443
+ */
444
+ $url = trim($url);
445
+ if (preg_match('/[\s\)#\x{7f}-\x{9f}]/u', $url)) {
446
+ $url = $match['quotes'] . $url . $match['quotes'];
447
+ }
448
+
449
+ // build replacement
450
+ $search[] = $match[0];
451
+ if ($type === 'url') {
452
+ $replace[] = 'url('.$url.')';
453
+ } elseif ($type === 'import') {
454
+ $replace[] = '@import "'.$url.'"';
455
+ }
456
+ }
457
+
458
+ // replace urls
459
+ return str_replace($search, $replace, $content);
460
+ }
461
+
462
+ /**
463
+ * Shorthand hex color codes.
464
+ * #FF0000 -> #F00.
465
+ *
466
+ * @param string $content The CSS content to shorten the hex color codes for
467
+ *
468
+ * @return string
469
+ */
470
+ protected function shortenHex($content)
471
+ {
472
+ $content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?=[; }])/i', '#$1$2$3', $content);
473
+
474
+ // we can shorten some even more by replacing them with their color name
475
+ $colors = array(
476
+ '#F0FFFF' => 'azure',
477
+ '#F5F5DC' => 'beige',
478
+ '#A52A2A' => 'brown',
479
+ '#FF7F50' => 'coral',
480
+ '#FFD700' => 'gold',
481
+ '#808080' => 'gray',
482
+ '#008000' => 'green',
483
+ '#4B0082' => 'indigo',
484
+ '#FFFFF0' => 'ivory',
485
+ '#F0E68C' => 'khaki',
486
+ '#FAF0E6' => 'linen',
487
+ '#800000' => 'maroon',
488
+ '#000080' => 'navy',
489
+ '#808000' => 'olive',
490
+ '#CD853F' => 'peru',
491
+ '#FFC0CB' => 'pink',
492
+ '#DDA0DD' => 'plum',
493
+ '#800080' => 'purple',
494
+ '#F00' => 'red',
495
+ '#FA8072' => 'salmon',
496
+ '#A0522D' => 'sienna',
497
+ '#C0C0C0' => 'silver',
498
+ '#FFFAFA' => 'snow',
499
+ '#D2B48C' => 'tan',
500
+ '#FF6347' => 'tomato',
501
+ '#EE82EE' => 'violet',
502
+ '#F5DEB3' => 'wheat',
503
+ );
504
+
505
+ return preg_replace_callback(
506
+ '/(?<=[: ])('.implode(array_keys($colors), '|').')(?=[; }])/i',
507
+ function ($match) use ($colors) {
508
+ return $colors[strtoupper($match[0])];
509
+ },
510
+ $content
511
+ );
512
+ }
513
+
514
+ /**
515
+ * Shorten CSS font weights.
516
+ *
517
+ * @param string $content The CSS content to shorten the font weights for
518
+ *
519
+ * @return string
520
+ */
521
+ protected function shortenFontWeights($content)
522
+ {
523
+ $weights = array(
524
+ 'normal' => 400,
525
+ 'bold' => 700,
526
+ );
527
+
528
+ $callback = function ($match) use ($weights) {
529
+ return $match[1].$weights[$match[2]];
530
+ };
531
+
532
+ return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
533
+ }
534
+
535
+ /**
536
+ * Shorthand 0 values to plain 0, instead of e.g. -0em.
537
+ *
538
+ * @param string $content The CSS content to shorten the zero values for
539
+ *
540
+ * @return string
541
+ */
542
+ protected function shortenZeroes($content)
543
+ {
544
+ // reusable bits of code throughout these regexes:
545
+ // before & after are used to make sure we don't match lose unintended
546
+ // 0-like values (e.g. in #000, or in http://url/1.0)
547
+ // units can be stripped from 0 values, or used to recognize non 0
548
+ // values (where wa may be able to strip a .0 suffix)
549
+ $before = '(?<=[:(, ])';
550
+ $after = '(?=[ ,);}])';
551
+ $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
552
+
553
+ // strip units after zeroes (0px -> 0)
554
+ // NOTE: it should be safe to remove all units for a 0 value, but in
555
+ // practice, Webkit (especially Safari) seems to stumble over at least
556
+ // 0%, potentially other units as well. Only stripping 'px' for now.
557
+ // @see https://github.com/matthiasmullie/minify/issues/60
558
+ $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
559
+
560
+ // strip 0-digits (.0 -> 0)
561
+ $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
562
+ // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
563
+ $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
564
+ // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
565
+ $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
566
+ // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
567
+ $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
568
+
569
+ // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
570
+ $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
571
+
572
+ // remove zeroes where they make no sense in calc: e.g. calc(100px - 0)
573
+ // the 0 doesn't have any effect, and this isn't even valid without unit
574
+ // strip all `+ 0` or `- 0` occurrences: calc(10% + 0) -> calc(10%)
575
+ // looped because there may be multiple 0s inside 1 group of parentheses
576
+ do {
577
+ $previous = $content;
578
+ $content = preg_replace('/\(([^\(\)]+) [\+\-] 0( [^\(\)]+)?\)/', '(\\1\\2)', $content);
579
+ } while ($content !== $previous);
580
+ // strip all `0 +` occurrences: calc(0 + 10%) -> calc(10%)
581
+ $content = preg_replace('/\(0 \+ ([^\(\)]+)\)/', '(\\1)', $content);
582
+ // strip all `0 -` occurrences: calc(0 - 10%) -> calc(-10%)
583
+ $content = preg_replace('/\(0 \- ([^\(\)]+)\)/', '(-\\1)', $content);
584
+ // I'm not going to attempt to optimize away `x * 0` instances:
585
+ // it's dumb enough code already that it likely won't occur, and it's
586
+ // too complex to do right (order of operations would have to be
587
+ // respected etc)
588
+ // what I cared about most here was fixing incorrectly truncated units
589
+
590
+ // IE doesn't seem to understand a unitless flex-basis value, so let's
591
+ // add it in again (make it `%`, which is only 1 char: 0%, 0px, 0
592
+ // anything, it's all just the same)
593
+ $content = preg_replace('/flex:([^ ]+ [^ ]+ )0([;\}])/', 'flex:${1}0%${2}', $content);
594
+ $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
595
+
596
+ return $content;
597
+ }
598
+
599
+ /**
600
+ * Strip comments from source code.
601
+ *
602
+ * @param string $content
603
+ *
604
+ * @return string
605
+ */
606
+ protected function stripEmptyTags($content)
607
+ {
608
+ return preg_replace('/(^|\}|;)[^\{\};]+\{\s*\}/', '\\1', $content);
609
+ }
610
+
611
+ /**
612
+ * Strip comments from source code.
613
+ */
614
+ protected function stripComments()
615
+ {
616
+ $this->registerPattern('/\/\*.*?\*\//s', '');
617
+ }
618
+
619
+ /**
620
+ * Strip whitespace.
621
+ *
622
+ * @param string $content The CSS content to strip the whitespace for
623
+ *
624
+ * @return string
625
+ */
626
+ protected function stripWhitespace($content)
627
+ {
628
+ // remove leading & trailing whitespace
629
+ $content = preg_replace('/^\s*/m', '', $content);
630
+ $content = preg_replace('/\s*$/m', '', $content);
631
+
632
+ // replace newlines with a single space
633
+ $content = preg_replace('/\s+/', ' ', $content);
634
+
635
+ // remove whitespace around meta characters
636
+ // inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
637
+ $content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
638
+ $content = preg_replace('/([\[(:])\s+/', '$1', $content);
639
+ $content = preg_replace('/\s+([\]\)])/', '$1', $content);
640
+ $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
641
+
642
+ // whitespace around + and - can only be stripped in selectors, like
643
+ // :nth-child(3+2n), not in things like calc(3px + 2px) or shorthands
644
+ // like 3px -2px
645
+ $content = preg_replace('/\s*([+-])\s*(?=[^}]*{)/', '$1', $content);
646
+
647
+ // remove semicolon/whitespace followed by closing bracket
648
+ $content = str_replace(';}', '}', $content);
649
+
650
+ return trim($content);
651
+ }
652
+
653
+ /**
654
+ * Check if file is small enough to be imported.
655
+ *
656
+ * @param string $path The path to the file
657
+ *
658
+ * @return bool
659
+ */
660
+ protected function canImportBySize($path)
661
+ {
662
+ return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
663
+ }
664
+
665
+ /**
666
+ * Check if file a file can be imported, going by the path.
667
+ *
668
+ * @param string $path
669
+ *
670
+ * @return bool
671
+ */
672
+ protected function canImportByPath($path)
673
+ {
674
+ return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
675
+ }
676
+
677
+ /**
678
+ * Return a converter to update relative paths to be relative to the new
679
+ * destination.
680
+ *
681
+ * @param string $source
682
+ * @param string $target
683
+ *
684
+ * @return ConverterInterface
685
+ */
686
+ protected function getPathConverter($source, $target)
687
+ {
688
+ return new Converter($source, $target);
689
+ }
690
+ }
libs/matthiasmullie/minify/src/Exception.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\Minify;
4
+
5
+ /**
6
+ * @deprecated Use Exceptions\BasicException instead
7
+ *
8
+ * @author Matthias Mullie <minify@mullie.eu>
9
+ */
10
+ abstract class Exception extends \Exception
11
+ {
12
+ }
libs/matthiasmullie/minify/src/Exceptions/BasicException.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\Minify\Exceptions;
4
+
5
+ use MatthiasMullie\Minify\Exception;
6
+
7
+ /**
8
+ * @author Matthias Mullie <minify@mullie.eu>
9
+ */
10
+ abstract class BasicException extends Exception
11
+ {
12
+ }
libs/matthiasmullie/minify/src/Exceptions/FileImportException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\Minify\Exceptions;
4
+
5
+ /**
6
+ * @author Matthias Mullie <minify@mullie.eu>
7
+ */
8
+ class FileImportException extends BasicException
9
+ {
10
+ }
libs/matthiasmullie/minify/src/Exceptions/IOException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\Minify\Exceptions;
4
+
5
+ /**
6
+ * @author Matthias Mullie <minify@mullie.eu>
7
+ */
8
+ class IOException extends BasicException
9
+ {
10
+ }
libs/matthiasmullie/minify/src/Exceptions/index.html ADDED
File without changes
libs/matthiasmullie/minify/src/JS.php ADDED
@@ -0,0 +1,529 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\Minify;
4
+
5
+ /**
6
+ * JavaScript minifier.
7
+ *
8
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
9
+ *
10
+ * @author Matthias Mullie <minify@mullie.eu>
11
+ * @author Tijs Verkoyen <minify@verkoyen.eu>
12
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
13
+ * @license MIT License
14
+ */
15
+ class JS extends Minify
16
+ {
17
+ /**
18
+ * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
19
+ *
20
+ * Note that regular expressions using that bit must have the PCRE_UTF8
21
+ * pattern modifier (/u) set.
22
+ *
23
+ * @var string
24
+ */
25
+ const REGEX_VARIABLE = '\b[$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}][$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}0-9\x{0300}-\x{036f}\x{0483}-\x{0487}\x{0591}-\x{05bd}\x{05bf}\x{05c1}\x{05c2}\x{05c4}\x{05c5}\x{05c7}\x{0610}-\x{061a}\x{064b}-\x{0669}\x{0670}\x{06d6}-\x{06dc}\x{06df}-\x{06e4}\x{06e7}\x{06e8}\x{06ea}-\x{06ed}\x{06f0}-\x{06f9}\x{0711}\x{0730}-\x{074a}\x{07a6}-\x{07b0}\x{07c0}-\x{07c9}\x{07eb}-\x{07f3}\x{0816}-\x{0819}\x{081b}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082d}\x{0859}-\x{085b}\x{08e4}-\x{08fe}\x{0900}-\x{0903}\x{093a}-\x{093c}\x{093e}-\x{094f}\x{0951}-\x{0957}\x{0962}\x{0963}\x{0966}-\x{096f}\x{0981}-\x{0983}\x{09bc}\x{09be}-\x{09c4}\x{09c7}\x{09c8}\x{09cb}-\x{09cd}\x{09d7}\x{09e2}\x{09e3}\x{09e6}-\x{09ef}\x{0a01}-\x{0a03}\x{0a3c}\x{0a3e}-\x{0a42}\x{0a47}\x{0a48}\x{0a4b}-\x{0a4d}\x{0a51}\x{0a66}-\x{0a71}\x{0a75}\x{0a81}-\x{0a83}\x{0abc}\x{0abe}-\x{0ac5}\x{0ac7}-\x{0ac9}\x{0acb}-\x{0acd}\x{0ae2}\x{0ae3}\x{0ae6}-\x{0aef}\x{0b01}-\x{0b03}\x{0b3c}\x{0b3e}-\x{0b44}\x{0b47}\x{0b48}\x{0b4b}-\x{0b4d}\x{0b56}\x{0b57}\x{0b62}\x{0b63}\x{0b66}-\x{0b6f}\x{0b82}\x{0bbe}-\x{0bc2}\x{0bc6}-\x{0bc8}\x{0bca}-\x{0bcd}\x{0bd7}\x{0be6}-\x{0bef}\x{0c01}-\x{0c03}\x{0c3e}-\x{0c44}\x{0c46}-\x{0c48}\x{0c4a}-\x{0c4d}\x{0c55}\x{0c56}\x{0c62}\x{0c63}\x{0c66}-\x{0c6f}\x{0c82}\x{0c83}\x{0cbc}\x{0cbe}-\x{0cc4}\x{0cc6}-\x{0cc8}\x{0cca}-\x{0ccd}\x{0cd5}\x{0cd6}\x{0ce2}\x{0ce3}\x{0ce6}-\x{0cef}\x{0d02}\x{0d03}\x{0d3e}-\x{0d44}\x{0d46}-\x{0d48}\x{0d4a}-\x{0d4d}\x{0d57}\x{0d62}\x{0d63}\x{0d66}-\x{0d6f}\x{0d82}\x{0d83}\x{0dca}\x{0dcf}-\x{0dd4}\x{0dd6}\x{0dd8}-\x{0ddf}\x{0df2}\x{0df3}\x{0e31}\x{0e34}-\x{0e3a}\x{0e47}-\x{0e4e}\x{0e50}-\x{0e59}\x{0eb1}\x{0eb4}-\x{0eb9}\x{0ebb}\x{0ebc}\x{0ec8}-\x{0ecd}\x{0ed0}-\x{0ed9}\x{0f18}\x{0f19}\x{0f20}-\x{0f29}\x{0f35}\x{0f37}\x{0f39}\x{0f3e}\x{0f3f}\x{0f71}-\x{0f84}\x{0f86}\x{0f87}\x{0f8d}-\x{0f97}\x{0f99}-\x{0fbc}\x{0fc6}\x{102b}-\x{103e}\x{1040}-\x{1049}\x{1056}-\x{1059}\x{105e}-\x{1060}\x{1062}-\x{1064}\x{1067}-\x{106d}\x{1071}-\x{1074}\x{1082}-\x{108d}\x{108f}-\x{109d}\x{135d}-\x{135f}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17d3}\x{17dd}\x{17e0}-\x{17e9}\x{180b}-\x{180d}\x{1810}-\x{1819}\x{18a9}\x{1920}-\x{192b}\x{1930}-\x{193b}\x{1946}-\x{194f}\x{19b0}-\x{19c0}\x{19c8}\x{19c9}\x{19d0}-\x{19d9}\x{1a17}-\x{1a1b}\x{1a55}-\x{1a5e}\x{1a60}-\x{1a7c}\x{1a7f}-\x{1a89}\x{1a90}-\x{1a99}\x{1b00}-\x{1b04}\x{1b34}-\x{1b44}\x{1b50}-\x{1b59}\x{1b6b}-\x{1b73}\x{1b80}-\x{1b82}\x{1ba1}-\x{1bad}\x{1bb0}-\x{1bb9}\x{1be6}-\x{1bf3}\x{1c24}-\x{1c37}\x{1c40}-\x{1c49}\x{1c50}-\x{1c59}\x{1cd0}-\x{1cd2}\x{1cd4}-\x{1ce8}\x{1ced}\x{1cf2}-\x{1cf4}\x{1dc0}-\x{1de6}\x{1dfc}-\x{1dff}\x{200c}\x{200d}\x{203f}\x{2040}\x{2054}\x{20d0}-\x{20dc}\x{20e1}\x{20e5}-\x{20f0}\x{2cef}-\x{2cf1}\x{2d7f}\x{2de0}-\x{2dff}\x{302a}-\x{302f}\x{3099}\x{309a}\x{a620}-\x{a629}\x{a66f}\x{a674}-\x{a67d}\x{a69f}\x{a6f0}\x{a6f1}\x{a802}\x{a806}\x{a80b}\x{a823}-\x{a827}\x{a880}\x{a881}\x{a8b4}-\x{a8c4}\x{a8d0}-\x{a8d9}\x{a8e0}-\x{a8f1}\x{a900}-\x{a909}\x{a926}-\x{a92d}\x{a947}-\x{a953}\x{a980}-\x{a983}\x{a9b3}-\x{a9c0}\x{a9d0}-\x{a9d9}\x{aa29}-\x{aa36}\x{aa43}\x{aa4c}\x{aa4d}\x{aa50}-\x{aa59}\x{aa7b}\x{aab0}\x{aab2}-\x{aab4}\x{aab7}\x{aab8}\x{aabe}\x{aabf}\x{aac1}\x{aaeb}-\x{aaef}\x{aaf5}\x{aaf6}\x{abe3}-\x{abea}\x{abec}\x{abed}\x{abf0}-\x{abf9}\x{fb1e}\x{fe00}-\x{fe0f}\x{fe20}-\x{fe26}\x{fe33}\x{fe34}\x{fe4d}-\x{fe4f}\x{ff10}-\x{ff19}\x{ff3f}]*\b';
26
+
27
+ /**
28
+ * Full list of JavaScript reserved words.
29
+ * Will be loaded from /data/js/keywords_reserved.txt.
30
+ *
31
+ * @see https://mathiasbynens.be/notes/reserved-keywords
32
+ *
33
+ * @var string[]
34
+ */
35
+ protected $keywordsReserved = array();
36
+
37
+ /**
38
+ * List of JavaScript reserved words that accept a <variable, value, ...>
39
+ * after them. Some end of lines are not the end of a statement, like with
40
+ * these keywords.
41
+ *
42
+ * E.g.: we shouldn't insert a ; after this else
43
+ * else
44
+ * console.log('this is quite fine')
45
+ *
46
+ * Will be loaded from /data/js/keywords_before.txt
47
+ *
48
+ * @var string[]
49
+ */
50
+ protected $keywordsBefore = array();
51
+
52
+ /**
53
+ * List of JavaScript reserved words that accept a <variable, value, ...>
54
+ * before them. Some end of lines are not the end of a statement, like when
55
+ * continued by one of these keywords on the newline.
56
+ *
57
+ * E.g.: we shouldn't insert a ; before this instanceof
58
+ * variable
59
+ * instanceof String
60
+ *
61
+ * Will be loaded from /data/js/keywords_after.txt
62
+ *
63
+ * @var string[]
64
+ */
65
+ protected $keywordsAfter = array();
66
+
67
+ /**
68
+ * List of all JavaScript operators.
69
+ *
70
+ * Will be loaded from /data/js/operators.txt
71
+ *
72
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
73
+ *
74
+ * @var string[]
75
+ */
76
+ protected $operators = array();
77
+
78
+ /**
79
+ * List of JavaScript operators that accept a <variable, value, ...> after
80
+ * them. Some end of lines are not the end of a statement, like with these
81
+ * operators.
82
+ *
83
+ * Note: Most operators are fine, we've only removed !, ++ and --.
84
+ * There can't be a newline separating ! and whatever it is negating.
85
+ * ++ & -- have to be joined with the value they're in-/decrementing.
86
+ *
87
+ * Will be loaded from /data/js/operators_before.txt
88
+ *
89
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
90
+ *
91
+ * @var string[]
92
+ */
93
+ protected $operatorsBefore = array();
94
+
95
+ /**
96
+ * List of JavaScript operators that accept a <variable, value, ...> before
97
+ * them. Some end of lines are not the end of a statement, like when
98
+ * continued by one of these operators on the newline.
99
+ *
100
+ * Note: Most operators are fine, we've only removed ), ], ++ and --.
101
+ * ++ & -- have to be joined with the value they're in-/decrementing.
102
+ * ) & ] are "special" in that they have lots or usecases. () for example
103
+ * is used for function calls, for grouping, in if () and for (), ...
104
+ *
105
+ * Will be loaded from /data/js/operators_after.txt
106
+ *
107
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
108
+ *
109
+ * @var string[]
110
+ */
111
+ protected $operatorsAfter = array();
112
+
113
+ /**
114
+ * {@inheritdoc}
115
+ */
116
+ public function __construct()
117
+ {
118
+ call_user_func_array(array('parent', '__construct'), func_get_args());
119
+
120
+ $dataDir = __DIR__.'/../data/js/';
121
+ $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
122
+ $this->keywordsReserved = file($dataDir.'keywords_reserved.txt', $options);
123
+ $this->keywordsBefore = file($dataDir.'keywords_before.txt', $options);
124
+ $this->keywordsAfter = file($dataDir.'keywords_after.txt', $options);
125
+ $this->operators = file($dataDir.'operators.txt', $options);
126
+ $this->operatorsBefore = file($dataDir.'operators_before.txt', $options);
127
+ $this->operatorsAfter = file($dataDir.'operators_after.txt', $options);
128
+ }
129
+
130
+ /**
131
+ * Minify the data.
132
+ * Perform JS optimizations.
133
+ *
134
+ * @param string[optional] $path Path to write the data to
135
+ *
136
+ * @return string The minified data
137
+ */
138
+ public function execute($path = null)
139
+ {
140
+ $content = '';
141
+
142
+ // loop files
143
+ foreach ($this->data as $source => $js) {
144
+ /*
145
+ * Combine js: separating the scripts by a ;
146
+ * I'm also adding a newline: it will be eaten when whitespace is
147
+ * stripped, but we need to make sure we're not just appending
148
+ * a new script right after a previous script that ended with a
149
+ * singe-line comment on the last line (in which case it would also
150
+ * be seen as part of that comment)
151
+ */
152
+ $content .= $js."\n;";
153
+ }
154
+
155
+ /*
156
+ * Let's first take out strings, comments and regular expressions.
157
+ * All of these can contain JS code-like characters, and we should make
158
+ * sure any further magic ignores anything inside of these.
159
+ *
160
+ * Consider this example, where we should not strip any whitespace:
161
+ * var str = "a test";
162
+ *
163
+ * Comments will be removed altogether, strings and regular expressions
164
+ * will be replaced by placeholder text, which we'll restore later.
165
+ */
166
+ $this->extractStrings('\'"`');
167
+ $this->stripComments();
168
+ $this->extractRegex();
169
+ $content = $this->replace($content);
170
+
171
+ $content = $this->propertyNotation($content);
172
+ $content = $this->shortenBools($content);
173
+ $content = $this->stripWhitespace($content);
174
+
175
+ /*
176
+ * Earlier, we extracted strings & regular expressions and replaced them
177
+ * with placeholder text. This will restore them.
178
+ */
179
+ $content = $this->restoreExtractedData($content);
180
+
181
+ return $content;
182
+ }
183
+
184
+ /**
185
+ * Strip comments from source code.
186
+ */
187
+ protected function stripComments()
188
+ {
189
+ // single-line comments
190
+ $this->registerPattern('/\/\/.*$/m', '');
191
+
192
+ // multi-line comments
193
+ $this->registerPattern('/\/\*.*?\*\//s', '');
194
+ }
195
+
196
+ /**
197
+ * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
198
+ *
199
+ * The content inside the regex can contain characters that may be confused
200
+ * for JS code: e.g. it could contain whitespace it needs to match & we
201
+ * don't want to strip whitespace in there.
202
+ *
203
+ * The regex can be pretty simple: we don't have to care about comments,
204
+ * (which also use slashes) because stripComments() will have stripped those
205
+ * already.
206
+ *
207
+ * This method will replace all string content with simple REGEX#
208
+ * placeholder text, so we've rid all regular expressions from characters
209
+ * that may be misinterpreted. Original regex content will be saved in
210
+ * $this->extracted and after doing all other minifying, we can restore the
211
+ * original content via restoreRegex()
212
+ */
213
+ protected function extractRegex()
214
+ {
215
+ // PHP only supports $this inside anonymous functions since 5.4
216
+ $minifier = $this;
217
+ $callback = function ($match) use ($minifier) {
218
+ $count = count($minifier->extracted);
219
+ $placeholder = '/'.$count.'/';
220
+ $minifier->extracted[$placeholder] = $match[0];
221
+
222
+ return $placeholder;
223
+ };
224
+
225
+ $pattern = '\/.*?(?<!\\\\)(\\\\\\\\)*\/[gimy]*(?![0-9a-zA-Z\/])';
226
+
227
+ // a regular expression can only be followed by a few operators or some
228
+ // of the RegExp methods (a `\` followed by a variable or value is
229
+ // likely part of a division, not a regex)
230
+ $after = '[\.,;\)\}]';
231
+ $methods = '\.(exec|test|match|search|replace|split)\(';
232
+ $this->registerPattern('/'.$pattern.'(?=\s*('.$after.'|'.$methods.'))/', $callback);
233
+
234
+ // 1 more edge case: a regex can be followed by a lot more operators or
235
+ // keywords if there's a newline (ASI) in between, where the operator
236
+ // actually starts a new statement
237
+ // (https://github.com/matthiasmullie/minify/issues/56)
238
+ $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
239
+ $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
240
+ $this->registerPattern('/'.$pattern.'\s*\n(?=\s*('.implode('|', $operators).'))/', $callback);
241
+ }
242
+
243
+ /**
244
+ * Strip whitespace.
245
+ *
246
+ * We won't strip *all* whitespace, but as much as possible. The thing that
247
+ * we'll preserve are newlines we're unsure about.
248
+ * JavaScript doesn't require statements to be terminated with a semicolon.
249
+ * It will automatically fix missing semicolons with ASI (automatic semi-
250
+ * colon insertion) at the end of line causing errors (without semicolon.)
251
+ *
252
+ * Because it's sometimes hard to tell if a newline is part of a statement
253
+ * that should be terminated or not, we'll just leave some of them alone.
254
+ *
255
+ * @param string $content The content to strip the whitespace for
256
+ *
257
+ * @return string
258
+ */
259
+ protected function stripWhitespace($content)
260
+ {
261
+ // uniform line endings, make them all line feed
262
+ $content = str_replace(array("\r\n", "\r"), "\n", $content);
263
+
264
+ // collapse all non-line feed whitespace into a single space
265
+ $content = preg_replace('/[^\S\n]+/', ' ', $content);
266
+
267
+ // strip leading & trailing whitespace
268
+ $content = str_replace(array(" \n", "\n "), "\n", $content);
269
+
270
+ // collapse consecutive line feeds into just 1
271
+ $content = preg_replace('/\n+/', "\n", $content);
272
+
273
+ $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
274
+ $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
275
+ $operators = $this->getOperatorsForRegex($this->operators, '/');
276
+ $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
277
+ $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
278
+
279
+ // strip whitespace that ends in (or next line begin with) an operator
280
+ // that allows statements to be broken up over multiple lines
281
+ unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
282
+ $content = preg_replace(
283
+ array(
284
+ '/('.implode('|', $operatorsBefore).')\s+/',
285
+ '/\s+('.implode('|', $operatorsAfter).')/',
286
+ ), '\\1', $content
287
+ );
288
+
289
+ // make sure + and - can't be mistaken for, or joined into ++ and --
290
+ $content = preg_replace(
291
+ array(
292
+ '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
293
+ '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
294
+ ), '\\1', $content
295
+ );
296
+
297
+ // collapse whitespace around reserved words into single space
298
+ $content = preg_replace('/(^|[;\}\s])\K('.implode('|', $keywordsBefore).')\s+/', '\\2 ', $content);
299
+ $content = preg_replace('/\s+('.implode('|', $keywordsAfter).')(?=([;\{\s]|$))/', ' \\1', $content);
300
+
301
+ /*
302
+ * We didn't strip whitespace after a couple of operators because they
303
+ * could be used in different contexts and we can't be sure it's ok to
304
+ * strip the newlines. However, we can safely strip any non-line feed
305
+ * whitespace that follows them.
306
+ */
307
+ $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
308
+ $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
309
+ $content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
310
+ $content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
311
+
312
+ /*
313
+ * Get rid of double semicolons, except where they can be used like:
314
+ * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
315
+ * I'll safeguard these double semicolons inside for-loops by
316
+ * temporarily replacing them with an invalid condition: they won't have
317
+ * a double semicolon and will be easy to spot to restore afterwards.
318
+ */
319
+ $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
320
+ $content = preg_replace('/;+/', ';', $content);
321
+ $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
322
+
323
+ /*
324
+ * Next, we'll be removing all semicolons where ASI kicks in.
325
+ * for-loops however, can have an empty body (ending in only a
326
+ * semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
327
+ * Here, nothing happens during the loop; it's just used to keep
328
+ * increasing `i`. With that ; omitted, the next line would be expected
329
+ * to be the for-loop's body...
330
+ * I'm going to double that semicolon (if any) so after the next line,
331
+ * which strips semicolons here & there, we're still left with this one.
332
+ */
333
+ $content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
334
+ $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
335
+
336
+ /*
337
+ * We also can't strip empty else-statements. Even though they're
338
+ * useless and probably shouldn't be in the code in the first place, we
339
+ * shouldn't be stripping the `;` that follows it as it breaks the code.
340
+ * We can just remove those useless else-statements completely.
341
+ *
342
+ * @see https://github.com/matthiasmullie/minify/issues/91
343
+ */
344
+ $content = preg_replace('/else;/s', '', $content);
345
+
346
+ /*
347
+ * We also don't really want to terminate statements followed by closing
348
+ * curly braces (which we've ignored completely up until now) or end-of-
349
+ * script: ASI will kick in here & we're all about minifying.
350
+ * Semicolons at beginning of the file don't make any sense either.
351
+ */
352
+ $content = preg_replace('/;(\}|$)/s', '\\1', $content);
353
+ $content = ltrim($content, ';');
354
+
355
+ // get rid of remaining whitespace af beginning/end
356
+ return trim($content);
357
+ }
358
+
359
+ /**
360
+ * We'll strip whitespace around certain operators with regular expressions.
361
+ * This will prepare the given array by escaping all characters.
362
+ *
363
+ * @param string[] $operators
364
+ * @param string $delimiter
365
+ *
366
+ * @return string[]
367
+ */
368
+ protected function getOperatorsForRegex(array $operators, $delimiter = '/')
369
+ {
370
+ // escape operators for use in regex
371
+ $delimiters = array_fill(0, count($operators), $delimiter);
372
+ $escaped = array_map('preg_quote', $operators, $delimiters);
373
+
374
+ $operators = array_combine($operators, $escaped);
375
+
376
+ // ignore + & - for now, they'll get special treatment
377
+ unset($operators['+'], $operators['-']);
378
+
379
+ // dot can not just immediately follow a number; it can be confused for
380
+ // decimal point, or calling a method on it, e.g. 42 .toString()
381
+ $operators['.'] = '(?<![0-9]\s)\.';
382
+
383
+ // don't confuse = with other assignment shortcuts (e.g. +=)
384
+ $chars = preg_quote('+-*\=<>%&|', $delimiter);
385
+ $operators['='] = '(?<!['.$chars.'])\=';
386
+
387
+ return $operators;
388
+ }
389
+
390
+ /**
391
+ * We'll strip whitespace around certain keywords with regular expressions.
392
+ * This will prepare the given array by escaping all characters.
393
+ *
394
+ * @param string[] $keywords
395
+ * @param string $delimiter
396
+ *
397
+ * @return string[]
398
+ */
399
+ protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
400
+ {
401
+ // escape keywords for use in regex
402
+ $delimiter = array_fill(0, count($keywords), $delimiter);
403
+ $escaped = array_map('preg_quote', $keywords, $delimiter);
404
+
405
+ // add word boundaries
406
+ array_walk($keywords, function ($value) {
407
+ return '\b'.$value.'\b';
408
+ });
409
+
410
+ $keywords = array_combine($keywords, $escaped);
411
+
412
+ return $keywords;
413
+ }
414
+
415
+ /**
416
+ * Replaces all occurrences of array['key'] by array.key.
417
+ *
418
+ * @param string $content
419
+ *
420
+ * @return string
421
+ */
422
+ protected function propertyNotation($content)
423
+ {
424
+ // PHP only supports $this inside anonymous functions since 5.4
425
+ $minifier = $this;
426
+ $keywords = $this->keywordsReserved;
427
+ $callback = function ($match) use ($minifier, $keywords) {
428
+ $property = trim($minifier->extracted[$match[1]], '\'"');
429
+
430
+ /*
431
+ * Check if the property is a reserved keyword. In this context (as
432
+ * property of an object literal/array) it shouldn't matter, but IE8
433
+ * freaks out with "Expected identifier".
434
+ */
435
+ if (in_array($property, $keywords)) {
436
+ return $match[0];
437
+ }
438
+
439
+ /*
440
+ * See if the property is in a variable-like format (e.g.
441
+ * array['key-here'] can't be replaced by array.key-here since '-'
442
+ * is not a valid character there.
443
+ */
444
+ if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
445
+ return $match[0];
446
+ }
447
+
448
+ return '.'.$property;
449
+ };
450
+
451
+ /*
452
+ * Figure out if previous character is a variable name (of the array
453
+ * we want to use property notation on) - this is to make sure
454
+ * standalone ['value'] arrays aren't confused for keys-of-an-array.
455
+ * We can (and only have to) check the last character, because PHP's
456
+ * regex implementation doesn't allow unfixed-length look-behind
457
+ * assertions.
458
+ */
459
+ preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
460
+ $previousChar = $previousChar[1];
461
+
462
+ /*
463
+ * Make sure word preceding the ['value'] is not a keyword, e.g.
464
+ * return['x']. Because -again- PHP's regex implementation doesn't allow
465
+ * unfixed-length look-behind assertions, I'm just going to do a lot of
466
+ * separate look-behind assertions, one for each keyword.
467
+ */
468
+ $keywords = $this->getKeywordsForRegex($keywords);
469
+ $keywords = '(?<!'.implode(')(?<!', $keywords).')';
470
+
471
+ return preg_replace_callback('/(?<='.$previousChar.'|\])'.$keywords.'\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
472
+ }
473
+
474
+ /**
475
+ * Replaces true & false by !0 and !1.
476
+ *
477
+ * @param string $content
478
+ *
479
+ * @return string
480
+ */
481
+ protected function shortenBools($content)
482
+ {
483
+ /*
484
+ * 'true' or 'false' could be used as property names (which may be
485
+ * followed by whitespace) - we must not replace those!
486
+ * Since PHP doesn't allow variable-length (to account for the
487
+ * whitespace) lookbehind assertions, I need to capture the leading
488
+ * character and check if it's a `.`
489
+ */
490
+ $callback = function ($match) {
491
+ if (trim($match[1]) === '.') {
492
+ return $match[0];
493
+ }
494
+
495
+ return $match[1].($match[2] === 'true' ? '!0' : '!1');
496
+ };
497
+ $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
498
+
499
+ // for(;;) is exactly the same as while(true), but shorter :)
500
+ $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
501
+
502
+ // now make sure we didn't turn any do ... while(true) into do ... for(;;)
503
+ preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
504
+
505
+ // go backward to make sure positional offsets aren't altered when $content changes
506
+ $dos = array_reverse($dos);
507
+ foreach ($dos as $do) {
508
+ $offsetDo = $do[0][1];
509
+
510
+ // find all `while` (now `for`) following `do`: one of those must be
511
+ // associated with the `do` and be turned back into `while`
512
+ preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
513
+ foreach ($whiles as $while) {
514
+ $offsetWhile = $while[0][1];
515
+
516
+ $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
517
+ $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
518
+ if ($open === $close) {
519
+ // only restore `while` if amount of `{` and `}` are the same;
520
+ // otherwise, that `for` isn't associated with this `do`
521
+ $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
522
+ break;
523
+ }
524
+ }
525
+ }
526
+
527
+ return $content;
528
+ }
529
+ }
libs/matthiasmullie/minify/src/Minify.php ADDED
@@ -0,0 +1,434 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\Minify;
4
+
5
+ use MatthiasMullie\Minify\Exceptions\IOException;
6
+ use Psr\Cache\CacheItemInterface;
7
+
8
+ /**
9
+ * Abstract minifier class.
10
+ *
11
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
12
+ *
13
+ * @author Matthias Mullie <minify@mullie.eu>
14
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
15
+ * @license MIT License
16
+ */
17
+ abstract class Minify
18
+ {
19
+ /**
20
+ * The data to be minified.
21
+ *
22
+ * @var string[]
23
+ */
24
+ protected $data = array();
25
+
26
+ /**
27
+ * Array of patterns to match.
28
+ *
29
+ * @var string[]
30
+ */
31
+ protected $patterns = array();
32
+
33
+ /**
34
+ * This array will hold content of strings and regular expressions that have
35
+ * been extracted from the JS source code, so we can reliably match "code",
36
+ * without having to worry about potential "code-like" characters inside.
37
+ *
38
+ * @var string[]
39
+ */
40
+ public $extracted = array();
41
+
42
+ /**
43
+ * Init the minify class - optionally, code may be passed along already.
44
+ */
45
+ public function __construct(/* $data = null, ... */)
46
+ {
47
+ // it's possible to add the source through the constructor as well ;)
48
+ if (func_num_args()) {
49
+ call_user_func_array(array($this, 'add'), func_get_args());
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Add a file or straight-up code to be minified.
55
+ *
56
+ * @param string|string[] $data
57
+ *
58
+ * @return static
59
+ */
60
+ public function add($data /* $data = null, ... */)
61
+ {
62
+ // bogus "usage" of parameter $data: scrutinizer warns this variable is
63
+ // not used (we're using func_get_args instead to support overloading),
64
+ // but it still needs to be defined because it makes no sense to have
65
+ // this function without argument :)
66
+ $args = array($data) + func_get_args();
67
+
68
+ // this method can be overloaded
69
+ foreach ($args as $data) {
70
+ if (is_array($data)) {
71
+ call_user_func_array(array($this, 'add'), $data);
72
+ continue;
73
+ }
74
+
75
+ // redefine var
76
+ $data = (string) $data;
77
+
78
+ // load data
79
+ $value = $this->load($data);
80
+ $key = ($data != $value) ? $data : count($this->data);
81
+
82
+ // replace CR linefeeds etc.
83
+ // @see https://github.com/matthiasmullie/minify/pull/139
84
+ $value = str_replace(array("\r\n", "\r"), "\n", $value);
85
+
86
+ // store data
87
+ $this->data[$key] = $value;
88
+ }
89
+
90
+ return $this;
91
+ }
92
+
93
+ /**
94
+ * Minify the data & (optionally) saves it to a file.
95
+ *
96
+ * @param string[optional] $path Path to write the data to
97
+ *
98
+ * @return string The minified data
99
+ */
100
+ public function minify($path = null)
101
+ {
102
+ $content = $this->execute($path);
103
+
104
+ // save to path
105
+ if ($path !== null) {
106
+ $this->save($content, $path);
107
+ }
108
+
109
+ return $content;
110
+ }
111
+
112
+ /**
113
+ * Minify & gzip the data & (optionally) saves it to a file.
114
+ *
115
+ * @param string[optional] $path Path to write the data to
116
+ * @param int[optional] $level Compression level, from 0 to 9
117
+ *
118
+ * @return string The minified & gzipped data
119
+ */
120
+ public function gzip($path = null, $level = 9)
121
+ {
122
+ $content = $this->execute($path);
123
+ $content = gzencode($content, $level, FORCE_GZIP);
124
+
125
+ // save to path
126
+ if ($path !== null) {
127
+ $this->save($content, $path);
128
+ }
129
+
130
+ return $content;
131
+ }
132
+
133
+ /**
134
+ * Minify the data & write it to a CacheItemInterface object.
135
+ *
136
+ * @param CacheItemInterface $item Cache item to write the data to
137
+ *
138
+ * @return CacheItemInterface Cache item with the minifier data
139
+ */
140
+ public function cache(CacheItemInterface $item)
141
+ {
142
+ $content = $this->execute();
143
+ $item->set($content);
144
+
145
+ return $item;
146
+ }
147
+
148
+ /**
149
+ * Minify the data.
150
+ *
151
+ * @param string[optional] $path Path to write the data to
152
+ *
153
+ * @return string The minified data
154
+ */
155
+ abstract public function execute($path = null);
156
+
157
+ /**
158
+ * Load data.
159
+ *
160
+ * @param string $data Either a path to a file or the content itself
161
+ *
162
+ * @return string
163
+ */
164
+ protected function load($data)
165
+ {
166
+ // check if the data is a file
167
+ if ($this->canImportFile($data)) {
168
+ $data = file_get_contents($data);
169
+
170
+ // strip BOM, if any
171
+ if (substr($data, 0, 3) == "\xef\xbb\xbf") {
172
+ $data = substr($data, 3);
173
+ }
174
+ }
175
+
176
+ return $data;
177
+ }
178
+
179
+ /**
180
+ * Save to file.
181
+ *
182
+ * @param string $content The minified data
183
+ * @param string $path The path to save the minified data to
184
+ *
185
+ * @throws IOException
186
+ */
187
+ protected function save($content, $path)
188
+ {
189
+ $handler = $this->openFileForWriting($path);
190
+
191
+ $this->writeToFile($handler, $content);
192
+
193
+ @fclose($handler);
194
+ }
195
+
196
+ /**
197
+ * Register a pattern to execute against the source content.
198
+ *
199
+ * @param string $pattern PCRE pattern
200
+ * @param string|callable $replacement Replacement value for matched pattern
201
+ */
202
+ protected function registerPattern($pattern, $replacement = '')
203
+ {
204
+ // study the pattern, we'll execute it more than once
205
+ $pattern .= 'S';
206
+
207
+ $this->patterns[] = array($pattern, $replacement);
208
+ }
209
+
210
+ /**
211
+ * We can't "just" run some regular expressions against JavaScript: it's a
212
+ * complex language. E.g. having an occurrence of // xyz would be a comment,
213
+ * unless it's used within a string. Of you could have something that looks
214
+ * like a 'string', but inside a comment.
215
+ * The only way to accurately replace these pieces is to traverse the JS one
216
+ * character at a time and try to find whatever starts first.
217
+ *
218
+ * @param string $content The content to replace patterns in
219
+ *
220
+ * @return string The (manipulated) content
221
+ */
222
+ protected function replace($content)
223
+ {
224
+ $processed = '';
225
+ $positions = array_fill(0, count($this->patterns), -1);
226
+ $matches = array();
227
+
228
+ while ($content) {
229
+ // find first match for all patterns
230
+ foreach ($this->patterns as $i => $pattern) {
231
+ list($pattern, $replacement) = $pattern;
232
+
233
+ // no need to re-run matches that are still in the part of the
234
+ // content that hasn't been processed
235
+ if ($positions[$i] >= 0) {
236
+ continue;
237
+ }
238
+
239
+ $match = null;
240
+ if (preg_match($pattern, $content, $match)) {
241
+ $matches[$i] = $match;
242
+
243
+ // we'll store the match position as well; that way, we
244
+ // don't have to redo all preg_matches after changing only
245
+ // the first (we'll still know where those others are)
246
+ $positions[$i] = strpos($content, $match[0]);
247
+ } else {
248
+ // if the pattern couldn't be matched, there's no point in
249
+ // executing it again in later runs on this same content;
250
+ // ignore this one until we reach end of content
251
+ unset($matches[$i]);
252
+ $positions[$i] = strlen($content);
253
+ }
254
+ }
255
+
256
+ // no more matches to find: everything's been processed, break out
257
+ if (!$matches) {
258
+ $processed .= $content;
259
+ break;
260
+ }
261
+
262
+ // see which of the patterns actually found the first thing (we'll
263
+ // only want to execute that one, since we're unsure if what the
264
+ // other found was not inside what the first found)
265
+ $discardLength = min($positions);
266
+ $firstPattern = array_search($discardLength, $positions);
267
+ $match = $matches[$firstPattern][0];
268
+
269
+ // execute the pattern that matches earliest in the content string
270
+ list($pattern, $replacement) = $this->patterns[$firstPattern];
271
+ $replacement = $this->replacePattern($pattern, $replacement, $content);
272
+
273
+ // figure out which part of the string was unmatched; that's the
274
+ // part we'll execute the patterns on again next
275
+ $content = substr($content, $discardLength);
276
+ $unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
277
+
278
+ // move the replaced part to $processed and prepare $content to
279
+ // again match batch of patterns against
280
+ $processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
281
+ $content = $unmatched;
282
+
283
+ // first match has been replaced & that content is to be left alone,
284
+ // the next matches will start after this replacement, so we should
285
+ // fix their offsets
286
+ foreach ($positions as $i => $position) {
287
+ $positions[$i] -= $discardLength + strlen($match);
288
+ }
289
+ }
290
+
291
+ return $processed;
292
+ }
293
+
294
+ /**
295
+ * This is where a pattern is matched against $content and the matches
296
+ * are replaced by their respective value.
297
+ * This function will be called plenty of times, where $content will always
298
+ * move up 1 character.
299
+ *
300
+ * @param string $pattern Pattern to match
301
+ * @param string|callable $replacement Replacement value
302
+ * @param string $content Content to match pattern against
303
+ *
304
+ * @return string
305
+ */
306
+ protected function replacePattern($pattern, $replacement, $content)
307
+ {
308
+ if (is_callable($replacement)) {
309
+ return preg_replace_callback($pattern, $replacement, $content, 1, $count);
310
+ } else {
311
+ return preg_replace($pattern, $replacement, $content, 1, $count);
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Strings are a pattern we need to match, in order to ignore potential
317
+ * code-like content inside them, but we just want all of the string
318
+ * content to remain untouched.
319
+ *
320
+ * This method will replace all string content with simple STRING#
321
+ * placeholder text, so we've rid all strings from characters that may be
322
+ * misinterpreted. Original string content will be saved in $this->extracted
323
+ * and after doing all other minifying, we can restore the original content
324
+ * via restoreStrings().
325
+ *
326
+ * @param string[optional] $chars
327
+ */
328
+ protected function extractStrings($chars = '\'"')
329
+ {
330
+ // PHP only supports $this inside anonymous functions since 5.4
331
+ $minifier = $this;
332
+ $callback = function ($match) use ($minifier) {
333
+ // check the second index here, because the first always contains a quote
334
+ if ($match[2] === '') {
335
+ /*
336
+ * Empty strings need no placeholder; they can't be confused for
337
+ * anything else anyway.
338
+ * But we still needed to match them, for the extraction routine
339
+ * to skip over this particular string.
340
+ */
341
+ return $match[0];
342
+ }
343
+
344
+ $count = count($minifier->extracted);
345
+ $placeholder = $match[1].$count.$match[1];
346
+ $minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
347
+
348
+ return $placeholder;
349
+ };
350
+
351
+ /*
352
+ * The \\ messiness explained:
353
+ * * Don't count ' or " as end-of-string if it's escaped (has backslash
354
+ * in front of it)
355
+ * * Unless... that backslash itself is escaped (another leading slash),
356
+ * in which case it's no longer escaping the ' or "
357
+ * * So there can be either no backslash, or an even number
358
+ * * multiply all of that times 4, to account for the escaping that has
359
+ * to be done to pass the backslash into the PHP string without it being
360
+ * considered as escape-char (times 2) and to get it in the regex,
361
+ * escaped (times 2)
362
+ */
363
+ $this->registerPattern('/(['.$chars.'])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
364
+ }
365
+
366
+ /**
367
+ * This method will restore all extracted data (strings, regexes) that were
368
+ * replaced with placeholder text in extract*(). The original content was
369
+ * saved in $this->extracted.
370
+ *
371
+ * @param string $content
372
+ *
373
+ * @return string
374
+ */
375
+ protected function restoreExtractedData($content)
376
+ {
377
+ if (!$this->extracted) {
378
+ // nothing was extracted, nothing to restore
379
+ return $content;
380
+ }
381
+
382
+ $content = strtr($content, $this->extracted);
383
+
384
+ $this->extracted = array();
385
+
386
+ return $content;
387
+ }
388
+
389
+ /**
390
+ * Check if the path is a regular file and can be read.
391
+ *
392
+ * @param string $path
393
+ *
394
+ * @return bool
395
+ */
396
+ protected function canImportFile($path)
397
+ {
398
+ return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
399
+ }
400
+
401
+ /**
402
+ * Attempts to open file specified by $path for writing.
403
+ *
404
+ * @param string $path The path to the file
405
+ *
406
+ * @return resource Specifier for the target file
407
+ *
408
+ * @throws IOException
409
+ */
410
+ protected function openFileForWriting($path)
411
+ {
412
+ if (($handler = @fopen($path, 'w')) === false) {
413
+ throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
414
+ }
415
+
416
+ return $handler;
417
+ }
418
+
419
+ /**
420
+ * Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
421
+ *
422
+ * @param resource $handler The resource to write to
423
+ * @param string $content The content to write
424
+ * @param string $path The path to the file (for exception printing only)
425
+ *
426
+ * @throws IOException
427
+ */
428
+ protected function writeToFile($handler, $content, $path = '')
429
+ {
430
+ if (($result = @fwrite($handler, $content)) === false || ($result < strlen($content))) {
431
+ throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
432
+ }
433
+ }
434
+ }
libs/matthiasmullie/minify/src/index.html ADDED
File without changes
libs/matthiasmullie/path-converter/index.html ADDED
File without changes
libs/matthiasmullie/path-converter/src/Converter.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\PathConverter;
4
+
5
+ /**
6
+ * Convert paths relative from 1 file to another.
7
+ *
8
+ * E.g.
9
+ * ../../images/icon.jpg relative to /css/imports/icons.css
10
+ * becomes
11
+ * ../images/icon.jpg relative to /css/minified.css
12
+ *
13
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
14
+ *
15
+ * @author Matthias Mullie <pathconverter@mullie.eu>
16
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
17
+ * @license MIT License
18
+ */
19
+ class Converter implements ConverterInterface
20
+ {
21
+ /**
22
+ * @var string
23
+ */
24
+ protected $from;
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ protected $to;
30
+
31
+ /**
32
+ * @param string $from The original base path (directory, not file!)
33
+ * @param string $to The new base path (directory, not file!)
34
+ */
35
+ public function __construct($from, $to)
36
+ {
37
+ $shared = $this->shared($from, $to);
38
+ if ($shared === '') {
39
+ // when both paths have nothing in common, one of them is probably
40
+ // absolute while the other is relative
41
+ $cwd = getcwd();
42
+ $from = strpos($from, $cwd) === 0 ? $from : $cwd.'/'.$from;
43
+ $to = strpos($to, $cwd) === 0 ? $to : $cwd.'/'.$to;
44
+
45
+ // or traveling the tree via `..`
46
+ // attempt to resolve path, or assume it's fine if it doesn't exist
47
+ $from = realpath($from) ?: $from;
48
+ $to = realpath($to) ?: $to;
49
+ }
50
+
51
+ $from = $this->dirname($from);
52
+ $to = $this->dirname($to);
53
+
54
+ $from = $this->normalize($from);
55
+ $to = $this->normalize($to);
56
+
57
+ $this->from = $from;
58
+ $this->to = $to;
59
+ }
60
+
61
+ /**
62
+ * Normalize path.
63
+ *
64
+ * @param string $path
65
+ *
66
+ * @return string
67
+ */
68
+ protected function normalize($path)
69
+ {
70
+ // deal with different operating systems' directory structure
71
+ $path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
72
+
73
+ /*
74
+ * Example:
75
+ * /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif
76
+ * to
77
+ * /home/forkcms/frontend/core/layout/images/img.gif
78
+ */
79
+ do {
80
+ $path = preg_replace('/[^\/]+(?<!\.\.)\/\.\.\//', '', $path, -1, $count);
81
+ } while ($count);
82
+
83
+ return $path;
84
+ }
85
+
86
+ /**
87
+ * Figure out the shared path of 2 locations.
88
+ *
89
+ * Example:
90
+ * /home/forkcms/frontend/core/layout/images/img.gif
91
+ * and
92
+ * /home/forkcms/frontend/cache/minified_css
93
+ * share
94
+ * /home/forkcms/frontend
95
+ *
96
+ * @param string $path1
97
+ * @param string $path2
98
+ *
99
+ * @return string
100
+ */
101
+ protected function shared($path1, $path2)
102
+ {
103
+ // $path could theoretically be empty (e.g. no path is given), in which
104
+ // case it shouldn't expand to array(''), which would compare to one's
105
+ // root /
106
+ $path1 = $path1 ? explode('/', $path1) : array();
107
+ $path2 = $path2 ? explode('/', $path2) : array();
108
+
109
+ $shared = array();
110
+
111
+ // compare paths & strip identical ancestors
112
+ foreach ($path1 as $i => $chunk) {
113
+ if (isset($path2[$i]) && $path1[$i] == $path2[$i]) {
114
+ $shared[] = $chunk;
115
+ } else {
116
+ break;
117
+ }
118
+ }
119
+
120
+ return implode('/', $shared);
121
+ }
122
+
123
+ /**
124
+ * Convert paths relative from 1 file to another.
125
+ *
126
+ * E.g.
127
+ * ../images/img.gif relative to /home/forkcms/frontend/core/layout/css
128
+ * should become:
129
+ * ../../core/layout/images/img.gif relative to
130
+ * /home/forkcms/frontend/cache/minified_css
131
+ *
132
+ * @param string $path The relative path that needs to be converted
133
+ *
134
+ * @return string The new relative path
135
+ */
136
+ public function convert($path)
137
+ {
138
+ // quit early if conversion makes no sense
139
+ if ($this->from === $this->to) {
140
+ return $path;
141
+ }
142
+
143
+ $path = $this->normalize($path);
144
+ // if we're not dealing with a relative path, just return absolute
145
+ if (strpos($path, '/') === 0) {
146
+ return $path;
147
+ }
148
+
149
+ // normalize paths
150
+ $path = $this->normalize($this->from.'/'.$path);
151
+
152
+ // strip shared ancestor paths
153
+ $shared = $this->shared($path, $this->to);
154
+ $path = mb_substr($path, mb_strlen($shared));
155
+ $to = mb_substr($this->to, mb_strlen($shared));
156
+
157
+ // add .. for every directory that needs to be traversed to new path
158
+ $to = str_repeat('../', mb_substr_count($to, '/'));
159
+
160
+ return $to.ltrim($path, '/');
161
+ }
162
+
163
+ /**
164
+ * Attempt to get the directory name from a path.
165
+ *
166
+ * @param string $path
167
+ *
168
+ * @return string
169
+ */
170
+ protected function dirname($path)
171
+ {
172
+ if (is_file($path)) {
173
+ return dirname($path);
174
+ }
175
+
176
+ if (is_dir($path)) {
177
+ return rtrim($path, '/');
178
+ }
179
+
180
+ // no known file/dir, start making assumptions
181
+
182
+ // ends in / = dir
183
+ if (mb_substr($path, -1) === '/') {
184
+ return rtrim($path, '/');
185
+ }
186
+
187
+ // has a dot in the name, likely a file
188
+ if (preg_match('/.*\..*$/', basename($path)) !== 0) {
189
+ return dirname($path);
190
+ }
191
+
192
+ // you're on your own here!
193
+ return $path;
194
+ }
195
+ }
libs/matthiasmullie/path-converter/src/ConverterInterface.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\PathConverter;
4
+
5
+ /**
6
+ * Convert file paths.
7
+ *
8
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
+ *
10
+ * @author Matthias Mullie <pathconverter@mullie.eu>
11
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
+ * @license MIT License
13
+ */
14
+ interface ConverterInterface
15
+ {
16
+ /**
17
+ * Convert file paths.
18
+ *
19
+ * @param string $path The path to be converted
20
+ *
21
+ * @return string The new path
22
+ */
23
+ public function convert($path);
24
+ }
libs/matthiasmullie/path-converter/src/NoConverter.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\PathConverter;
4
+
5
+ /**
6
+ * Don't convert paths.
7
+ *
8
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
+ *
10
+ * @author Matthias Mullie <pathconverter@mullie.eu>
11
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
+ * @license MIT License
13
+ */
14
+ class NoConverter implements ConverterInterface
15
+ {
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ public function convert($path)
20
+ {
21
+ return $path;
22
+ }
23
+ }
libs/matthiasmullie/path-converter/src/index.html ADDED
File without changes
libs/mrclay/HTML.php ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* https://github.com/mrclay/minify/blob/2.x/min/lib/Minify/HTML.php - same as WP ROCKET v2.91 + custom changes */
4
+ # line:
5
+
6
+ class fastvelocity_min_Minify_HTML {
7
+ protected $_jsCleanComments = true;
8
+
9
+ /**
10
+ * "Minify" an HTML page
11
+ *
12
+ * @param string $html
13
+ *
14
+ * @param array $options
15
+ *
16
+ * 'cssMinifier' : (optional) callback function to process content of STYLE
17
+ * elements.
18
+ *
19
+ * 'jsMinifier' : (optional) callback function to process content of SCRIPT
20
+ * elements. Note: the type attribute is ignored.
21
+ *
22
+ * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
23
+ * unset, minify will sniff for an XHTML doctype.
24
+ *
25
+ * @return string
26
+ */
27
+ public static function minify($html, $options = array()) {
28
+ $min = new self($html, $options);
29
+ return $min->process();
30
+ }
31
+
32
+
33
+ /**
34
+ * Create a minifier object
35
+ *
36
+ * @param string $html
37
+ *
38
+ * @param array $options
39
+ *
40
+ * 'cssMinifier' : (optional) callback function to process content of STYLE
41
+ * elements.
42
+ *
43
+ * 'jsMinifier' : (optional) callback function to process content of SCRIPT
44
+ * elements. Note: the type attribute is ignored.
45
+ *
46
+ * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
47
+ *
48
+ * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
49
+ * unset, minify will sniff for an XHTML doctype.
50
+ */
51
+ public function __construct($html, $options = array())
52
+ {
53
+ $this->_html = str_replace("\r\n", "\n", trim($html));
54
+ if (isset($options['xhtml'])) {
55
+ $this->_isXhtml = (bool)$options['xhtml'];
56
+ }
57
+ if (isset($options['cssMinifier'])) {
58
+ $this->_cssMinifier = $options['cssMinifier'];
59
+ }
60
+ if (isset($options['jsMinifier'])) {
61
+ $this->_jsMinifier = $options['jsMinifier'];
62
+ }
63
+ if (isset($options['jsCleanComments'])) {
64
+ $this->_jsCleanComments = (bool)$options['jsCleanComments'];
65
+ }
66
+ }
67
+
68
+
69
+ /**
70
+ * Minify the markeup given in the constructor
71
+ *
72
+ * @return string
73
+ */
74
+ public function process()
75
+ {
76
+ if ($this->_isXhtml === null) {
77
+ $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
78
+ }
79
+
80
+ $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
81
+ $this->_placeholders = array();
82
+
83
+ // replace SCRIPTs (and minify) with placeholders
84
+ $this->_html = preg_replace_callback(
85
+ '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
86
+ ,array($this, '_removeScriptCB')
87
+ ,$this->_html);
88
+
89
+ // replace STYLEs (and minify) with placeholders
90
+ $this->_html = preg_replace_callback(
91
+ '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
92
+ ,array($this, '_removeStyleCB')
93
+ ,$this->_html);
94
+
95
+ // remove HTML comments (not containing IE conditional comments).
96
+ global $strip_htmlcomments;
97
+ if($strip_htmlcomments) {
98
+ $this->_html = preg_replace_callback('/<!--([\\s\\S]*?)-->/' ,array($this, '_commentCB') ,$this->_html);
99
+ }
100
+
101
+ // replace PREs with placeholders
102
+ $this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
103
+ ,array($this, '_removePreCB')
104
+ ,$this->_html);
105
+
106
+ // replace TEXTAREAs with placeholders
107
+ $this->_html = preg_replace_callback(
108
+ '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
109
+ ,array($this, '_removeTextareaCB')
110
+ ,$this->_html);
111
+
112
+ // trim each line.
113
+ // @todo take into account attribute values that span multiple lines.
114
+ $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
115
+
116
+ // remove ws around block/undisplayed elements
117
+ $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
118
+ .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
119
+ .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
120
+ .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
121
+ .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
122
+
123
+ // remove ws outside of all elements
124
+ $this->_html = preg_replace_callback(
125
+ '/>([^<]+)</'
126
+ ,array($this, '_outsideTagCB')
127
+ ,$this->_html);
128
+
129
+ // use newlines before 1st attribute in open tags (to limit line lengths)
130
+ //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
131
+
132
+ // fill placeholders
133
+ $this->_html = str_replace(
134
+ array_keys($this->_placeholders)
135
+ ,array_values($this->_placeholders)
136
+ ,$this->_html
137
+ );
138
+ // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
139
+ $this->_html = str_replace(
140
+ array_keys($this->_placeholders)
141
+ ,array_values($this->_placeholders)
142
+ ,$this->_html
143
+ );
144
+ return $this->_html;
145
+ }
146
+
147
+ protected function _commentCB($m)
148
+ {
149
+ return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<![') || 0 === strpos($m[1], 'esi')) ? $m[0] : '';
150
+ }
151
+
152
+ protected function _reservePlace($content)
153
+ {
154
+ $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
155
+ $this->_placeholders[$placeholder] = $content;
156
+ return $placeholder;
157
+ }
158
+
159
+ protected $_isXhtml = null;
160
+ protected $_replacementHash = null;
161
+ protected $_placeholders = array();
162
+ protected $_cssMinifier = null;
163
+ protected $_jsMinifier = null;
164
+
165
+ protected function _outsideTagCB($m)
166
+ {
167
+ return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
168
+ }
169
+
170
+ protected function _removePreCB($m)
171
+ {
172
+ return $this->_reservePlace("<pre{$m[1]}");
173
+ }
174
+
175
+ protected function _removeTextareaCB($m)
176
+ {
177
+ return $this->_reservePlace("<textarea{$m[1]}");
178
+ }
179
+
180
+ protected function _removeStyleCB($m)
181
+ {
182
+ $openStyle = "<style{$m[1]}";
183
+ $css = $m[2];
184
+ // remove css comments
185
+ $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
186
+
187
+ // remove CDATA section markers
188
+ $css = $this->_removeCdata($css);
189
+
190
+ // minify
191
+ $minifier = $this->_cssMinifier ? $this->_cssMinifier : 'trim';
192
+ $css = call_user_func($minifier, $css);
193
+ $css = preg_replace('/\s+/', ' ', $css);
194
+
195
+ return $this->_reservePlace($this->_needsCdata($css)
196
+ ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
197
+ : "{$openStyle}{$css}</style>"
198
+ );
199
+ }
200
+
201
+ protected function _removeScriptCB($m)
202
+ {
203
+ $openScript = "<script{$m[2]}";
204
+ $js = $m[3];
205
+
206
+ // whitespace surrounding? preserve at least one space
207
+ $ws1 = ($m[1] === '') ? '' : ' ';
208
+ $ws2 = ($m[4] === '') ? '' : ' ';
209
+
210
+ // remove HTML comments (and ending "//" if present)
211
+ if ($this->_jsCleanComments) {
212
+ $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
213
+ }
214
+
215
+ // remove CDATA section markers
216
+ $js = $this->_removeCdata($js);
217
+
218
+ // minify
219
+ $minifier = $this->_jsMinifier
220
+ ? $this->_jsMinifier
221
+ : 'trim';
222
+ $js = call_user_func($minifier, $js);
223
+
224
+ return $this->_reservePlace($this->_needsCdata($js)
225
+ ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
226
+ : "{$ws1}{$openScript}{$js}</script>{$ws2}"
227
+ );
228
+ }
229
+
230
+ protected function _removeCdata($str)
231
+ {
232
+ return (false !== strpos($str, '<![CDATA['))
233
+ ? str_replace(array('<![CDATA[', ']]>'), '', $str)
234
+ : $str;
235
+ }
236
+
237
+ protected function _needsCdata($str)
238
+ {
239
+ return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
240
+ }
241
+ }
libs/mrclay/index.html ADDED
File without changes
readme.txt ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Fast Velocity Minify ===
2
+ Contributors: Alignak
3
+ Tags: merge, combine, concatenate, PHP Minify, YUI Compressor, CSS, javascript, JS, minification, minify, optimization, optimize, stylesheet, aggregate, cache, CSS, html, minimize, pagespeed, performance, speed, GTmetrix, pingdom
4
+ Requires at least: 4.5
5
+ Stable tag: 2.2.0
6
+ Tested up to: 4.8.1
7
+ License: GPLv3 or later
8
+ License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
+
10
+ Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by merging and minifying CSS, JavaScript and HTML.
11
+
12
+
13
+ == Description ==
14
+
15
+ This plugin reduces HTTP requests by merging CSS & Javascript files into groups of files, while attempting to use the least amount of files as possible. It minifies CSS and JS files with PHP Minify (no extra requirements).
16
+
17
+ Minification is done in real time and done on the frontend only during the first uncached request. Once the first request is processed, any other pages that require the same set of CSS and JavaScript will be served that same static cache file.
18
+
19
+ This plugin includes options for developers and advanced users, however the default settings should work just fine for most sites.
20
+
21
+ = Aditional Optimization =
22
+
23
+ I can offer you aditional `custom made` optimization on top of this plugin. If you would like to hire me, please visit my profile links for further information.
24
+
25
+
26
+ = Features =
27
+
28
+ * Merge JS and CSS files into groups to reduce the number of HTTP requests
29
+ * Google Fonts merging and optimization
30
+ * Handles scripts loaded both in the header & footer separately
31
+ * Keeps the order of the scripts even if you exclude some files from minification
32
+ * Supports localized scripts (https://codex.wordpress.org/Function_Reference/wp_localize_script)
33
+ * Minifies CSS and JS with PHP Minify only, no third party software or libraries needed.
34
+ * Option to use YUI Compressor rather than PHP Minify (you you have "exec" and "java" available on your system).
35
+ * Option to defer JavaScript and CSS files.
36
+ * Stores the cache files in the uploads directory.
37
+ * View the status and logs on the WordPress admin page.
38
+ * Option to Minify HTML for further improvements.
39
+ * Ability to turn off minification
40
+ * Ability to turn off CSS or JS optimization seperatly (by disabling either css or js processing)
41
+ * Ability to manually ignore scripts or css
42
+ * Support for conditional scripts and styles
43
+ * Support for multisite installations
44
+ * Support for gzip_static on Nginx
45
+ * Support for the CDN Enabler plugin
46
+ * Auto purging of cache files on W3 Total Cache, WP Supercache, WP Rocket, Wp Fastest Cache, Cachify, Comet Cache, Zen Cache, LiteSpeed Cache, Nginx Cache (by Till Krüss ), SG Optimizer, Godaddy Managed WordPress Hosting and WP Engine (read the FAQs)
47
+ * Support for preconnect and preload headers
48
+ * and some more...
49
+
50
+
51
+ = Notes =
52
+ * The JavaScript minification is by [PHP Minify](https://github.com/matthiasmullie/minify)
53
+ * The alternative JavaScript minification is by [YUI Compressor](http://yui.github.io/yuicompressor/)
54
+ * Compatible with Nginx, HHVM and PHP 7.
55
+ * Minimum requirements are PHP 5.5 and WP 4.4, from version 1.4.0 onwards
56
+
57
+
58
+ == Installation ==
59
+
60
+ 1. Upload the plugin folder to the `/wp-content/plugins/` directory or upload the zip within WordPress
61
+ 2. Activate the plugin through the `Plugins` menu in WordPress
62
+ 3. Configure the options under: `Settings > Fast Velocity Minify` and that's it.
63
+
64
+
65
+ == Screenshots ==
66
+
67
+ 1. You can view the logs and purge the cache files.
68
+ 2. The settings page.
69
+
70
+
71
+ == Frequently Asked Questions ==
72
+
73
+ = How can I exclude certain assets by wildcard? =
74
+
75
+ By default, each line on the ignore list will try to match a substring against all css or js urls, for example: `//yoursite.com/wp-content/plugins/some-plugin/js/` will ignore all files inside that directory. You can also shorten the url like `/some-plugin/js/` and then it will match any css or js url that has `/some-plugin/js/` on the path. Obviously, doing `/js/` would match any files inside any "/js/" directory and in any location, so to avoid unexpected situations please always use the longest, most specific path you can use.
76
+
77
+ ...
78
+
79
+ = Why is the ignore list not working? =
80
+
81
+ The ignore list is working but you need to remove query vars from static urls (ex: ?ver=) and use partial (see wildcard help above) and use relative urls only.
82
+
83
+ ...
84
+
85
+
86
+ = Why are there several or a lot's of js and css files listed on the status page? =
87
+
88
+ Those files are created whenever a new set of javascript or css files are found on your front end and it's due to your plugins and themes needing different js and css files per page, post, category, tag, homepage or even custom post types. If you always load the exact same css and javascript in every page on your site, you won't see as many files. Likewise, if you have some dynamic url for css or js that always changes in each pageview, you should add it to the ignore list.
89
+
90
+ ...
91
+
92
+ = Can I update other plugins and themes? =
93
+
94
+ Yes, but it's recommended that you purge the cached files (from the plugin status page) in order for the merging and minification cache files to be regenerated. The plugin will try to automatically purge some popular cache plugins. We still recommend, however, that you purge all caches on your cache plugin (whatever you use) "after" purging Fast Velocity Minify cache.
95
+
96
+ ...
97
+
98
+ = Is it compatible with other caching plugins? =
99
+
100
+ The plugin will try to automatically purge several popular cache plugins, however we still recommend you to purge all caches (on whatever you use) if you also manually purge the cache on the plugin settings for some reason.
101
+ The automatic purge is active for the following plugins and hosting: W3 Total Cache, WP Supercache, WP Rocket, Wp Fastest Cache, Cachify, Comet Cache, Zen Cache, LiteSpeed Cache, SG Optimizer, Godaddy Managed WordPress Hosting and WP Engine
102
+
103
+ ...
104
+
105
+ = Is it resource intensive, or will it use too much CPU on my shared hosting plan? =
106
+
107
+ No it's not. The generation of the minified files is done only once per group of CSS or JS files (and only if needed). All pages that request the same group of CSS or JS files will also make use of that cache file. The cache file will be served from the uploads directory as a static file and there is no PHP involved.
108
+
109
+ ...
110
+
111
+ = Is it compatible with multisites? =
112
+
113
+ Yes, it generates a new cache file for every different set of JS and CSS requirements it finds.
114
+
115
+ ...
116
+
117
+ = Is it compatible with Adsense and other ad networks? =
118
+
119
+ The plugin is compatible with any add network but also depends on how you're loading the ads into the site. We only merge and minify css and javascript files enqueued in the header and footer which would exclude any ads. If you're using a plugin that uses JS to insert the ads on the page, there could be issues. Please report on the support forum if you found such case.
120
+
121
+ ...
122
+
123
+ = After installing, why did my site became slow? =
124
+
125
+ Please note that the cache regeration happen's once per page or if the requested CSS + JS files change. If you need the same set of CSS and JS files in every page, the cache file will only be generated once and reused for all other pages. If you have a CSS or JS that uses a different name on every pageview, try to add it to the ignore list by using wildcards.
126
+
127
+ ...
128
+
129
+ = How do I use the precompressed files with gzip_static on Nginx? =
130
+
131
+ When we merge and minify the css and js files, we also create a `.gz` file to be used with `gzip_static` on Nginx. You need to enable this feature on your Nginx configuration file if you want to make use of it. If you're upgrading from 1.2.3 or earlier, you need to clear the plugin cache.
132
+
133
+ ...
134
+
135
+ = Where is the YUI Compressor option gone to? =
136
+
137
+ This functionality depends on wheter you have exec and java available on your system. It will be visible on the basic Settings page under the JavaScript Options section and it only applies to JS files.
138
+
139
+ ...
140
+
141
+ = After installing, why is my design or layout broken or some images and sliders are not showing? =
142
+
143
+ First thing to check is, are you doing double minification?
144
+ You must disable any features on your theme and other plugins, that perform minification of css, html and js.
145
+ You cannot also have other optimization plugins, because you are forcing wordpress to do double work.
146
+ I recommend W3 Total Cache (with css, js and html minifcation disabled) + Fast Velocity Minify, for minification of CSS and JS files.
147
+ Also, kindly review the "Why are some of the CSS and JS files not being merged or why is my layout broken ?" below for better insights.
148
+
149
+ Additionally, the Advanced Otions should only be used by advanced users or developers that understand what the settings mean and do, especially the "defer JS" options. Having said that, this is how you can solve most issues on the settings tab when not using any options on the advanced tab:
150
+
151
+ * `Disabling CSS processing but keeping JS processing enabled:` This will leave CSS files alone and it's useful to determine if the problem is CSS or JS related. If isabling CSS processing fixed the layout problems, now you know it's related to CSS...
152
+ Likewise, you can keep CSS processing and disable JS processing to find out if the problem is on JS processing.
153
+ If you determine it's a CSS issue, check the log file on the status page for possible css file urls that should not be there (such as Internet Explorer only files) and add them to the ignore list.
154
+ Also kindly report those files on the support forum so they can be blacklisted for future releases.
155
+ Sometimes there are JS files than conflict with each other when merged so they may need to be excluded too.
156
+
157
+ * `JS Defer:` If you have a theme that heavily relies on javascript and jQuery (usually with sliders, paralax animations, etc) most probably, you cannot `defer JavaScript` for all JS files, but you could try to add the jQuery library as well as the specific JS files that need to be render blocking to the ignore list.
158
+
159
+ * `Developers only:` Note that if you defer jQuery, the library will not run until after the html DOM page loads. That means that whatever jQuery code is inlined on the HTML will not work and trigger an "undefined" error on the browser console log.
160
+ On Google Chrome you can look at the console by pressing CTRL + SHIFT + J on your keyboard and refreshing the page. If there are errors you need to track down which JS file is causing trouble and add it to the ignore list.
161
+ If you have no idea which files to add, I recommend checking the log file on the status page, adding them all to the ignore list and then one by one trying to delete each url until you find the one causing trouble.
162
+ Also beware of any cache plugin in use (and cloudflare) when testing, because that cache needs to be off or purged when configuring things around.
163
+
164
+ ...
165
+
166
+ = Why are some of the CSS and JS files not being merged or why is my layout broken ? =
167
+
168
+ There are thousands of plugins and themes out there and not every developer follows the standard way of enqueueing their files on WordPress.
169
+
170
+ For example, some choose to "print" the html tag directly into the header or footer, rather than to use the official method outlined here: https://developer.wordpress.org/themes/basics/including-css-javascript/
171
+
172
+ Some developers enqueue all files properly but still "print" conditional tags, such as IE only comments around some CSS or JS files , while they should be following the example as explained on the codex: https://developer.wordpress.org/reference/functions/wp_script_add_data/
173
+
174
+ Because "printing CSS and JS tags on the header and footer" is evil (seriously), we cannot capture those files and merge them together.
175
+ There are also files meant to be loaded for IE users only, mobile users, desktop users, printers, so the enqueing of all the files needs to follow the official method of enqueing data and files.
176
+
177
+ This can also cause layout issues because some files that should be for IE only can be merged together with other users, thus overwriting the usual layout (please check if any of those got merged together and add it to the ignore list). Also, to avoid some of these, we have implemented an IE only blacklist of "known" file names that are "always" added to the ignore list behind the scenes.
178
+
179
+ Please feel free to open a support topic if you found some more JS or CSS files on your theme or plugins that "must always" also blacklisted and why.
180
+
181
+ The default ignore list can be found here:
182
+ https://fastvelocity.com/api/fvm/ignore.txt
183
+
184
+ The IE only blacklist (mostly IE only files or that cause trouble most of the times when merged with others), can be found here: https://fastvelocity.com/api/fvm/ie_blacklist.txt
185
+
186
+ These files are downloaded directly by the plugin, once every 24 hours from our cdn provider.
187
+
188
+ ...
189
+
190
+ = Why is it that even though I have disabled or removed the plugin, the design is still broken? =
191
+
192
+ While this is rare, it can happen if you have some sort of cache enabled on your site or server. A cache means that the site or server makes a static copy of your page and serves it for a while (until it's deleted or expires) instead of loading wordpress directly to the users. Some hosting providers such as Godaddy (and their derivates) enforce their own cache plugin to be installed and creates a new menu which allows you to purge the cache.
193
+
194
+ If you don't see any option anywhere to clear your cache, you can contact your hosting provider or developer to clear the cache for you or ask them how you can do it in the future.
195
+
196
+ ...
197
+
198
+ = Why is my frontend editor not working ? =
199
+
200
+ Some plugins and themes need to edit the layout and styles on the frontend. When they need to do that, they enqueue several extra js and css files that are caught by this plugin and get merged together, thus sometimes, it breaks things. If you encounter such issue of your page editor not working on the frontend, kindly enable the "Fix Page Editors" on the Troubleshooting page.
201
+
202
+ ...
203
+
204
+ = What is the "Fix Page Editors" on the Troubleshooting page ? =
205
+
206
+ This hides all optimization from editors and administrators, as long as they are logged in. This also means that you will see the site exactly as it was before installing the plugin and it's meant to fix compatibility with frontend page editors, or plugins that edit things in preview mode using the frontend.
207
+
208
+ ...
209
+
210
+ = Is it compatible with Visual Composer and other editors ? =
211
+
212
+ Visual composer, adds some style tags into your header and/or footer, however they simply print the code and don't use the wordpress `wp_add_inline_style` hook.
213
+ This means, we cannot easily capture that csa code and therefore it's left out of all the merging by Fast Velocity Minify.
214
+ You may have all else merged and minified correctly, however if that generated visual composer css code is important, those styles might be overwritten by the merged file, or that code can also overwrite the rules inside the css generated file.
215
+ If you experience some styles missing, this could be the cause... but try the ignore list first.
216
+
217
+ ...
218
+
219
+ = How should I use the "Preload Images" and what is it for? =
220
+
221
+ Certain themes and plugins, either load large images or sliders on the homepage. Most of them will also load "above the fold" causing the "Prioritize visible content" or the "Eliminate render-blocking JavaScript and CSS in above-the-fold content" message on pagespeed insights (see the previous faq question above).
222
+
223
+ How you can use the "Preload Images" is by adding the url of the first relevant images that load above the fold, such as the first background image (and the first image only) of the slider. Any big or large enough image that is above the fold should be added here, however note that the images you add here "must" actually exist on the page, else it will trigger a warning on the browser console such as "The resource [...] was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it wasn't preloaded for nothing." which is not good practice.
224
+
225
+ Don't put too many resources here as those are downloaded in high priority and it will slow down the page load on mobile or slower connections (because the browser won't process the rest until it finishes downloading all of those big "preload" images).
226
+
227
+ ...
228
+
229
+ = What are the recommended cloudflare settings for this plugin? =
230
+
231
+ On the "Speed" tab, deselect the Auto Minify for JavaScript, CSS and HTML as well as the Rocket Loader option. There is no benefit of using them with our plugin (we already minify things). Those options sometimes can also break the design due to double minification or the fact that the Rocket Loader is still experimental (you can read about that on the "Help" link under each selected option on cloudflare).
232
+
233
+ ...
234
+
235
+ = How to undo all changes done by the plugin? =
236
+
237
+ The plugin itself doesn't do any "changes" to your site and all original files are untouched. It intercepts the enqueued CSS and JS files, processes and hides them, while enqueuing the newly optimized cached version of those files. As with any plugin, simply disable or uninstall the plugin, purge all caches you may have in use (plugins, server, cloudflare, etc) and the site will go back to what it was before installing it. The plugin doesn't delete anything from the database or modify any of your files.
238
+
239
+ ...
240
+
241
+ = I have a complaint or I need support right now. Why haven't you replied to my topic on the support forum yet? =
242
+
243
+ Before getting angry because you have no answer within a few hours (even with paid plugins, sometimes it takes weeks...), please be informed about how wordpress.org and the plugins directory work.
244
+
245
+ The plugins directory is an open source, free service where developers and programmers contribute (on their free time) with plugins that can be downloaded and installed by anyone "at their own risk" and are all released under the GPL license.
246
+
247
+ While all plugins have to be approved and reviewed by the wordpress team before being published ( for dangerous code, spam, etc ) this doesn't change the license or add any warranty. All plugins are provided as they are, free of charge and should be used at your own risk (so you should make backups before installing any plugin or performing updates) and it's your sole responsability if you break your site after installing a plugin from the plugins directory.
248
+
249
+ Support is provided by plugin authors on their free time and without warranty of a reply, so you can experience different levels of support level from plugin to plugin. As the author of this plugin I strive to provide support on a daily basis and I can take a look and help you with some issues related with my plugin, but please note that this is done out of my goodwill and in no way I have any legal or moral obligation for doing this. I'm also available for hiring if you need custom made speed optimizations (check my profile links).
250
+
251
+ For a full version of the license, please read: https://wordpress.org/about/gpl/
252
+
253
+ ...
254
+
255
+ = Where can I get support or report bugs? =
256
+
257
+ You can get support on the official wordpress plugin page at: https://wordpress.org/support/plugin/fast-velocity-minify
258
+ You can also see my profile and check my links if you wish to hire me for custom speed optimization on wordpress or extra features.
259
+
260
+ ...
261
+
262
+ = How can I donate to the plugin author? =
263
+
264
+ If you would like to donate any amount to the plugin author (thank you in advance), you can do it via Paypal at https://goo.gl/vpLrSV
265
+
266
+ ...
267
+
268
+
269
+ == Upgrade Notice ==
270
+
271
+ = 2.1.6 =
272
+ Note: Kindly purge the plugin cache as well as your server /plugin cache after updating.
273
+
274
+
275
+ == Changelog ==
276
+
277
+ = 2.2.0 [2017.08.13] =
278
+ * fixed some debug notices
279
+ * fixed the alternative html minification option
280
+
281
+ = 2.1.9 [2017.08.11] =
282
+ * fixed a devolopment bug
283
+
284
+ = 2.1.8 [2017.08.11] =
285
+ * fixed the html minification not working
286
+ * added support for the cdn enabler plugin (force http or https method)
287
+
288
+ = 2.1.7 [2017.07.17] =
289
+ * improved html minification speed and response time to the first byte
290
+ * fixed a random bug with the html minification library on large html pages (white pages)
291
+ * added support for the "Nginx Cache" plugin purge, by Till Krüss
292
+
293
+ = 2.1.6 [2017.07.17] =
294
+ * fixed a php notice in debug mode
295
+ * children styles (added with wp_add_inline_style) are now kept in order and merged together in place
296
+ * added faqs for possible "visual composer" issues
297
+
298
+ = 2.1.5 [2017.07.17] =
299
+ * css bug fixes and performance improvements
300
+ * added support for auto purging on WP Engine
301
+
302
+ = 2.1.4 [2017.07.14] =
303
+ * added compatibility with WP Engine.com and other providers that use a CNAME with their own subdomain
304
+
305
+ = 2.1.3 [2017.07.11] =
306
+ * updated PHP Minify for better compatibility
307
+ * added an alternative mode for html minification (because PHP Minify sometimes breaks things)
308
+ * css bug fixes and performance improvements
309
+
310
+ = 2.1.2 [2017.06.27] =
311
+ * fixed another error notice when debug mode is on
312
+
313
+ = 2.1.1 [2017.06.24] =
314
+ * fixed an error notice
315
+
316
+ = 2.1.0 [2017.06.21] =
317
+ * some performance improvements
318
+
319
+ = 2.0.9 [2017.06.01] =
320
+ * several bug and compatibility fixes
321
+
322
+ = 2.0.8 [2017.05.28] =
323
+ * fixed a notice alert on php for undefined function
324
+
325
+ = 2.0.7 [2017.05.28] =
326
+ * added support for auto purging of LiteSpeed Cache
327
+ * added support for auto purging on Godaddy Managed WordPress Hosting
328
+ * added the ie only blacklist, wich doesn't split merged files anymore, like the ignore list does
329
+ * added auto updates for the default ignore list and blacklist from our api once every 24 hours
330
+ * added cdn rewrite support for generated css and js files only
331
+ * removed url protocol rewrites and set default to dynamic "//" protocols
332
+ * updated the faqs
333
+
334
+ = 2.0.6 [2017.05.22] =
335
+ * added a "Troubleshooting" option to fix frontend editors for admin and editor level users
336
+ * updated the faqs
337
+
338
+ = 2.0.5 [2017.05.15] =
339
+ * fixed preserving the SVG namespace definition "http://www.w3.org/2000/svg" used on Bootstrap 4
340
+ * added some exclusions for Thrive and Visual Composer frontend preview and editors
341
+
342
+ = 2.0.4 [2017.05.15] =
343
+ * improved compatibility with Windows operating systems
344
+
345
+ = 2.0.3 [2017.05.15] =
346
+ * fixed an "undefined" notice
347
+
348
+ = 2.0.2 [2017.05.14] =
349
+ * improved compatibility on JS merging and minification
350
+
351
+ = 2.0.1 [2017.05.11] =
352
+ * fixed missing file that caused some errors on new installs
353
+
354
+ = 2.0.0 [2017.05.11] =
355
+ * moved the css and js merging base code back to 1.4.3 because it was better for compatibility
356
+ * removed the font awesome optimization tweaks because people have multiple versions and requirements (but duplicate css and js files are always removed)
357
+ * added all usable improvements and features up to 1.5.2, except for the "Defer CSS" and "Critical Path" features (will consider for the future)
358
+ * added info to the FAQ's about our internal blacklist for known CSS or JS files that are always ignored by the plugin
359
+ * changed the way CSS and JS files are fetched and merged to make use of the new improvements that were supposed to be on 1.4.4+
360
+ * changed the advanced settings tab back to the settings page for quicker selection of options by the advanced users
361
+ * changed the cache purging option to also delete our plugin transients via the API, rather than just let them expire
362
+ * changed the "Inline all CSS" option into header and footer separately
363
+
364
+ = 1.0 [2016.06.19] =
365
+ * Initial Release