Fast Velocity Minify - Version 3.3.3

Version Description

[2022.12.11] = * custom cache directory fixes * upgraded matthiasmullie's PHP Minify to 1.3.70 * move the viewport meta tag up, before preloading tags

Download this release

Release Info

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

Code changes from version 3.3.1 to 3.3.3

README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ # fast-velocity-minify
2
+ Official repository for Fast Velocity Minify
3
+ https://wordpress.org/plugins/fast-velocity-minify/
fvm.php CHANGED
@@ -6,7 +6,7 @@ Description: Improve your speed score on GTmetrix, Pingdom Tools and Google Page
6
  Author: Raul Peixoto
7
  Author URI: http://fastvelocity.com
8
  Text Domain: fast-velocity-minify
9
- Version: 3.3.1
10
  License: GPL2
11
 
12
  ------------------------------------------------------------------------
@@ -34,11 +34,11 @@ if(function_exists('wp_opcache_invalidate') && stripos(__FILE__, '/fvm.php') !==
34
  }
35
 
36
  # info, variables, paths
37
- if (!defined('FVM_DIR')) { define('FVM_DIR', __DIR__ . '/'); } # /home/path/plugins/pluginname/
38
- $fvm_var_dir_path = FVM_DIR; # /home/path/plugins/pluginname/
39
- $fvm_var_file = FVM_DIR . 'fvm.php'; # /home/path/plugins/pluginname/wpr.php
40
- $fvm_var_inc_dir = FVM_DIR . 'inc' . DIRECTORY_SEPARATOR; # /home/path/plugins/pluginname/inc/
41
- $fvm_var_inc_lib = FVM_DIR . 'libs' . DIRECTORY_SEPARATOR; # /home/path/plugins/pluginname/libs/
42
  $fvm_var_basename = plugin_basename($fvm_var_file); # pluginname/wpr.php
43
  $fvm_var_url_path = plugins_url(dirname($fvm_var_basename)) . '/'; # https://example.com/wp-content/plugins/pluginname/
44
  $fvm_var_plugin_version = get_file_data($fvm_var_file, array('Version' => 'Version'), false)['Version'];
@@ -107,5 +107,13 @@ if(!is_admin()) {
107
  # actions for frontend only
108
  add_action('template_redirect', 'fvm_start_buffer', 999999);
109
 
 
 
 
 
 
 
 
 
110
  }
111
 
6
  Author: Raul Peixoto
7
  Author URI: http://fastvelocity.com
8
  Text Domain: fast-velocity-minify
9
+ Version: 3.3.3
10
  License: GPL2
11
 
12
  ------------------------------------------------------------------------
34
  }
35
 
36
  # info, variables, paths
37
+ if (!defined('FVM_PDIR')) { define('FVM_PDIR', __DIR__ . '/'); } # /home/path/plugins/pluginname/
38
+ $fvm_var_dir_path = FVM_PDIR; # /home/path/plugins/pluginname/
39
+ $fvm_var_file = FVM_PDIR . 'fvm.php'; # /home/path/plugins/pluginname/wpr.php
40
+ $fvm_var_inc_dir = FVM_PDIR . 'inc' . DIRECTORY_SEPARATOR; # /home/path/plugins/pluginname/inc/
41
+ $fvm_var_inc_lib = FVM_PDIR . 'libs' . DIRECTORY_SEPARATOR; # /home/path/plugins/pluginname/libs/
42
  $fvm_var_basename = plugin_basename($fvm_var_file); # pluginname/wpr.php
43
  $fvm_var_url_path = plugins_url(dirname($fvm_var_basename)) . '/'; # https://example.com/wp-content/plugins/pluginname/
44
  $fvm_var_plugin_version = get_file_data($fvm_var_file, array('Version' => 'Version'), false)['Version'];
107
  # actions for frontend only
108
  add_action('template_redirect', 'fvm_start_buffer', 999999);
109
 
110
+ # add with positive priority, remove with negative as suggested by:
111
+ # https://github.com/nextend/wp-ob-plugins-themes
112
+
113
+ # allow overwrite
114
+ if (!defined('FVM_HOOK_INTO')) { define('FVM_HOOK_INTO', 'setup_theme'); }
115
+ add_action(constant("FVM_HOOK_INTO"), 'fvm_start_buffer', 50);
116
+ add_action('shutdown', 'fvm_end_buffer', -50);
117
+
118
  }
119
 
inc/common.php CHANGED
@@ -59,12 +59,12 @@ function fvm_admintoolbar() {
59
  function fvm_get_cache_location() {
60
 
61
  # custom path
62
- if (defined('FVM_DIR') && defined('FVM_URL')){
63
 
64
  # define paths and url
65
  $sep = DIRECTORY_SEPARATOR;
66
- $dir = trim(rtrim(FVM_DIR, '/\\')). $sep . 'cache' . $sep . 'fvm'. $sep . 'min';
67
- $durl = trim(rtrim(FVM_URL, '/')). '/cache/fvm/min';
68
 
69
  # create and return
70
  if(!is_dir($dir) && function_exists('wp_mkdir_p')) { wp_mkdir_p($dir); }
59
  function fvm_get_cache_location() {
60
 
61
  # custom path
62
+ if (defined('FVM_CACHE_DIR') && defined('FVM_CACHE_URL')){
63
 
64
  # define paths and url
65
  $sep = DIRECTORY_SEPARATOR;
66
+ $dir = trim(rtrim(FVM_CACHE_DIR, '/\\')). $sep . 'cache' . $sep . 'fvm'. $sep . 'min';
67
+ $durl = trim(rtrim(FVM_CACHE_URL, '/')). '/cache/fvm/min';
68
 
69
  # create and return
70
  if(!is_dir($dir) && function_exists('wp_mkdir_p')) { wp_mkdir_p($dir); }
inc/frontend.php CHANGED
@@ -32,192 +32,292 @@ include_once($fvm_var_inc_lib_mm . 'path-converter' . DIRECTORY_SEPARATOR . 'src
32
  ################################################
33
 
34
 
35
- # start buffering before template
36
  function fvm_start_buffer() {
37
  if(fvm_can_minify_css() || fvm_can_minify_js() || fvm_can_process_html()) {
38
- ob_start('fvm_process_page', 0, PHP_OUTPUT_HANDLER_REMOVABLE);
 
 
 
 
 
 
 
39
  }
40
  }
41
 
42
  # process html
43
  function fvm_process_page($html) {
44
 
45
- # get globals
46
- global $fvm_settings, $fvm_cache_paths, $fvm_urls;
47
-
48
- # get html into an object
49
- # https://simplehtmldom.sourceforge.io/manual.htm
50
- $html_object = fvm_str_get_html($html, false, true, 'UTF-8', false, PHP_EOL, ' ');
51
-
52
- # return early if html is not an object, or overwrite html into an object for processing
53
- if (!is_object($html_object)) {
54
- return $html . '<!-- simplehtmldom failed to process the html -->';
55
- } else {
56
- $html_src = $html;
57
- $html = $html_object;
58
- }
59
-
60
- # get globals
61
- global $fvm_settings, $fvm_urls;
62
-
63
- # defaults
64
- $tvers = get_option('fvm_last_cache_update', '0');
65
- $htmlpreloads = array();
66
- $htmlcssheader = array();
67
- $htmljsheader = array();
68
- $htmljsdefer = array();
69
-
70
- # only error possible for now
71
- $fvm_error = PHP_EOL . '<!-- ['.date('r').'] FVM has no write access for CSS / JS cache files under '. fvm_get_cache_location()['ch_url'] . ' -->'. PHP_EOL;
72
-
73
 
74
- # collect all link preload headers, skip amp
75
- if(fvm_is_amp_page() !== true) {
76
 
77
- # skip on web stories
78
- if(count($html->find('script[src*=cdn.ampproject.org]')) > 0) {
79
- return $html_src . DIRECTORY_SEPARATOR . '<!-- FVM ['.date('r').'] does not support AMP -->';
80
- }
81
-
82
- # add other preloads
83
- foreach($html->find('link[rel=preload]') as $tag) {
84
- $htmlpreloads[] = $tag->outertext;
85
- $tag->outertext = '';
 
86
  }
87
 
88
- }
89
-
90
-
91
- # START CSS PROCESSING
92
- if(fvm_can_minify_css()) {
93
-
94
  # defaults
95
- $fvm_styles = array();
96
- $allcss = array();
97
- $css_lowpriority_code = '';
 
 
98
 
99
- # start log
100
- $log = array();
101
- $css_total = 0;
102
 
103
- # start log
104
- $log[]= 'PAGE - '. fvm_get_uripath(true) . PHP_EOL . '---' . PHP_EOL;
105
 
106
-
107
- # exclude styles and link tags inside scripts, no scripts or html comments
108
- $excl = array();
109
- foreach($html->find('script link[rel=stylesheet], script style, noscript style, noscript link[rel=stylesheet], comment') as $element) {
110
- $excl[] = $element->outertext;
111
- }
112
-
113
- # collect all styles, but filter out if excluded
114
- foreach($html->find('link[rel=stylesheet], style') as $element) {
115
- if(!in_array($element->outertext, $excl)) {
116
- $allcss[] = $element;
117
  }
 
 
 
 
 
 
 
118
  }
119
 
120
- # START CSS LOOP
121
- foreach($allcss as $k=>$tag) {
 
122
 
123
- # mediatypes
124
- if(isset($tag->media)) {
 
 
125
 
126
- # Normalize mediatypes
127
- if($tag->media == 'screen' || $tag->media == 'screen, print' || strlen($tag->media) == 0) {
128
- $tag->media = 'all';
129
- }
130
-
131
- # Remove print styles
132
- if(isset($fvm_settings['css']['noprint']) && $fvm_settings['css']['noprint'] == true && $tag->media == 'print'){
133
- $tag->outertext = '';
134
- unset($allcss[$k]);
135
- continue;
 
 
 
 
 
 
 
 
136
  }
137
-
138
- } else {
139
- # must have
140
- $tag->media = 'all';
141
  }
142
 
143
-
144
- # START CSS FILES
145
- if($tag->tag == 'link' && isset($tag->href)) {
146
 
147
- # filter url
148
- $href = fvm_normalize_url($tag->href);
149
 
150
- # Ignore css files
151
- $ignore_css_merging = false;
152
- if(isset($fvm_settings['css']['ignore']) && !empty($fvm_settings['css']['ignore'])) {
153
- $arr = fvm_string_toarray($fvm_settings['css']['ignore']);
154
- if(is_array($arr) && count($arr) > 0) {
155
- foreach ($arr as $e) {
156
- if(stripos($href, $e) !== false) {
157
- unset($allcss[$k]);
158
- continue 2;
159
- }
160
- }
161
  }
162
- }
163
-
164
- # Remove CSS files
165
- if(isset($fvm_settings['css']['remove']) && !empty($fvm_settings['css']['remove'])) {
166
- $arr = fvm_string_toarray($fvm_settings['css']['remove']);
167
- if(is_array($arr) && count($arr) > 0) {
168
- foreach ($arr as $e) {
169
- if(stripos($href, $e) !== false) {
170
- $tag->outertext = '';
171
- unset($allcss[$k]);
172
- continue 2;
173
- }
174
- }
175
  }
 
 
 
 
176
  }
177
 
178
- # css merging functionality
179
- if(isset($fvm_settings['css']['combine']) && $fvm_settings['css']['combine'] == true) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
- # download or fetch from transient, minified
182
- $css = fvm_get_css_from_file($tag);
 
 
 
 
 
 
 
 
 
 
 
183
 
184
- if($css !== false && is_array($css)) {
 
 
 
 
185
 
186
- # error
187
- if(isset($css['error'])) {
188
- $tag->outertext = '/* Error on '.$href.' : '.$css['error'].' */'. PHP_EOL . $tag->outertext;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  unset($allcss[$k]);
190
  continue;
 
191
  }
 
 
 
 
 
192
 
193
- # extract fonts and icons
194
- if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
195
- $extract_fonts_arr = fvm_extract_fonts($css['code'], $href);
196
- $css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
197
- $css_code = $extract_fonts_arr['code'];
198
- } else {
199
- $css_code = $css['code'];
200
- }
201
-
202
 
203
- # async from the list only
204
- if(isset($fvm_settings['css']['async']) && $fvm_settings['css']['async'] == true) {
205
- if(isset($fvm_settings['css']['async']) && !empty($fvm_settings['css']['async'])) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  $arr = fvm_string_toarray($fvm_settings['css']['async']);
207
  if(is_array($arr) && count($arr) > 0) {
208
  foreach ($arr as $aa) {
209
  if(stripos($href, $aa) !== false) {
210
 
211
- # save css for merging
212
- $fvm_styles[$tag->media]['async'][] = $css_code;
213
-
 
 
214
  # log
215
  $size = strlen($css['code']);
216
  $css_total = $css_total + $size;
217
  $log[] = '[CSS Async: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $css['url']). PHP_EOL;
218
-
219
  # finish early
220
- $tag->outertext = '';
221
  unset($allcss[$k]);
222
  continue 2;
223
 
@@ -225,863 +325,780 @@ function fvm_process_page($html) {
225
  }
226
  }
227
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  }
229
-
230
- # else render block
231
-
232
- # save css for merging as fallback
233
- $fvm_styles[$tag->media]['block'][] = $css_code;
234
-
235
- # log
236
- $size = strlen($css['code']);
237
- $css_total = $css_total + $size;
238
- $log[] = '[CSS Block: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $css['url']). PHP_EOL;
239
-
240
- # finish early
241
  $tag->outertext = '';
242
  unset($allcss[$k]);
243
  continue;
244
-
245
  }
246
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
 
 
 
 
248
 
249
- # minify individually, if enabled
250
  if(!isset($fvm_settings['css']['min_disable']) || (isset($fvm_settings['css']['min_disable'])&& $fvm_settings['css']['min_disable'] != true)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
- # download or fetch from transient, minified
253
- $css = fvm_get_css_from_file($tag);
 
 
 
 
 
 
 
254
 
255
- if($css !== false && is_array($css)) {
 
 
 
 
 
256
 
257
- # error
258
- if(isset($css['error'])) {
259
- $tag->outertext = '/* Error on '.$href.' : '.$css['error'].' */'. PHP_EOL . $tag->outertext;
260
- unset($allcss[$k]);
261
  continue;
262
  }
263
 
264
- # extract fonts and icons
265
- if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
266
- $extract_fonts_arr = fvm_extract_fonts($css['code'], $href);
267
- $css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
268
- $css_code = $extract_fonts_arr['code'];
269
- } else {
270
- $css_code = $css['code'];
271
- }
272
-
273
- # empty files
274
- if(empty(trim($css_code))) {
275
- $tag->outertext = '';
276
- unset($allcss[$k]);
277
- continue;
278
- } else {
279
- $css_code = '/* '.$href.' */'. PHP_EOL . $css_code;
280
- }
281
-
282
- # generate url
283
- $ind_css_url = fvm_generate_min_url($href, $css['tkey'], 'css', $css_code);
284
- if($ind_css_url === false) {
285
  return $html_src . $fvm_error;
286
  }
287
-
288
- # cdn
289
- if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
290
- $ind_css_url = fvm_rewrite_cdn_url($ind_css_url);
291
- }
292
-
293
- # async from the list only
294
- if(isset($fvm_settings['css']['async']) && !empty($fvm_settings['css']['async'])) {
295
- $arr = fvm_string_toarray($fvm_settings['css']['async']);
296
- if(is_array($arr) && count($arr) > 0) {
297
- foreach ($arr as $aa) {
298
- if(stripos($href, $aa) !== false) {
299
-
300
- # async attributes
301
- $tag->rel = 'preload';
302
- $tag->as = 'style';
303
- $tag->onload = "this.rel='stylesheet';this.onload=null";
304
-
305
- # log
306
- $size = strlen($css['code']);
307
- $css_total = $css_total + $size;
308
- $log[] = '[CSS Async: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $css['url']). PHP_EOL;
309
-
310
- # finish early
311
- $tag->href = $ind_css_url;
312
- unset($allcss[$k]);
313
- continue 2;
314
-
315
- }
316
  }
 
 
 
 
 
 
317
  }
318
- }
319
-
320
- # force render blocking for other files
321
-
322
- # http and html preload for render blocking css
323
- if(!isset($fvm_settings['css']['nopreload']) || (isset($fvm_settings['css']['nopreload']) && $fvm_settings['css']['nopreload'] != true)) {
324
- $htmlpreloads[] = '<link rel="preload" href="'.$ind_css_url.'" as="style" media="'.$tag->media.'" />';
325
- }
326
-
327
- # log
328
- $size = strlen($css['code']);
329
- $css_total = $css_total + $size;
330
- $log[] = '[CSS Block: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $css['url']). PHP_EOL;
331
-
332
- # finish early
333
- $tag->href = $ind_css_url;
334
- unset($allcss[$k]);
335
 
336
  }
337
  }
338
  }
339
- # END CSS FILES
340
-
341
-
342
- # START STYLE TAGS
343
- if($tag->tag == 'style' && !isset($tag->href)) {
344
-
345
- # remove if empty
346
- if(strlen(trim($tag->innertext)) == 0) {
347
- $tag->outertext = '';
348
- unset($allcss[$k]);
349
- continue;
350
- }
351
-
352
- # default
353
- $css = $tag->innertext;
354
-
355
- # minify?
356
- if(!isset($fvm_settings['css']['min_disable_styles']) || (isset($fvm_settings['css']['min_disable_styles'])&& $fvm_settings['css']['min_disable_styles'] != true)) {
357
- $css = fvm_minify_css_string($css);
358
- }
359
-
360
- # handle import rules
361
- $css = fvm_replace_css_imports($css);
362
-
363
- # simplify font face
364
- $arr = fvm_simplify_fontface($css);
365
- if($arr !== false && is_array($arr)) {
366
- $css = str_replace($arr['before'], $arr['after'], $css);
367
- }
368
-
369
- # extract fonts and icons
370
- if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
371
- $extract_fonts_arr = fvm_extract_fonts($css);
372
- $css_lowpriority_code.= $extract_fonts_arr['fonts'];
373
- $css = $extract_fonts_arr['code'];
374
- }
375
-
376
- # add filtered css code
377
- if(!empty($css)) {
378
- $tag->innertext = $css;
379
- unset($allcss[$k]);
380
- continue;
381
- }
382
-
383
- }
384
- # END STYLE TAGS
385
-
386
  }
387
- # END CSS LOOP
388
 
389
- # START CSS LOG
390
- if(count($log) > 1) {
391
- $log[] = str_pad('-', 22, '-',STR_PAD_LEFT) . PHP_EOL;
392
- $log[] = '[CSS Total: '.str_pad(fvm_format_filesize($css_total), 9,' ',STR_PAD_LEFT).']' . PHP_EOL;
393
- $log[] = str_pad('-', 22, '-',STR_PAD_LEFT);
394
- fvm_save_log(array('type'=>'min','msg'=>implode('', $log)));
395
- }
396
- # END CSS LOG
397
-
398
 
399
- # START OPTIMIZED FONT DELIVERY
400
- if(!empty($css_lowpriority_code)) {
 
 
 
 
 
401
 
402
- # minify?
403
- if(!isset($fvm_settings['css']['min_disable']) || (isset($fvm_settings['css']['min_disable'])&& $fvm_settings['css']['min_disable'] != true)) {
404
- $css_lowpriority_code = fvm_minify_css_string($css_lowpriority_code);
405
- }
406
 
407
- # save transient, if not yet saved
408
- $tkey = fvm_generate_hash_with_prefix(hash('sha256', $css_lowpriority_code), 'css');
409
 
410
- # generate url
411
- $css_fonts_url = fvm_generate_min_url('fonts', $tkey, 'css', $css_lowpriority_code);
412
- if($css_fonts_url === false) {
413
- return $html_src . $fvm_error;
414
  }
415
-
416
- # cdn
417
- if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
418
- $css_fonts_url = fvm_rewrite_cdn_url($css_fonts_url);
419
- }
420
-
421
- # preload
422
- $htmlcssheader[0] = '<link rel="preload" fetchpriority="low" id="fvmfonts-css" href="'.$css_fonts_url.'" as="style" media="all" onload="this.rel=\'stylesheet\';this.onload=null">';
423
-
424
-
425
- }
426
- # END OPTIMIZED FONT DELIVERY
427
-
428
- # START COMBINING CSS
429
- if(is_array($fvm_styles) && count($fvm_styles) > 0) {
430
-
431
- # process mediatypes
432
- foreach ($fvm_styles as $mediatype=>$css_arr) {
433
-
434
- # process types, block or async
435
- foreach ($css_arr as $css_method=>$css_arr2) {
436
-
437
- # merge and hash
438
- $merged_css = implode(PHP_EOL, $css_arr2);
439
- $tkey = fvm_generate_hash_with_prefix(hash('sha256', $merged_css), 'css');
440
 
441
- # inline if small
442
- if(strlen($merged_css) < 15000 && $css_method == 'block') {
443
- $htmlcssheader[] = '<style type="text/css" media="'.$mediatype.'">'.$merged_css.'</style>';
 
 
 
 
 
 
 
 
 
444
  continue;
445
  }
446
 
447
- # url, preload, add
448
- $merged_css_url = fvm_generate_min_url('combined', $tkey, 'css', $merged_css);
449
- if($merged_css_url === false) {
450
- return $html_src . $fvm_error;
451
  }
 
 
 
 
 
 
452
 
453
- # cdn
454
- if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
455
- $merged_css_url = fvm_rewrite_cdn_url($merged_css_url);
456
- }
457
 
458
- # http, html preload + header
459
- if($css_method == 'block') {
460
-
461
- # add to header
462
- $htmlcssheader[] = '<link rel="stylesheet" href="'.$merged_css_url.'" media="'.$mediatype.'" />';
463
-
464
- # http and html preload for render blocking css
465
- if(!isset($fvm_settings['css']['nopreload']) || (isset($fvm_settings['css']['nopreload']) && $fvm_settings['css']['nopreload'] != true)) {
466
- $htmlpreloads[] = '<link rel="preload" href="'.$merged_css_url.'" as="style" media="'.$mediatype.'" />';
 
467
  }
468
-
469
- } else {
470
-
471
- # async
472
- $htmlcssheader[] = '<link rel="preload" as="style" href="'.$merged_css_url.'" media="'.$mediatype.'" onload="this.rel=\'stylesheet\'" />';
473
 
 
 
 
 
 
 
 
 
 
 
 
 
474
  }
475
-
476
- }
477
- }
478
- }
479
- # END COMBINING CSS
480
-
481
- }
482
- # END CSS PROCESSING
483
-
484
-
485
- # START JS PROCESSING
486
- if(fvm_can_minify_js()) {
487
-
488
- # defaults
489
- $scripts_duplicate_check = array();
490
- $fvm_scripts_header = array();
491
- $fvm_scripts_defer = array();
492
-
493
- # start log
494
- $log = array();
495
- $js_total = 0;
496
-
497
- # start log
498
- $log[]= 'PAGE - '. fvm_get_uripath(true) . PHP_EOL . '---' . PHP_EOL;
499
-
500
- # get all scripts
501
- $allscripts = array();
502
- foreach($html->find('script') as $element) {
503
- $allscripts[] = $element;
504
- }
505
-
506
- # START JS LOOP
507
- if (is_array($allscripts) && count($allscripts) > 0) {
508
- foreach($allscripts as $k=>$tag) {
509
-
510
- # handle application/ld+json or application/json before anything else
511
- if(isset($tag->type) && ($tag->type == 'application/ld+json' || $tag->type == 'application/json')) {
512
-
513
- # minify
514
- if(isset($fvm_settings['js']['js_enable_min_inline']) && $fvm_settings['js']['js_enable_min_inline'] == true) { $tag->innertext = fvm_minify_microdata($tag->innertext); }
515
-
516
- # remove
517
- unset($allscripts[$k]);
518
- continue;
519
- }
520
-
521
- # skip unknown script types
522
- if(isset($tag->type) && $tag->type != 'text/javascript') {
523
- unset($allscripts[$k]);
524
- continue;
525
- }
526
-
527
- # remove default script type
528
- if(isset($tag->type) && $tag->type == 'text/javascript') { unset($tag->type); }
529
-
530
- # START JS FILES
531
- if(isset($tag->src)) {
532
-
533
- # filter url
534
- $href = fvm_normalize_url($tag->src);
535
-
536
- # ignore js files
537
- if(isset($fvm_settings['js']['ignore']) && !empty($fvm_settings['js']['ignore'])) {
538
- $arr = fvm_string_toarray($fvm_settings['js']['ignore']);
539
- if(is_array($arr) && count($arr) > 0) {
540
- foreach ($arr as $a) {
541
- if(stripos($tag->src, $a) !== false) {
542
- unset($allscripts[$k]);
543
- continue 2;
544
- }
545
- }
546
- }
547
- }
548
-
549
- # remove js files
550
- if(isset($fvm_settings['js']['remove']) && !empty($fvm_settings['js']['remove'])) {
551
- $arr = fvm_string_toarray($fvm_settings['js']['remove']);
552
- if(is_array($arr) && count($arr) > 0) {
553
- foreach ($arr as $a) {
554
- if(stripos($tag->src, $a) !== false) {
555
- $tag->outertext = '';
556
- unset($allscripts[$k]);
557
- continue 2;
558
- }
559
- }
560
- }
561
- }
562
 
563
-
564
- # JS files to delay until user interaction (unmergeable)
565
- if(isset($fvm_settings['js']['thirdparty']) && !empty($fvm_settings['js']['thirdparty'])) {
566
- $arr = fvm_string_toarray($fvm_settings['js']['thirdparty']);
567
- if(is_array($arr) && count($arr) > 0) {
568
- foreach ($arr as $ac) {
569
- if(stripos($tag->src, $ac) !== false) {
570
-
571
- # unique identifier
572
- $uid = fvm_generate_hash_with_prefix(hash('sha256', $tag->outertext), 'js');
573
-
574
- # remove exact duplicates, or replace transformed tag
575
- if(isset($scripts_duplicate_check[$uid])) {
576
- $tag->outertext = '';
577
- } else {
578
- $tag->type = 'fvmdelay';
579
- $scripts_duplicate_check[$uid] = $uid;
580
- }
581
-
582
- # mark as processed, unset and break inner loop
583
- unset($allscripts[$k]);
584
- continue 2;
585
-
586
- }
587
- }
588
- }
589
- }
590
-
591
-
592
- # START MERGING JS
593
- if(isset($fvm_settings['js']['combine']) && $fvm_settings['js']['combine'] == true) {
594
-
595
- # force render blocking
596
- if(isset($fvm_settings['js']['merge_header']) && !empty($fvm_settings['js']['merge_header'])) {
597
- $arr = fvm_string_toarray($fvm_settings['js']['merge_header']);
598
  if(is_array($arr) && count($arr) > 0) {
599
- foreach ($arr as $aa) {
600
- if(stripos($tag->src, $aa) !== false) {
601
 
602
- # download or fetch from transient, minified
603
- $js = fvm_get_js_from_file($tag);
604
- if($js !== false && is_array($js)) {
605
-
606
- # error
607
- if(isset($js['error'])) {
608
- $tag->outertext = '/* Error on '.$href.' : '.$js['error'].' */'. PHP_EOL . $tag->outertext;
609
- unset($allscripts[$k]);
610
- continue 2;
611
- }
612
 
613
- # save js for merging
614
- $fvm_scripts_header[] = $js['code'];
615
-
616
- # log
617
- $size = strlen($js['code']);
618
- $js_total = $js_total + $size;
619
- $log[] = '[JS Block: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $js['url']). PHP_EOL;
620
-
621
- # remove from html
622
  $tag->outertext = '';
623
- unset($allscripts[$k]);
624
- continue 2;
625
-
626
- }
 
 
 
 
 
627
  }
628
  }
629
  }
630
  }
631
 
632
- # force defer
633
- if(isset($fvm_settings['js']['merge_defer']) && !empty($fvm_settings['js']['merge_defer'])) {
634
- $arr = fvm_string_toarray($fvm_settings['js']['merge_defer']);
635
- if(is_array($arr) && count($arr) > 0) {
636
- foreach ($arr as $aa) {
637
- if(stripos($tag->src, $aa) !== false) {
638
-
639
- # download or fetch from transient, minified
640
- $js = fvm_get_js_from_file($tag);
641
- if($js !== false && is_array($js)) {
 
 
 
 
 
 
 
 
 
 
 
642
 
643
- # error
644
- if(isset($js['error'])) {
645
- $tag->outertext = '/* Error on '.$href.' : '.$js['error'].' */'. PHP_EOL . $tag->outertext;
 
 
 
 
 
 
 
646
  unset($allscripts[$k]);
647
  continue 2;
 
648
  }
649
-
650
- # save js for merging
651
- $fvm_scripts_defer[] = $js['code'];
 
 
 
 
 
 
 
 
652
 
653
- # log
654
- $size = strlen($js['code']);
655
- $js_total = $js_total + $size;
656
- $log[] = '[JS Defer: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $js['url']). PHP_EOL;
657
-
658
- # remove from html
659
- $tag->outertext = '';
660
- unset($allscripts[$k]);
661
- continue 2;
 
662
 
663
- }
664
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
665
  }
666
  }
667
- }
 
 
 
 
 
 
 
668
 
669
- # jquery needs to load earlier, if not being merged (while merging is active)
670
- if(stripos($tag->src, '/jquery.js') !== false || stripos($tag->src, '/jquery.min.js') !== false || stripos($tag->src, '/jquery-migrate') !== false) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
 
672
- # http and html preload for render blocking js
673
- if(!isset($fvm_settings['js']['nopreload']) || (isset($fvm_settings['js']['nopreload']) && $fvm_settings['js']['nopreload'] != true)) {
674
- $htmlpreloads[] = '<link rel="preload" href="'.$href.'" as="script" />';
 
 
675
  }
676
 
677
- # header
678
- if(stripos($tag->src, '/jquery-migrate') !== false) {
679
- $htmljsheader[1] = "<script data-cfasync='false' src='".$href."'></script>"; # jquery migrate
680
- } else {
681
- $htmljsheader[0] = "<script data-cfasync='false' src='".$href."'></script>"; # jquery
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
682
  }
683
 
684
- # content
685
- $tag->outertext = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
  unset($allscripts[$k]);
687
  continue;
688
- }
689
-
 
690
  }
691
- # END MERGING JS
692
 
693
 
694
- # START INDIVIDUAL JS MINIFICATION
695
- # minify individually, if enabled
696
- if(!isset($fvm_settings['js']['min_disable']) || (isset($fvm_settings['js']['min_disable'])&& $fvm_settings['js']['min_disable'] != true)) {
697
-
698
- # skip third party scripts, unless allowed
699
- $allowed = array($fvm_urls['wp_domain'], '/ajax.aspnetcdn.com/ajax/', '/ajax.googleapis.com/ajax/libs/', '/cdnjs.cloudflare.com/ajax/libs/');
700
- if(str_replace($allowed, '', $href) == $href) {
701
- unset($allscripts[$k]);
702
  continue;
703
  }
704
 
705
- # force render blocking
706
- if(isset($fvm_settings['js']['merge_header']) && !empty($fvm_settings['js']['merge_header'])) {
707
- $arr = fvm_string_toarray($fvm_settings['js']['merge_header']);
 
 
 
 
 
 
 
 
708
  if(is_array($arr) && count($arr) > 0) {
709
- foreach ($arr as $aa) {
710
- if(stripos($tag->src, $aa) !== false) {
711
 
712
- # download or fetch from transient, minified
713
- $js = fvm_get_js_from_file($tag);
714
- if($js !== false && is_array($js)) {
715
-
716
- # error
717
- if(isset($js['error'])) {
718
- $tag->outertext = '/* Error on '.$href.' : '.$js['error'].' */'. PHP_EOL . $tag->outertext;
719
- unset($allscripts[$k]);
720
- continue 2;
721
- }
722
 
723
- # generate url
724
- $ind_js_url = fvm_generate_min_url($tag->src, $js['tkey'], 'js', $js['code']);
725
- if($ind_js_url === false) {
726
- return $html_src . $fvm_error;
727
- }
728
-
729
- # cdn
730
- if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
731
- $ind_js_url = fvm_rewrite_cdn_url($ind_js_url);
732
- }
733
-
734
- # render block
735
- if(isset($tag->async)) { unset($tag->async); }
736
- if(isset($tag->defer)) { unset($tag->defer); }
737
-
738
- # http and html preload for render blocking scripts
739
- if(!isset($fvm_settings['js']['nopreload']) || (isset($fvm_settings['js']['nopreload']) && $fvm_settings['js']['nopreload'] != true)) {
740
- $htmlpreloads[] = '<link rel="preload" href="'.$ind_js_url.'" as="script" />';
741
- }
742
-
743
- # log
744
- $size = strlen($js['code']);
745
- $js_total = $js_total + $size;
746
- $log[] = '[JS Block: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $js['url']). PHP_EOL;
747
-
748
- # finish early
749
- $tag->src = $ind_js_url;
750
- unset($allscripts[$k]);
751
- continue 2;
752
-
753
  }
754
- }
 
 
 
 
755
  }
756
  }
757
  }
758
-
759
- # force defer
760
- if(isset($fvm_settings['js']['merge_defer']) && !empty($fvm_settings['js']['merge_defer'])) {
761
- $arr = fvm_string_toarray($fvm_settings['js']['merge_defer']);
762
  if(is_array($arr) && count($arr) > 0) {
763
- foreach ($arr as $aa) {
764
- if(stripos($tag->src, $aa) !== false) {
765
-
766
- # download or fetch from transient, minified
767
- $js = fvm_get_js_from_file($tag);
768
- if($js !== false && is_array($js)) {
769
-
770
- # error
771
- if(isset($js['error'])) {
772
- $tag->outertext = '/* Error on '.$href.' : '.$js['error'].' */'. PHP_EOL . $tag->outertext;
773
- unset($allscripts[$k]);
774
- continue 2;
775
- }
776
 
777
- # generate url
778
- $ind_js_url = fvm_generate_min_url($tag->src, $js['tkey'], 'js', $js['code']);
779
- if($ind_js_url === false) {
780
- return $html_src . $fvm_error;
781
- }
782
-
783
- # cdn
784
- if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
785
- $ind_js_url = fvm_rewrite_cdn_url($ind_js_url);
786
- }
787
-
788
- # add defer
789
- if(!isset($tag->defer)) { $tag->defer = 'defer'; }
790
-
791
- # log
792
- $size = strlen($js['code']);
793
- $js_total = $js_total + $size;
794
- $log[] = '[JS Defer: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $js['url']). PHP_EOL;
795
-
796
- # finish early
797
- $tag->src = $ind_js_url;
798
- unset($allscripts[$k]);
799
- continue 2;
800
-
801
  }
802
- }
 
 
 
 
803
  }
804
  }
805
- }
806
-
807
- # remove unprocessed scripts from loop
808
- unset($allscripts[$k]);
809
- continue;
810
- }
811
- # END INDIVIDUAL JS MINIFICATION
812
-
813
  }
814
- # END JS FILES
815
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
816
 
817
- # START INLINED SCRIPTS
818
- if(!isset($tag->src)) {
819
-
820
- # remove if empty
821
- if(strlen(trim($tag->innertext)) == 0) {
822
- $tag->outertext = '';
823
- unset($allcss[$k]);
824
- continue;
825
- }
826
-
827
- # default
828
- $js = ''; $js = $tag->innertext;
829
 
830
- # minify?
831
- if(!isset($fvm_settings['js']['min_disable_inline']) || (isset($fvm_settings['js']['min_disable_inline'])&& $fvm_settings['js']['min_disable_inline'] != true)) {
832
- $js = PHP_EOL . fvm_maybe_minify_js($js, null, true) . PHP_EOL;
833
  }
834
 
835
- # delay inline scripts until user interaction (unmergeable)
836
- if(isset($fvm_settings['js']['thirdparty']) && !empty($fvm_settings['js']['thirdparty'])) {
837
- $arr = fvm_string_toarray($fvm_settings['js']['thirdparty']);
838
- if(is_array($arr) && count($arr) > 0) {
839
- foreach ($arr as $b) {
840
- if(stripos($js, $b) !== false || stripos($js, $b) !== false) {
841
-
842
- # delay
843
- $tag->type = 'fvmdelay';
844
-
845
- # minified
846
- if(!empty($js)) {
847
- $tag->innertext = $js;
848
- }
849
-
850
- # unset
851
- unset($allscripts[$k]);
852
- continue 2;
853
- }
854
- }
855
- }
856
  }
 
 
 
857
 
858
- # defer inline scripts
859
- if(isset($fvm_settings['js']['defer_dependencies']) && !empty($fvm_settings['js']['defer_dependencies'])) {
860
- $arr = fvm_string_toarray($fvm_settings['js']['defer_dependencies']);
861
- if(is_array($arr) && count($arr) > 0) {
862
- foreach ($arr as $b) {
863
- if((stripos($js, $b) !== false || stripos($js, $b) !== false) && !isset($tag->src)) {
864
-
865
- # defer and rawurlencode
866
- # jquery document ready needs to execute before deferred scripts
867
- if(!empty($js) && stripos($js, ').ready(') === false) {
868
- $tag->src = 'data:application/javascript,'.rawurlencode($js);
869
- $tag->innertext = '';
870
- }
871
-
872
- # unset
873
- unset($allscripts[$k]);
874
- continue 2;
875
- }
876
- }
877
- }
878
- }
879
- }
880
- # END INLINED SCRIPTS
881
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
882
  }
 
883
  }
884
- # END JS LOOP
885
-
886
-
887
- # START JS LOG
888
- if(count($log) > 1) {
889
- $log[] = str_pad('-', 21, '-',STR_PAD_LEFT) . PHP_EOL;
890
- $log[] = '[JS Total: '.str_pad(fvm_format_filesize($js_total), 9,' ',STR_PAD_LEFT).']' . PHP_EOL;
891
- $log[] = str_pad('-', 21, '-',STR_PAD_LEFT);
892
- fvm_save_log(array('type'=>'min','msg'=>implode('', $log)));
893
- }
894
- # END JS LOG
895
-
896
- # START COMBINING JS
897
 
898
- # header scripts
899
- if(is_array($fvm_scripts_header) && count($fvm_scripts_header) > 0) {
900
-
901
- # merge code, hash
902
- $merged_js = implode(PHP_EOL, $fvm_scripts_header);
903
- $tkey = fvm_generate_hash_with_prefix(hash('sha256', $merged_js), 'js');
904
-
905
- # generate url
906
- $merged_js_url = fvm_generate_min_url('combined', $tkey, 'js', $merged_js);
907
- if($merged_js_url === false) {
908
- return $html_src . $fvm_error;
909
- }
910
 
911
- # cdn
912
- if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
913
- $merged_js_url = fvm_rewrite_cdn_url($merged_js_url);
 
914
  }
 
 
 
 
915
 
916
- # http and html preload for render blocking scripts
917
- if(!isset($fvm_settings['js']['nopreload']) || (isset($fvm_settings['js']['nopreload']) && $fvm_settings['js']['nopreload'] != true)) {
918
- $htmlpreloads[] = '<link rel="preload" href="'.$merged_js_url.'" as="script" />';
919
  }
920
 
921
- # add to header
922
- $htmljsheader[] = "<script data-cfasync='false' src='".$merged_js_url."'></script>";
923
-
 
 
 
 
 
 
 
924
  }
925
 
926
- # deferred scripts
927
- if(is_array($fvm_scripts_defer) && count($fvm_scripts_defer) > 0) {
 
 
 
 
 
 
 
 
 
928
 
929
- # merge code, hash
930
- $merged_js = implode(PHP_EOL, $fvm_scripts_defer);
931
- $tkey = fvm_generate_hash_with_prefix(hash('sha256', $merged_js), 'js');
932
-
933
- # generate url
934
- $merged_js_url = fvm_generate_min_url('combined', $tkey, 'js', $merged_js);
935
- if($merged_js_url === false) {
936
- return $html_src . $fvm_error;
937
- }
938
-
939
- # cdn
940
- if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
941
- $merged_js_url = fvm_rewrite_cdn_url($merged_js_url);
942
- }
943
-
944
- # header, no preload for deferred files
945
- $htmljsheader[] = "<script defer='defer' src='".$merged_js_url."'></script>";
946
-
947
  }
948
 
949
- }
950
- # END JS PROCESSING
951
-
952
-
953
-
954
- # process HTML minification, if not disabled ###############################
955
- if(fvm_can_process_html()) {
956
-
957
- # Remove HTML comments and IE conditionals
958
- if(isset($fvm_settings['html']['nocomments']) && $fvm_settings['html']['nocomments'] == true) {
959
- foreach($html->find('comment') as $element) {
960
- $element->outertext = '';
961
- }
962
  }
963
 
964
- # Remove generator tags
965
- if(isset($fvm_settings['html']['cleanup_header']) && $fvm_settings['html']['cleanup_header'] == true) {
 
 
 
 
 
 
 
 
 
966
 
967
- # remove
968
- foreach($html->find('head meta[name=generator], head link[rel=shortlink], head link[rel=dns-prefetch], head link[rel=preconnect], head link[rel=prefetch], head link[rel=prerender], head meta[name*=msapplication], head link[rel=apple-touch-icon], head link[rel=EditURI], head link[rel=preconnect], head link[rel=wlwmanifest], head link[rel=https://api.w.org/], head link[href*=/wp-json/], head link[rel=pingback], head link[type=application/json+oembed], head link[type=text/xml+oembed]') as $element) {
969
- $element->outertext = '';
 
 
 
970
  }
971
 
972
- # allow only the last link[rel=icon]
973
- $ic = array(); $ic = $html->find('head link[rel=icon]');
974
- $i = 1; $len = count($ic);
975
- if($len > 1) {
976
- foreach($ic as $element) {
977
- if ($i != $len) { $element->outertext = ''; } $i++; # delete except if last
978
- }
979
  }
980
  }
981
-
982
- }
983
-
984
- # build extra head and footer ###############################
985
-
986
- # header and footer markers
987
- $hm = '<!-- h_preheader --><!-- h_header_function -->';
988
- $hm_late = '<!-- h_cssheader --><!-- h_jsheader -->';
989
- $fm = '<!-- h_footer_fvm_scripts -->';
990
-
991
- # add our function to head
992
- if(fvm_can_minify_css() || fvm_can_minify_js()) {
993
- $hm = fvm_add_header_function($hm);
994
- }
995
 
996
- # remove charset meta tag and collect it to first position
997
- if(!is_null($html->find('meta[charset]', 0))) {
998
- $hm = str_replace('<!-- h_preheader -->', $html->find('meta[charset]', 0)->outertext.'<!-- h_preheader -->', $hm);
999
- foreach($html->find('meta[charset]') as $element) { $element->outertext = ''; }
1000
- }
1001
-
1002
- # remove other meta tag and collect them between preload and css/js files
1003
- foreach($html->find('head meta, head title, head link[rel=canonical], head link[rel=alternate], head link[rel=pingback], head script[type=application/ld+json]') as $element) {
1004
- $hm_late = str_replace('<!-- h_cssheader -->', $element->outertext.'<!-- h_cssheader -->', $hm_late);
1005
- $element->outertext = '';
1006
- }
1007
-
1008
- # preload headers, by fetchpriority attribute
1009
- if(is_array($htmlpreloads)) {
1010
 
1011
- # deduplicate
1012
- $htmlpreloads = array_unique($htmlpreloads);
 
 
 
 
 
1013
 
1014
- # get values
1015
- $pre_html = array_values($htmlpreloads);
 
 
 
 
 
 
 
1016
 
1017
- # add preload html header
1018
- if(count($pre_html) > 0) {
1019
- $hm = str_replace('<!-- h_preheader -->', implode(PHP_EOL, $pre_html).'<!-- h_preheader -->', $hm);
 
 
 
1020
  }
1021
 
1022
- }
1023
 
1024
- # add stylesheets
1025
- if(isset($htmlcssheader)) {
1026
- if(is_array($htmlcssheader) && count($htmlcssheader) > 0) {
1027
- ksort($htmlcssheader); # priority
1028
- $hm_late = str_replace('<!-- h_cssheader -->', implode(PHP_EOL, $htmlcssheader).'<!-- h_cssheader -->', $hm_late);
1029
- }
1030
- }
1031
-
1032
- # add header scripts
1033
- if(isset($htmljsheader)) {
1034
- if(is_array($htmljsheader) && count($htmljsheader) > 0) {
1035
- ksort($htmljsheader); # priority
1036
- $hm_late = str_replace('<!-- h_jsheader -->', implode(PHP_EOL, $htmljsheader).'<!-- h_jsheader -->', $hm_late);
1037
- }
1038
- }
1039
-
1040
- # add defer scripts
1041
- if(isset($htmljscodedefer)) {
1042
- if(is_array($htmljscodedefer) && count($htmljscodedefer) > 0) {
1043
- ksort($htmljscodedefer); # priority
1044
- $hm_late = str_replace('<!-- h_jsheader -->', implode(PHP_EOL, $htmljscodedefer), $hm_late);
1045
  }
1046
- }
1047
-
1048
- # add fvm_footer scripts, if enabled
1049
- if(fvm_can_minify_js()) {
1050
- $fm = fvm_add_footer_function($fm);
1051
- }
1052
-
1053
- # cleanup leftover markers
1054
- $hm = str_replace(array('<!-- h_preheader -->', '<!-- h_header_function -->'), '', $hm);
1055
- $hm_late = str_replace(array('<!-- h_cssheader -->', '<!-- h_jsheader -->'), '', $hm_late);
1056
- $fm = str_replace('<!-- h_footer_fvm_scripts -->', '', $fm);
1057
-
1058
- # append header and footer
1059
- if(!is_null($html->find('head', 0)) && !is_null($html->find('body', -1))) {
1060
- if(!is_null($html->find('head', 0)->first_child()) && !is_null($html->find('body', -1)->last_child())) {
1061
- $html->find('head', 0)->first_child()->outertext = $hm . $html->find('head', 0)->first_child()->outertext . $hm_late;
1062
- $html->find('body', -1)->last_child()->outertext = $html->find('body', -1)->last_child ()->outertext . $fm;
1063
  }
1064
- }
1065
-
1066
-
1067
- # convert html object to string, save all objects to string
1068
- $html = trim($html->save());
1069
-
1070
- # process cdn optimization
1071
- if(fvm_can_process_cdn()) {
1072
- $html = fvm_process_cdn($html);
1073
- }
1074
-
1075
- # minify remaining HTML at the end, if enabled
1076
- if(fvm_can_process_html()) {
1077
- if(!isset($fvm_settings['html']['min_disable']) || (isset($fvm_settings['html']['min_disable']) && $fvm_settings['html']['min_disable'] != true)) {
1078
- $html = fvm_raisermin_html($html);
1079
  }
1080
- }
1081
 
1082
- # filter final html if needed
1083
- if(function_exists('fvm_filter_final_html')) {
1084
- $html = fvm_filter_final_html($html);
1085
  }
1086
 
1087
  # return html
32
  ################################################
33
 
34
 
35
+ # start buffering on template load
36
  function fvm_start_buffer() {
37
  if(fvm_can_minify_css() || fvm_can_minify_js() || fvm_can_process_html()) {
38
+ ob_start('fvm_process_page', 0, PHP_OUTPUT_HANDLER_FLUSHABLE);
39
+ }
40
+ }
41
+
42
+ # process the buffer
43
+ function fvm_end_buffer() {
44
+ if(fvm_can_minify_css() || fvm_can_minify_js() || fvm_can_process_html()) {
45
+ ob_end_flush();
46
  }
47
  }
48
 
49
  # process html
50
  function fvm_process_page($html) {
51
 
52
+ if(fvm_can_minify_css() || fvm_can_minify_js() || fvm_can_process_html()) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
+ # get globals
55
+ global $fvm_settings, $fvm_cache_paths, $fvm_urls;
56
 
57
+ # get html into an object
58
+ # https://simplehtmldom.sourceforge.io/manual.htm
59
+ $html_object = fvm_str_get_html($html, false, true, 'UTF-8', false, PHP_EOL, ' ');
60
+
61
+ # return early if html is not an object, or overwrite html into an object for processing
62
+ if (!is_object($html_object)) {
63
+ return $html . '<!-- simplehtmldom failed to process the html -->';
64
+ } else {
65
+ $html_src = $html;
66
+ $html = $html_object;
67
  }
68
 
69
+ # get globals
70
+ global $fvm_settings, $fvm_urls;
71
+
 
 
 
72
  # defaults
73
+ $tvers = get_option('fvm_last_cache_update', '0');
74
+ $htmlpreloads = array();
75
+ $htmlcssheader = array();
76
+ $htmljsheader = array();
77
+ $htmljsdefer = array();
78
 
79
+ # only error possible for now
80
+ $fvm_error = PHP_EOL . '<!-- ['.date('r').'] FVM has no write access for CSS / JS cache files under '. fvm_get_cache_location()['ch_url'] . ' -->'. PHP_EOL;
 
81
 
 
 
82
 
83
+ # collect all link preload headers, skip amp
84
+ if(fvm_is_amp_page() !== true) {
85
+
86
+ # skip on web stories
87
+ if(count($html->find('script[src*=cdn.ampproject.org]')) > 0) {
88
+ return $html_src . DIRECTORY_SEPARATOR . '<!-- FVM ['.date('r').'] does not support AMP -->';
 
 
 
 
 
89
  }
90
+
91
+ # add other preloads
92
+ foreach($html->find('link[rel=preload]') as $tag) {
93
+ $htmlpreloads[] = $tag->outertext;
94
+ $tag->outertext = '';
95
+ }
96
+
97
  }
98
 
99
+
100
+ # START CSS PROCESSING
101
+ if(fvm_can_minify_css()) {
102
 
103
+ # defaults
104
+ $fvm_styles = array();
105
+ $allcss = array();
106
+ $css_lowpriority_code = '';
107
 
108
+ # start log
109
+ $log = array();
110
+ $css_total = 0;
111
+
112
+ # start log
113
+ $log[]= 'PAGE - '. fvm_get_uripath(true) . PHP_EOL . '---' . PHP_EOL;
114
+
115
+
116
+ # exclude styles and link tags inside scripts, no scripts or html comments
117
+ $excl = array();
118
+ foreach($html->find('script link[rel=stylesheet], script style, noscript style, noscript link[rel=stylesheet], comment') as $element) {
119
+ $excl[] = $element->outertext;
120
+ }
121
+
122
+ # collect all styles, but filter out if excluded
123
+ foreach($html->find('link[rel=stylesheet], style') as $element) {
124
+ if(!in_array($element->outertext, $excl)) {
125
+ $allcss[] = $element;
126
  }
 
 
 
 
127
  }
128
 
129
+ # START CSS LOOP
130
+ foreach($allcss as $k=>$tag) {
 
131
 
132
+ # mediatypes
133
+ if(isset($tag->media)) {
134
 
135
+ # Normalize mediatypes
136
+ if($tag->media == 'screen' || $tag->media == 'screen, print' || strlen($tag->media) == 0) {
137
+ $tag->media = 'all';
 
 
 
 
 
 
 
 
138
  }
139
+
140
+ # Remove print styles
141
+ if(isset($fvm_settings['css']['noprint']) && $fvm_settings['css']['noprint'] == true && $tag->media == 'print'){
142
+ $tag->outertext = '';
143
+ unset($allcss[$k]);
144
+ continue;
 
 
 
 
 
 
 
145
  }
146
+
147
+ } else {
148
+ # must have
149
+ $tag->media = 'all';
150
  }
151
 
152
+
153
+ # START CSS FILES
154
+ if($tag->tag == 'link' && isset($tag->href)) {
155
+
156
+ # filter url
157
+ $href = fvm_normalize_url($tag->href);
158
+
159
+ # Ignore css files
160
+ $ignore_css_merging = false;
161
+ if(isset($fvm_settings['css']['ignore']) && !empty($fvm_settings['css']['ignore'])) {
162
+ $arr = fvm_string_toarray($fvm_settings['css']['ignore']);
163
+ if(is_array($arr) && count($arr) > 0) {
164
+ foreach ($arr as $e) {
165
+ if(stripos($href, $e) !== false) {
166
+ unset($allcss[$k]);
167
+ continue 2;
168
+ }
169
+ }
170
+ }
171
+ }
172
 
173
+ # Remove CSS files
174
+ if(isset($fvm_settings['css']['remove']) && !empty($fvm_settings['css']['remove'])) {
175
+ $arr = fvm_string_toarray($fvm_settings['css']['remove']);
176
+ if(is_array($arr) && count($arr) > 0) {
177
+ foreach ($arr as $e) {
178
+ if(stripos($href, $e) !== false) {
179
+ $tag->outertext = '';
180
+ unset($allcss[$k]);
181
+ continue 2;
182
+ }
183
+ }
184
+ }
185
+ }
186
 
187
+ # css merging functionality
188
+ if(isset($fvm_settings['css']['combine']) && $fvm_settings['css']['combine'] == true) {
189
+
190
+ # download or fetch from transient, minified
191
+ $css = fvm_get_css_from_file($tag);
192
 
193
+ if($css !== false && is_array($css)) {
194
+
195
+ # error
196
+ if(isset($css['error'])) {
197
+ $tag->outertext = '/* Error on '.$href.' : '.$css['error'].' */'. PHP_EOL . $tag->outertext;
198
+ unset($allcss[$k]);
199
+ continue;
200
+ }
201
+
202
+ # extract fonts and icons
203
+ if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
204
+ $extract_fonts_arr = fvm_extract_fonts($css['code'], $href);
205
+ $css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
206
+ $css_code = $extract_fonts_arr['code'];
207
+ } else {
208
+ $css_code = $css['code'];
209
+ }
210
+
211
+
212
+ # async from the list only
213
+ if(isset($fvm_settings['css']['async']) && $fvm_settings['css']['async'] == true) {
214
+ if(isset($fvm_settings['css']['async']) && !empty($fvm_settings['css']['async'])) {
215
+ $arr = fvm_string_toarray($fvm_settings['css']['async']);
216
+ if(is_array($arr) && count($arr) > 0) {
217
+ foreach ($arr as $aa) {
218
+ if(stripos($href, $aa) !== false) {
219
+
220
+ # save css for merging
221
+ $fvm_styles[$tag->media]['async'][] = $css_code;
222
+
223
+ # log
224
+ $size = strlen($css['code']);
225
+ $css_total = $css_total + $size;
226
+ $log[] = '[CSS Async: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $css['url']). PHP_EOL;
227
+
228
+ # finish early
229
+ $tag->outertext = '';
230
+ unset($allcss[$k]);
231
+ continue 2;
232
+
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ # else render block
240
+
241
+ # save css for merging as fallback
242
+ $fvm_styles[$tag->media]['block'][] = $css_code;
243
+
244
+ # log
245
+ $size = strlen($css['code']);
246
+ $css_total = $css_total + $size;
247
+ $log[] = '[CSS Block: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $css['url']). PHP_EOL;
248
+
249
+ # finish early
250
+ $tag->outertext = '';
251
  unset($allcss[$k]);
252
  continue;
253
+
254
  }
255
+ }
256
+
257
+
258
+ # minify individually, if enabled
259
+ if(!isset($fvm_settings['css']['min_disable']) || (isset($fvm_settings['css']['min_disable'])&& $fvm_settings['css']['min_disable'] != true)) {
260
 
261
+ # download or fetch from transient, minified
262
+ $css = fvm_get_css_from_file($tag);
 
 
 
 
 
 
 
263
 
264
+ if($css !== false && is_array($css)) {
265
+
266
+ # error
267
+ if(isset($css['error'])) {
268
+ $tag->outertext = '/* Error on '.$href.' : '.$css['error'].' */'. PHP_EOL . $tag->outertext;
269
+ unset($allcss[$k]);
270
+ continue;
271
+ }
272
+
273
+ # extract fonts and icons
274
+ if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
275
+ $extract_fonts_arr = fvm_extract_fonts($css['code'], $href);
276
+ $css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
277
+ $css_code = $extract_fonts_arr['code'];
278
+ } else {
279
+ $css_code = $css['code'];
280
+ }
281
+
282
+ # empty files
283
+ if(empty(trim($css_code))) {
284
+ $tag->outertext = '';
285
+ unset($allcss[$k]);
286
+ continue;
287
+ } else {
288
+ $css_code = '/* '.$href.' */'. PHP_EOL . $css_code;
289
+ }
290
+
291
+ # generate url
292
+ $ind_css_url = fvm_generate_min_url($href, $css['tkey'], 'css', $css_code);
293
+ if($ind_css_url === false) {
294
+ return $html_src . $fvm_error;
295
+ }
296
+
297
+ # cdn
298
+ if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
299
+ $ind_css_url = fvm_rewrite_cdn_url($ind_css_url);
300
+ }
301
+
302
+ # async from the list only
303
+ if(isset($fvm_settings['css']['async']) && !empty($fvm_settings['css']['async'])) {
304
  $arr = fvm_string_toarray($fvm_settings['css']['async']);
305
  if(is_array($arr) && count($arr) > 0) {
306
  foreach ($arr as $aa) {
307
  if(stripos($href, $aa) !== false) {
308
 
309
+ # async attributes
310
+ $tag->rel = 'preload';
311
+ $tag->as = 'style';
312
+ $tag->onload = "this.rel='stylesheet';this.onload=null";
313
+
314
  # log
315
  $size = strlen($css['code']);
316
  $css_total = $css_total + $size;
317
  $log[] = '[CSS Async: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $css['url']). PHP_EOL;
318
+
319
  # finish early
320
+ $tag->href = $ind_css_url;
321
  unset($allcss[$k]);
322
  continue 2;
323
 
325
  }
326
  }
327
  }
328
+
329
+ # force render blocking for other files
330
+
331
+ # http and html preload for render blocking css
332
+ if(!isset($fvm_settings['css']['nopreload']) || (isset($fvm_settings['css']['nopreload']) && $fvm_settings['css']['nopreload'] != true)) {
333
+ $htmlpreloads[] = '<link rel="preload" href="'.$ind_css_url.'" as="style" media="'.$tag->media.'" />';
334
+ }
335
+
336
+ # log
337
+ $size = strlen($css['code']);
338
+ $css_total = $css_total + $size;
339
+ $log[] = '[CSS Block: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $css['url']). PHP_EOL;
340
+
341
+ # finish early
342
+ $tag->href = $ind_css_url;
343
+ unset($allcss[$k]);
344
+
345
  }
346
+ }
347
+ }
348
+ # END CSS FILES
349
+
350
+
351
+ # START STYLE TAGS
352
+ if($tag->tag == 'style' && !isset($tag->href)) {
353
+
354
+ # remove if empty
355
+ if(strlen(trim($tag->innertext)) == 0) {
 
 
356
  $tag->outertext = '';
357
  unset($allcss[$k]);
358
  continue;
 
359
  }
360
+
361
+ # default
362
+ $css = $tag->innertext;
363
+
364
+ # minify?
365
+ if(!isset($fvm_settings['css']['min_disable_styles']) || (isset($fvm_settings['css']['min_disable_styles'])&& $fvm_settings['css']['min_disable_styles'] != true)) {
366
+ $css = fvm_minify_css_string($css);
367
+ }
368
+
369
+ # handle import rules
370
+ $css = fvm_replace_css_imports($css);
371
+
372
+ # simplify font face
373
+ $arr = fvm_simplify_fontface($css);
374
+ if($arr !== false && is_array($arr)) {
375
+ $css = str_replace($arr['before'], $arr['after'], $css);
376
+ }
377
+
378
+ # extract fonts and icons
379
+ if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
380
+ $extract_fonts_arr = fvm_extract_fonts($css);
381
+ $css_lowpriority_code.= $extract_fonts_arr['fonts'];
382
+ $css = $extract_fonts_arr['code'];
383
+ }
384
+
385
+ # add filtered css code
386
+ if(!empty($css)) {
387
+ $tag->innertext = $css;
388
+ unset($allcss[$k]);
389
+ continue;
390
+ }
391
+
392
+ }
393
+ # END STYLE TAGS
394
+
395
+ }
396
+ # END CSS LOOP
397
+
398
+ # START CSS LOG
399
+ if(count($log) > 1) {
400
+ $log[] = str_pad('-', 22, '-',STR_PAD_LEFT) . PHP_EOL;
401
+ $log[] = '[CSS Total: '.str_pad(fvm_format_filesize($css_total), 9,' ',STR_PAD_LEFT).']' . PHP_EOL;
402
+ $log[] = str_pad('-', 22, '-',STR_PAD_LEFT);
403
+ fvm_save_log(array('type'=>'min','msg'=>implode('', $log)));
404
+ }
405
+ # END CSS LOG
406
 
407
+
408
+ # START OPTIMIZED FONT DELIVERY
409
+ if(!empty($css_lowpriority_code)) {
410
 
411
+ # minify?
412
  if(!isset($fvm_settings['css']['min_disable']) || (isset($fvm_settings['css']['min_disable'])&& $fvm_settings['css']['min_disable'] != true)) {
413
+ $css_lowpriority_code = fvm_minify_css_string($css_lowpriority_code);
414
+ }
415
+
416
+ # save transient, if not yet saved
417
+ $tkey = fvm_generate_hash_with_prefix(hash('sha256', $css_lowpriority_code), 'css');
418
+
419
+ # generate url
420
+ $css_fonts_url = fvm_generate_min_url('fonts', $tkey, 'css', $css_lowpriority_code);
421
+ if($css_fonts_url === false) {
422
+ return $html_src . $fvm_error;
423
+ }
424
+
425
+ # cdn
426
+ if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
427
+ $css_fonts_url = fvm_rewrite_cdn_url($css_fonts_url);
428
+ }
429
+
430
+ # preload
431
+ $htmlcssheader[0] = '<link rel="preload" fetchpriority="low" id="fvmfonts-css" href="'.$css_fonts_url.'" as="style" media="all" onload="this.rel=\'stylesheet\';this.onload=null">';
432
 
433
+
434
+ }
435
+ # END OPTIMIZED FONT DELIVERY
436
+
437
+ # START COMBINING CSS
438
+ if(is_array($fvm_styles) && count($fvm_styles) > 0) {
439
+
440
+ # process mediatypes
441
+ foreach ($fvm_styles as $mediatype=>$css_arr) {
442
 
443
+ # process types, block or async
444
+ foreach ($css_arr as $css_method=>$css_arr2) {
445
+
446
+ # merge and hash
447
+ $merged_css = implode(PHP_EOL, $css_arr2);
448
+ $tkey = fvm_generate_hash_with_prefix(hash('sha256', $merged_css), 'css');
449
 
450
+ # inline if small
451
+ if(strlen($merged_css) < 15000 && $css_method == 'block') {
452
+ $htmlcssheader[] = '<style type="text/css" media="'.$mediatype.'">'.$merged_css.'</style>';
 
453
  continue;
454
  }
455
 
456
+ # url, preload, add
457
+ $merged_css_url = fvm_generate_min_url('combined', $tkey, 'css', $merged_css);
458
+ if($merged_css_url === false) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
459
  return $html_src . $fvm_error;
460
  }
461
+
462
+ # cdn
463
+ if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
464
+ $merged_css_url = fvm_rewrite_cdn_url($merged_css_url);
465
+ }
466
+
467
+ # http, html preload + header
468
+ if($css_method == 'block') {
469
+
470
+ # add to header
471
+ $htmlcssheader[] = '<link rel="stylesheet" href="'.$merged_css_url.'" media="'.$mediatype.'" />';
472
+
473
+ # http and html preload for render blocking css
474
+ if(!isset($fvm_settings['css']['nopreload']) || (isset($fvm_settings['css']['nopreload']) && $fvm_settings['css']['nopreload'] != true)) {
475
+ $htmlpreloads[] = '<link rel="preload" href="'.$merged_css_url.'" as="style" media="'.$mediatype.'" />';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  }
477
+
478
+ } else {
479
+
480
+ # async
481
+ $htmlcssheader[] = '<link rel="preload" as="style" href="'.$merged_css_url.'" media="'.$mediatype.'" onload="this.rel=\'stylesheet\'" />';
482
+
483
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
 
485
  }
486
  }
487
  }
488
+ # END COMBINING CSS
489
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  }
491
+ # END CSS PROCESSING
492
 
 
 
 
 
 
 
 
 
 
493
 
494
+ # START JS PROCESSING
495
+ if(fvm_can_minify_js()) {
496
+
497
+ # defaults
498
+ $scripts_duplicate_check = array();
499
+ $fvm_scripts_header = array();
500
+ $fvm_scripts_defer = array();
501
 
502
+ # start log
503
+ $log = array();
504
+ $js_total = 0;
 
505
 
506
+ # start log
507
+ $log[]= 'PAGE - '. fvm_get_uripath(true) . PHP_EOL . '---' . PHP_EOL;
508
 
509
+ # get all scripts
510
+ $allscripts = array();
511
+ foreach($html->find('script') as $element) {
512
+ $allscripts[] = $element;
513
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514
 
515
+ # START JS LOOP
516
+ if (is_array($allscripts) && count($allscripts) > 0) {
517
+ foreach($allscripts as $k=>$tag) {
518
+
519
+ # handle application/ld+json or application/json before anything else
520
+ if(isset($tag->type) && ($tag->type == 'application/ld+json' || $tag->type == 'application/json')) {
521
+
522
+ # minify
523
+ if(isset($fvm_settings['js']['js_enable_min_inline']) && $fvm_settings['js']['js_enable_min_inline'] == true) { $tag->innertext = fvm_minify_microdata($tag->innertext); }
524
+
525
+ # remove
526
+ unset($allscripts[$k]);
527
  continue;
528
  }
529
 
530
+ # skip unknown script types
531
+ if(isset($tag->type) && $tag->type != 'text/javascript') {
532
+ unset($allscripts[$k]);
533
+ continue;
534
  }
535
+
536
+ # remove default script type
537
+ if(isset($tag->type) && $tag->type == 'text/javascript') { unset($tag->type); }
538
+
539
+ # START JS FILES
540
+ if(isset($tag->src)) {
541
 
542
+ # filter url
543
+ $href = fvm_normalize_url($tag->src);
 
 
544
 
545
+ # ignore js files
546
+ if(isset($fvm_settings['js']['ignore']) && !empty($fvm_settings['js']['ignore'])) {
547
+ $arr = fvm_string_toarray($fvm_settings['js']['ignore']);
548
+ if(is_array($arr) && count($arr) > 0) {
549
+ foreach ($arr as $a) {
550
+ if(stripos($tag->src, $a) !== false) {
551
+ unset($allscripts[$k]);
552
+ continue 2;
553
+ }
554
+ }
555
  }
556
+ }
 
 
 
 
557
 
558
+ # remove js files
559
+ if(isset($fvm_settings['js']['remove']) && !empty($fvm_settings['js']['remove'])) {
560
+ $arr = fvm_string_toarray($fvm_settings['js']['remove']);
561
+ if(is_array($arr) && count($arr) > 0) {
562
+ foreach ($arr as $a) {
563
+ if(stripos($tag->src, $a) !== false) {
564
+ $tag->outertext = '';
565
+ unset($allscripts[$k]);
566
+ continue 2;
567
+ }
568
+ }
569
+ }
570
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
 
572
+
573
+ # JS files to delay until user interaction (unmergeable)
574
+ if(isset($fvm_settings['js']['thirdparty']) && !empty($fvm_settings['js']['thirdparty'])) {
575
+ $arr = fvm_string_toarray($fvm_settings['js']['thirdparty']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
  if(is_array($arr) && count($arr) > 0) {
577
+ foreach ($arr as $ac) {
578
+ if(stripos($tag->src, $ac) !== false) {
579
 
580
+ # unique identifier
581
+ $uid = fvm_generate_hash_with_prefix(hash('sha256', $tag->outertext), 'js');
 
 
 
 
 
 
 
 
582
 
583
+ # remove exact duplicates, or replace transformed tag
584
+ if(isset($scripts_duplicate_check[$uid])) {
 
 
 
 
 
 
 
585
  $tag->outertext = '';
586
+ } else {
587
+ $tag->type = 'fvmdelay';
588
+ $scripts_duplicate_check[$uid] = $uid;
589
+ }
590
+
591
+ # mark as processed, unset and break inner loop
592
+ unset($allscripts[$k]);
593
+ continue 2;
594
+
595
  }
596
  }
597
  }
598
  }
599
 
600
+
601
+ # START MERGING JS
602
+ if(isset($fvm_settings['js']['combine']) && $fvm_settings['js']['combine'] == true) {
603
+
604
+ # force render blocking
605
+ if(isset($fvm_settings['js']['merge_header']) && !empty($fvm_settings['js']['merge_header'])) {
606
+ $arr = fvm_string_toarray($fvm_settings['js']['merge_header']);
607
+ if(is_array($arr) && count($arr) > 0) {
608
+ foreach ($arr as $aa) {
609
+ if(stripos($tag->src, $aa) !== false) {
610
+
611
+ # download or fetch from transient, minified
612
+ $js = fvm_get_js_from_file($tag);
613
+ if($js !== false && is_array($js)) {
614
+
615
+ # error
616
+ if(isset($js['error'])) {
617
+ $tag->outertext = '/* Error on '.$href.' : '.$js['error'].' */'. PHP_EOL . $tag->outertext;
618
+ unset($allscripts[$k]);
619
+ continue 2;
620
+ }
621
 
622
+ # save js for merging
623
+ $fvm_scripts_header[] = $js['code'];
624
+
625
+ # log
626
+ $size = strlen($js['code']);
627
+ $js_total = $js_total + $size;
628
+ $log[] = '[JS Block: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $js['url']). PHP_EOL;
629
+
630
+ # remove from html
631
+ $tag->outertext = '';
632
  unset($allscripts[$k]);
633
  continue 2;
634
+
635
  }
636
+ }
637
+ }
638
+ }
639
+ }
640
+
641
+ # force defer
642
+ if(isset($fvm_settings['js']['merge_defer']) && !empty($fvm_settings['js']['merge_defer'])) {
643
+ $arr = fvm_string_toarray($fvm_settings['js']['merge_defer']);
644
+ if(is_array($arr) && count($arr) > 0) {
645
+ foreach ($arr as $aa) {
646
+ if(stripos($tag->src, $aa) !== false) {
647
 
648
+ # download or fetch from transient, minified
649
+ $js = fvm_get_js_from_file($tag);
650
+ if($js !== false && is_array($js)) {
651
+
652
+ # error
653
+ if(isset($js['error'])) {
654
+ $tag->outertext = '/* Error on '.$href.' : '.$js['error'].' */'. PHP_EOL . $tag->outertext;
655
+ unset($allscripts[$k]);
656
+ continue 2;
657
+ }
658
 
659
+ # save js for merging
660
+ $fvm_scripts_defer[] = $js['code'];
661
+
662
+ # log
663
+ $size = strlen($js['code']);
664
+ $js_total = $js_total + $size;
665
+ $log[] = '[JS Defer: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $js['url']). PHP_EOL;
666
+
667
+ # remove from html
668
+ $tag->outertext = '';
669
+ unset($allscripts[$k]);
670
+ continue 2;
671
+
672
+ }
673
+ }
674
+ }
675
  }
676
  }
677
+
678
+ # jquery needs to load earlier, if not being merged (while merging is active)
679
+ if(stripos($tag->src, '/jquery.js') !== false || stripos($tag->src, '/jquery.min.js') !== false || stripos($tag->src, '/jquery-migrate') !== false) {
680
+
681
+ # http and html preload for render blocking js
682
+ if(!isset($fvm_settings['js']['nopreload']) || (isset($fvm_settings['js']['nopreload']) && $fvm_settings['js']['nopreload'] != true)) {
683
+ $htmlpreloads[] = '<link rel="preload" href="'.$href.'" as="script" />';
684
+ }
685
 
686
+ # header
687
+ if(stripos($tag->src, '/jquery-migrate') !== false) {
688
+ $htmljsheader[1] = "<script data-cfasync='false' src='".$href."'></script>"; # jquery migrate
689
+ } else {
690
+ $htmljsheader[0] = "<script data-cfasync='false' src='".$href."'></script>"; # jquery
691
+ }
692
+
693
+ # content
694
+ $tag->outertext = '';
695
+ unset($allscripts[$k]);
696
+ continue;
697
+ }
698
+
699
+ }
700
+ # END MERGING JS
701
+
702
+
703
+ # START INDIVIDUAL JS MINIFICATION
704
+ # minify individually, if enabled
705
+ if(!isset($fvm_settings['js']['min_disable']) || (isset($fvm_settings['js']['min_disable'])&& $fvm_settings['js']['min_disable'] != true)) {
706
 
707
+ # skip third party scripts, unless allowed
708
+ $allowed = array($fvm_urls['wp_domain'], '/ajax.aspnetcdn.com/ajax/', '/ajax.googleapis.com/ajax/libs/', '/cdnjs.cloudflare.com/ajax/libs/');
709
+ if(str_replace($allowed, '', $href) == $href) {
710
+ unset($allscripts[$k]);
711
+ continue;
712
  }
713
 
714
+ # force render blocking
715
+ if(isset($fvm_settings['js']['merge_header']) && !empty($fvm_settings['js']['merge_header'])) {
716
+ $arr = fvm_string_toarray($fvm_settings['js']['merge_header']);
717
+ if(is_array($arr) && count($arr) > 0) {
718
+ foreach ($arr as $aa) {
719
+ if(stripos($tag->src, $aa) !== false) {
720
+
721
+ # download or fetch from transient, minified
722
+ $js = fvm_get_js_from_file($tag);
723
+ if($js !== false && is_array($js)) {
724
+
725
+ # error
726
+ if(isset($js['error'])) {
727
+ $tag->outertext = '/* Error on '.$href.' : '.$js['error'].' */'. PHP_EOL . $tag->outertext;
728
+ unset($allscripts[$k]);
729
+ continue 2;
730
+ }
731
+
732
+ # generate url
733
+ $ind_js_url = fvm_generate_min_url($tag->src, $js['tkey'], 'js', $js['code']);
734
+ if($ind_js_url === false) {
735
+ return $html_src . $fvm_error;
736
+ }
737
+
738
+ # cdn
739
+ if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
740
+ $ind_js_url = fvm_rewrite_cdn_url($ind_js_url);
741
+ }
742
+
743
+ # render block
744
+ if(isset($tag->async)) { unset($tag->async); }
745
+ if(isset($tag->defer)) { unset($tag->defer); }
746
+
747
+ # http and html preload for render blocking scripts
748
+ if(!isset($fvm_settings['js']['nopreload']) || (isset($fvm_settings['js']['nopreload']) && $fvm_settings['js']['nopreload'] != true)) {
749
+ $htmlpreloads[] = '<link rel="preload" href="'.$ind_js_url.'" as="script" />';
750
+ }
751
+
752
+ # log
753
+ $size = strlen($js['code']);
754
+ $js_total = $js_total + $size;
755
+ $log[] = '[JS Block: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $js['url']). PHP_EOL;
756
+
757
+ # finish early
758
+ $tag->src = $ind_js_url;
759
+ unset($allscripts[$k]);
760
+ continue 2;
761
+
762
+ }
763
+ }
764
+ }
765
+ }
766
  }
767
 
768
+ # force defer
769
+ if(isset($fvm_settings['js']['merge_defer']) && !empty($fvm_settings['js']['merge_defer'])) {
770
+ $arr = fvm_string_toarray($fvm_settings['js']['merge_defer']);
771
+ if(is_array($arr) && count($arr) > 0) {
772
+ foreach ($arr as $aa) {
773
+ if(stripos($tag->src, $aa) !== false) {
774
+
775
+ # download or fetch from transient, minified
776
+ $js = fvm_get_js_from_file($tag);
777
+ if($js !== false && is_array($js)) {
778
+
779
+ # error
780
+ if(isset($js['error'])) {
781
+ $tag->outertext = '/* Error on '.$href.' : '.$js['error'].' */'. PHP_EOL . $tag->outertext;
782
+ unset($allscripts[$k]);
783
+ continue 2;
784
+ }
785
+
786
+ # generate url
787
+ $ind_js_url = fvm_generate_min_url($tag->src, $js['tkey'], 'js', $js['code']);
788
+ if($ind_js_url === false) {
789
+ return $html_src . $fvm_error;
790
+ }
791
+
792
+ # cdn
793
+ if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
794
+ $ind_js_url = fvm_rewrite_cdn_url($ind_js_url);
795
+ }
796
+
797
+ # add defer
798
+ if(!isset($tag->defer)) { $tag->defer = 'defer'; }
799
+
800
+ # log
801
+ $size = strlen($js['code']);
802
+ $js_total = $js_total + $size;
803
+ $log[] = '[JS Defer: '.str_pad(fvm_format_filesize($size), 9,' ',STR_PAD_LEFT).']'."\t".str_replace($fvm_urls['wp_site_url'], '', $js['url']). PHP_EOL;
804
+
805
+ # finish early
806
+ $tag->src = $ind_js_url;
807
+ unset($allscripts[$k]);
808
+ continue 2;
809
+
810
+ }
811
+ }
812
+ }
813
+ }
814
+ }
815
+
816
+ # remove unprocessed scripts from loop
817
  unset($allscripts[$k]);
818
  continue;
819
+ }
820
+ # END INDIVIDUAL JS MINIFICATION
821
+
822
  }
823
+ # END JS FILES
824
 
825
 
826
+ # START INLINED SCRIPTS
827
+ if(!isset($tag->src)) {
828
+
829
+ # remove if empty
830
+ if(strlen(trim($tag->innertext)) == 0) {
831
+ $tag->outertext = '';
832
+ unset($allcss[$k]);
 
833
  continue;
834
  }
835
 
836
+ # default
837
+ $js = ''; $js = $tag->innertext;
838
+
839
+ # minify?
840
+ if(!isset($fvm_settings['js']['min_disable_inline']) || (isset($fvm_settings['js']['min_disable_inline'])&& $fvm_settings['js']['min_disable_inline'] != true)) {
841
+ $js = PHP_EOL . fvm_maybe_minify_js($js, null, true) . PHP_EOL;
842
+ }
843
+
844
+ # delay inline scripts until user interaction (unmergeable)
845
+ if(isset($fvm_settings['js']['thirdparty']) && !empty($fvm_settings['js']['thirdparty'])) {
846
+ $arr = fvm_string_toarray($fvm_settings['js']['thirdparty']);
847
  if(is_array($arr) && count($arr) > 0) {
848
+ foreach ($arr as $b) {
849
+ if(stripos($js, $b) !== false || stripos($js, $b) !== false) {
850
 
851
+ # delay
852
+ $tag->type = 'fvmdelay';
 
 
 
 
 
 
 
 
853
 
854
+ # minified
855
+ if(!empty($js)) {
856
+ $tag->innertext = $js;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
857
  }
858
+
859
+ # unset
860
+ unset($allscripts[$k]);
861
+ continue 2;
862
+ }
863
  }
864
  }
865
  }
866
+
867
+ # defer inline scripts
868
+ if(isset($fvm_settings['js']['defer_dependencies']) && !empty($fvm_settings['js']['defer_dependencies'])) {
869
+ $arr = fvm_string_toarray($fvm_settings['js']['defer_dependencies']);
870
  if(is_array($arr) && count($arr) > 0) {
871
+ foreach ($arr as $b) {
872
+ if((stripos($js, $b) !== false || stripos($js, $b) !== false) && !isset($tag->src)) {
 
 
 
 
 
 
 
 
 
 
 
873
 
874
+ # defer and rawurlencode
875
+ # jquery document ready needs to execute before deferred scripts
876
+ if(!empty($js) && stripos($js, ').ready(') === false) {
877
+ $tag->src = 'data:application/javascript,'.rawurlencode($js);
878
+ $tag->innertext = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
879
  }
880
+
881
+ # unset
882
+ unset($allscripts[$k]);
883
+ continue 2;
884
+ }
885
  }
886
  }
887
+ }
888
+ }
889
+ # END INLINED SCRIPTS
890
+
 
 
 
 
891
  }
892
+ }
893
+ # END JS LOOP
894
+
895
+
896
+ # START JS LOG
897
+ if(count($log) > 1) {
898
+ $log[] = str_pad('-', 21, '-',STR_PAD_LEFT) . PHP_EOL;
899
+ $log[] = '[JS Total: '.str_pad(fvm_format_filesize($js_total), 9,' ',STR_PAD_LEFT).']' . PHP_EOL;
900
+ $log[] = str_pad('-', 21, '-',STR_PAD_LEFT);
901
+ fvm_save_log(array('type'=>'min','msg'=>implode('', $log)));
902
+ }
903
+ # END JS LOG
904
+
905
+ # START COMBINING JS
906
+
907
+ # header scripts
908
+ if(is_array($fvm_scripts_header) && count($fvm_scripts_header) > 0) {
909
 
910
+ # merge code, hash
911
+ $merged_js = implode(PHP_EOL, $fvm_scripts_header);
912
+ $tkey = fvm_generate_hash_with_prefix(hash('sha256', $merged_js), 'js');
913
+
914
+ # generate url
915
+ $merged_js_url = fvm_generate_min_url('combined', $tkey, 'js', $merged_js);
916
+ if($merged_js_url === false) {
917
+ return $html_src . $fvm_error;
918
+ }
 
 
 
919
 
920
+ # cdn
921
+ if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
922
+ $merged_js_url = fvm_rewrite_cdn_url($merged_js_url);
923
  }
924
 
925
+ # http and html preload for render blocking scripts
926
+ if(!isset($fvm_settings['js']['nopreload']) || (isset($fvm_settings['js']['nopreload']) && $fvm_settings['js']['nopreload'] != true)) {
927
+ $htmlpreloads[] = '<link rel="preload" href="'.$merged_js_url.'" as="script" />';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
928
  }
929
+
930
+ # add to header
931
+ $htmljsheader[] = "<script data-cfasync='false' src='".$merged_js_url."'></script>";
932
 
933
+ }
934
+
935
+ # deferred scripts
936
+ if(is_array($fvm_scripts_defer) && count($fvm_scripts_defer) > 0) {
937
+
938
+ # merge code, hash
939
+ $merged_js = implode(PHP_EOL, $fvm_scripts_defer);
940
+ $tkey = fvm_generate_hash_with_prefix(hash('sha256', $merged_js), 'js');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
941
 
942
+ # generate url
943
+ $merged_js_url = fvm_generate_min_url('combined', $tkey, 'js', $merged_js);
944
+ if($merged_js_url === false) {
945
+ return $html_src . $fvm_error;
946
+ }
947
+
948
+ # cdn
949
+ if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
950
+ $merged_js_url = fvm_rewrite_cdn_url($merged_js_url);
951
+ }
952
+
953
+ # header, no preload for deferred files
954
+ $htmljsheader[] = "<script defer='defer' src='".$merged_js_url."'></script>";
955
+
956
  }
957
+
958
  }
959
+ # END JS PROCESSING
 
 
 
 
 
 
 
 
 
 
 
 
960
 
961
+
962
+
963
+ # process HTML minification, if not disabled ###############################
964
+ if(fvm_can_process_html()) {
 
 
 
 
 
 
 
 
965
 
966
+ # Remove HTML comments and IE conditionals
967
+ if(isset($fvm_settings['html']['nocomments']) && $fvm_settings['html']['nocomments'] == true) {
968
+ foreach($html->find('comment') as $element) {
969
+ $element->outertext = '';
970
  }
971
+ }
972
+
973
+ # Remove generator tags
974
+ if(isset($fvm_settings['html']['cleanup_header']) && $fvm_settings['html']['cleanup_header'] == true) {
975
 
976
+ # remove
977
+ foreach($html->find('head meta[name=generator], head link[rel=shortlink], head link[rel=dns-prefetch], head link[rel=preconnect], head link[rel=prefetch], head link[rel=prerender], head meta[name*=msapplication], head link[rel=apple-touch-icon], head link[rel=EditURI], head link[rel=preconnect], head link[rel=wlwmanifest], head link[rel=https://api.w.org/], head link[href*=/wp-json/], head link[rel=pingback], head link[type=application/json+oembed], head link[type=text/xml+oembed]') as $element) {
978
+ $element->outertext = '';
979
  }
980
 
981
+ # allow only the last link[rel=icon]
982
+ $ic = array(); $ic = $html->find('head link[rel=icon]');
983
+ $i = 1; $len = count($ic);
984
+ if($len > 1) {
985
+ foreach($ic as $element) {
986
+ if ($i != $len) { $element->outertext = ''; } $i++; # delete except if last
987
+ }
988
+ }
989
+ }
990
+
991
  }
992
 
993
+ # build extra head and footer ###############################
994
+
995
+ # header and footer markers
996
+ $hm = '<!-- h_preheader --><!-- h_header_function -->';
997
+ $hm_late = '<!-- h_cssheader --><!-- h_jsheader -->';
998
+ $fm = '<!-- h_footer_fvm_scripts -->';
999
+
1000
+ # add our function to head
1001
+ if(fvm_can_minify_css() || fvm_can_minify_js()) {
1002
+ $hm = fvm_add_header_function($hm);
1003
+ }
1004
 
1005
+ # move viewport meta tag up
1006
+ if(!is_null($html->find('meta[viewport]', 0))) {
1007
+ $hm = str_replace('<!-- h_preheader -->', $html->find('meta[viewport]', 0)->outertext.'<!-- h_preheader -->', $hm);
1008
+ foreach($html->find('meta[viewport]') as $element) { $element->outertext = ''; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1009
  }
1010
 
1011
+ # move charset meta tag up
1012
+ if(!is_null($html->find('meta[charset]', 0))) {
1013
+ $hm = str_replace('<!-- h_preheader -->', $html->find('meta[charset]', 0)->outertext.'<!-- h_preheader -->', $hm);
1014
+ foreach($html->find('meta[charset]') as $element) { $element->outertext = ''; }
 
 
 
 
 
 
 
 
 
1015
  }
1016
 
1017
+ # remove other meta tag and collect them between preload and css/js files
1018
+ foreach($html->find('head meta, head title, head link[rel=canonical], head link[rel=alternate], head link[rel=pingback], head script[type=application/ld+json]') as $element) {
1019
+ $hm_late = str_replace('<!-- h_cssheader -->', $element->outertext.'<!-- h_cssheader -->', $hm_late);
1020
+ $element->outertext = '';
1021
+ }
1022
+
1023
+ # preload headers, by fetchpriority attribute
1024
+ if(is_array($htmlpreloads)) {
1025
+
1026
+ # deduplicate
1027
+ $htmlpreloads = array_unique($htmlpreloads);
1028
 
1029
+ # get values
1030
+ $pre_html = array_values($htmlpreloads);
1031
+
1032
+ # add preload html header
1033
+ if(count($pre_html) > 0) {
1034
+ $hm = str_replace('<!-- h_preheader -->', implode(PHP_EOL, $pre_html).'<!-- h_preheader -->', $hm);
1035
  }
1036
 
1037
+ }
1038
+
1039
+ # add stylesheets
1040
+ if(isset($htmlcssheader)) {
1041
+ if(is_array($htmlcssheader) && count($htmlcssheader) > 0) {
1042
+ ksort($htmlcssheader); # priority
1043
+ $hm_late = str_replace('<!-- h_cssheader -->', implode(PHP_EOL, $htmlcssheader).'<!-- h_cssheader -->', $hm_late);
1044
  }
1045
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1046
 
1047
+ # add header scripts
1048
+ if(isset($htmljsheader)) {
1049
+ if(is_array($htmljsheader) && count($htmljsheader) > 0) {
1050
+ ksort($htmljsheader); # priority
1051
+ $hm_late = str_replace('<!-- h_jsheader -->', implode(PHP_EOL, $htmljsheader).'<!-- h_jsheader -->', $hm_late);
1052
+ }
1053
+ }
 
 
 
 
 
 
 
1054
 
1055
+ # add defer scripts
1056
+ if(isset($htmljscodedefer)) {
1057
+ if(is_array($htmljscodedefer) && count($htmljscodedefer) > 0) {
1058
+ ksort($htmljscodedefer); # priority
1059
+ $hm_late = str_replace('<!-- h_jsheader -->', implode(PHP_EOL, $htmljscodedefer), $hm_late);
1060
+ }
1061
+ }
1062
 
1063
+ # add fvm_footer scripts, if enabled
1064
+ if(fvm_can_minify_js()) {
1065
+ $fm = fvm_add_footer_function($fm);
1066
+ }
1067
+
1068
+ # cleanup leftover markers
1069
+ $hm = str_replace(array('<!-- h_preheader -->', '<!-- h_header_function -->'), '', $hm);
1070
+ $hm_late = str_replace(array('<!-- h_cssheader -->', '<!-- h_jsheader -->'), '', $hm_late);
1071
+ $fm = str_replace('<!-- h_footer_fvm_scripts -->', '', $fm);
1072
 
1073
+ # append header and footer
1074
+ if(!is_null($html->find('head', 0)) && !is_null($html->find('body', -1))) {
1075
+ if(!is_null($html->find('head', 0)->first_child()) && !is_null($html->find('body', -1)->last_child())) {
1076
+ $html->find('head', 0)->first_child()->outertext = $hm . $html->find('head', 0)->first_child()->outertext . $hm_late;
1077
+ $html->find('body', -1)->last_child()->outertext = $html->find('body', -1)->last_child ()->outertext . $fm;
1078
+ }
1079
  }
1080
 
 
1081
 
1082
+ # convert html object to string, save all objects to string
1083
+ $html = trim($html->save());
1084
+
1085
+ # process cdn optimization
1086
+ if(fvm_can_process_cdn()) {
1087
+ $html = fvm_process_cdn($html);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1088
  }
1089
+
1090
+ # minify remaining HTML at the end, if enabled
1091
+ if(fvm_can_process_html()) {
1092
+ if(!isset($fvm_settings['html']['min_disable']) || (isset($fvm_settings['html']['min_disable']) && $fvm_settings['html']['min_disable'] != true)) {
1093
+ $html = fvm_raisermin_html($html);
1094
+ }
 
 
 
 
 
 
 
 
 
 
 
1095
  }
1096
+
1097
+ # filter final html if needed
1098
+ if(function_exists('fvm_filter_final_html')) {
1099
+ $html = fvm_filter_final_html($html);
 
 
 
 
 
 
 
 
 
 
 
1100
  }
 
1101
 
 
 
 
1102
  }
1103
 
1104
  # return html
libs/matthiasmullie/minify/src/CSS.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
 
2
  /**
3
- * CSS Minifier
4
  *
5
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
  *
@@ -12,15 +13,14 @@
12
  namespace FVM\MatthiasMullie\Minify;
13
 
14
  use FVM\MatthiasMullie\Minify\Exceptions\FileImportException;
15
- use FVM\MatthiasMullie\PathConverter\ConverterInterface;
16
  use FVM\MatthiasMullie\PathConverter\Converter;
 
17
 
18
  /**
19
- * CSS minifier
20
  *
21
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
22
  *
23
- * @package Minify
24
  * @author Matthias Mullie <minify@mullie.eu>
25
  * @author Tijs Verkoyen <minify@verkoyen.eu>
26
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
@@ -44,6 +44,10 @@ class CSS extends Minify
44
  'jpeg' => 'data:image/jpeg',
45
  'svg' => 'data:image/svg+xml',
46
  'woff' => 'data:application/x-font-woff',
 
 
 
 
47
  'tif' => 'image/tiff',
48
  'tiff' => 'image/tiff',
49
  'xbm' => 'image/x-xbitmap',
@@ -93,7 +97,7 @@ class CSS extends Minify
93
  }
94
 
95
  // add to top
96
- $content = implode(';', $matches[2]).';'.trim($content, ';');
97
  }
98
 
99
  return $content;
@@ -102,8 +106,8 @@ class CSS extends Minify
102
  /**
103
  * Combine CSS from import statements.
104
  *
105
- * @import's will be loaded and their content merged into the original file,
106
- * to save HTTP requests.
107
  *
108
  * @param string $source The file to combine imports for
109
  * @param string $content The CSS content to combine imports for
@@ -200,7 +204,7 @@ class CSS extends Minify
200
  // loop the matches
201
  foreach ($matches as $match) {
202
  // get the path for the file that will be imported
203
- $importPath = dirname($source).'/'.$match['path'];
204
 
205
  // only replace the import with the content if we can grab the
206
  // content of the file
@@ -211,19 +215,19 @@ class CSS extends Minify
211
  // check if current file was not imported previously in the same
212
  // import chain.
213
  if (in_array($importPath, $parents)) {
214
- throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
215
  }
216
 
217
  // grab referenced file & minify it (which may include importing
218
  // yet other @import statements recursively)
219
- $minifier = new static($importPath);
220
  $minifier->setMaxImportSize($this->maxImportSize);
221
  $minifier->setImportExtensions($this->importExtensions);
222
  $importContent = $minifier->execute($source, $parents);
223
 
224
  // check if this is only valid for certain media
225
  if (!empty($match['media'])) {
226
- $importContent = '@media '.$match['media'].'{'.$importContent.'}';
227
  }
228
 
229
  // add to replacement array
@@ -262,7 +266,7 @@ class CSS extends Minify
262
 
263
  // get the path for the file that will be imported
264
  $path = $match[2];
265
- $path = dirname($source).'/'.$path;
266
 
267
  // only replace the import with the content if we're able to get
268
  // the content of the file, and it's relatively small
@@ -273,7 +277,7 @@ class CSS extends Minify
273
 
274
  // build replacement
275
  $search[] = $match[0];
276
- $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
277
  }
278
  }
279
 
@@ -289,7 +293,7 @@ class CSS extends Minify
289
  * Perform CSS optimizations.
290
  *
291
  * @param string[optional] $path Path to write the data to
292
- * @param string[] $parents Parent paths, for circular reference checks
293
  *
294
  * @return string The minified data
295
  */
@@ -307,7 +311,8 @@ class CSS extends Minify
307
  */
308
  $this->extractStrings();
309
  $this->stripComments();
310
- $this->extractCalcs();
 
311
  $css = $this->replace($css);
312
 
313
  $css = $this->stripWhitespace($css);
@@ -464,9 +469,9 @@ class CSS extends Minify
464
  // build replacement
465
  $search[] = $match[0];
466
  if ($type === 'url') {
467
- $replace[] = 'url('.$url.')';
468
  } elseif ($type === 'import') {
469
- $replace[] = '@import "'.$url.'"';
470
  }
471
  }
472
 
@@ -525,7 +530,7 @@ class CSS extends Minify
525
  );
526
 
527
  return preg_replace_callback(
528
- '/(?<=[: ])('.implode('|', array_keys($colors)).')(?=[; }])/i',
529
  function ($match) use ($colors) {
530
  return $colors[strtoupper($match[0])];
531
  },
@@ -548,10 +553,10 @@ class CSS extends Minify
548
  );
549
 
550
  $callback = function ($match) use ($weights) {
551
- return $match[1].$weights[$match[2]];
552
  };
553
 
554
- return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
555
  }
556
 
557
  /**
@@ -563,7 +568,47 @@ class CSS extends Minify
563
  */
564
  protected function shortenZeroes($content)
565
  {
566
- // removed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  return $content;
568
  }
569
 
@@ -591,7 +636,7 @@ class CSS extends Minify
591
  $minifier = $this;
592
  $callback = function ($match) use ($minifier) {
593
  $count = count($minifier->extracted);
594
- $placeholder = '/*'.$count.'*/';
595
  $minifier->extracted[$placeholder] = $match[0];
596
 
597
  return $placeholder;
@@ -629,7 +674,7 @@ class CSS extends Minify
629
  // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
630
  // selectors like `div.weird- p`
631
  $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
632
- $content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
633
 
634
  // remove semicolon/whitespace followed by closing bracket
635
  $content = str_replace(';}', '}', $content);
@@ -638,38 +683,71 @@ class CSS extends Minify
638
  }
639
 
640
  /**
641
- * Replace all `calc()` occurrences.
 
642
  */
643
- protected function extractCalcs()
644
  {
 
 
 
645
  // PHP only supports $this inside anonymous functions since 5.4
646
  $minifier = $this;
647
- $callback = function ($match) use ($minifier) {
648
- $length = strlen($match[1]);
 
649
  $expr = '';
650
  $opened = 0;
651
 
652
- for ($i = 0; $i < $length; $i++) {
653
- $char = $match[1][$i];
 
 
 
 
 
654
  $expr .= $char;
655
  if ($char === '(') {
656
- $opened++;
657
  } elseif ($char === ')' && --$opened === 0) {
658
  break;
659
  }
660
  }
661
- $rest = str_replace($expr, '', $match[1]);
662
- $expr = trim(substr($expr, 1, -1));
663
 
 
664
  $count = count($minifier->extracted);
665
- $placeholder = 'calc('.$count.')';
666
- $minifier->extracted[$placeholder] = 'calc('.$expr.')';
667
 
668
- return $placeholder.$rest;
 
 
 
 
 
 
669
  };
670
 
671
- $this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/', $callback);
672
- $this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/m', $callback);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
  }
674
 
675
  /**
1
  <?php
2
+
3
  /**
4
+ * CSS Minifier.
5
  *
6
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
7
  *
13
  namespace FVM\MatthiasMullie\Minify;
14
 
15
  use FVM\MatthiasMullie\Minify\Exceptions\FileImportException;
 
16
  use FVM\MatthiasMullie\PathConverter\Converter;
17
+ use FVM\MatthiasMullie\PathConverter\ConverterInterface;
18
 
19
  /**
20
+ * CSS minifier.
21
  *
22
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
23
  *
 
24
  * @author Matthias Mullie <minify@mullie.eu>
25
  * @author Tijs Verkoyen <minify@verkoyen.eu>
26
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
44
  'jpeg' => 'data:image/jpeg',
45
  'svg' => 'data:image/svg+xml',
46
  'woff' => 'data:application/x-font-woff',
47
+ 'woff2' => 'data:application/x-font-woff2',
48
+ 'avif' => 'data:image/avif',
49
+ 'apng' => 'data:image/apng',
50
+ 'webp' => 'data:image/webp',
51
  'tif' => 'image/tiff',
52
  'tiff' => 'image/tiff',
53
  'xbm' => 'image/x-xbitmap',
97
  }
98
 
99
  // add to top
100
+ $content = implode(';', $matches[2]) . ';' . trim($content, ';');
101
  }
102
 
103
  return $content;
106
  /**
107
  * Combine CSS from import statements.
108
  *
109
+ * Import statements will be loaded and their content merged into the original
110
+ * file, to save HTTP requests.
111
  *
112
  * @param string $source The file to combine imports for
113
  * @param string $content The CSS content to combine imports for
204
  // loop the matches
205
  foreach ($matches as $match) {
206
  // get the path for the file that will be imported
207
+ $importPath = dirname($source) . '/' . $match['path'];
208
 
209
  // only replace the import with the content if we can grab the
210
  // content of the file
215
  // check if current file was not imported previously in the same
216
  // import chain.
217
  if (in_array($importPath, $parents)) {
218
+ throw new FileImportException('Failed to import file "' . $importPath . '": circular reference detected.');
219
  }
220
 
221
  // grab referenced file & minify it (which may include importing
222
  // yet other @import statements recursively)
223
+ $minifier = new self($importPath);
224
  $minifier->setMaxImportSize($this->maxImportSize);
225
  $minifier->setImportExtensions($this->importExtensions);
226
  $importContent = $minifier->execute($source, $parents);
227
 
228
  // check if this is only valid for certain media
229
  if (!empty($match['media'])) {
230
+ $importContent = '@media ' . $match['media'] . '{' . $importContent . '}';
231
  }
232
 
233
  // add to replacement array
266
 
267
  // get the path for the file that will be imported
268
  $path = $match[2];
269
+ $path = dirname($source) . '/' . $path;
270
 
271
  // only replace the import with the content if we're able to get
272
  // the content of the file, and it's relatively small
277
 
278
  // build replacement
279
  $search[] = $match[0];
280
+ $replace[] = 'url(' . $this->importExtensions[$extension] . ';base64,' . $importContent . ')';
281
  }
282
  }
283
 
293
  * Perform CSS optimizations.
294
  *
295
  * @param string[optional] $path Path to write the data to
296
+ * @param string[] $parents Parent paths, for circular reference checks
297
  *
298
  * @return string The minified data
299
  */
311
  */
312
  $this->extractStrings();
313
  $this->stripComments();
314
+ $this->extractMath();
315
+ $this->extractCustomProperties();
316
  $css = $this->replace($css);
317
 
318
  $css = $this->stripWhitespace($css);
469
  // build replacement
470
  $search[] = $match[0];
471
  if ($type === 'url') {
472
+ $replace[] = 'url(' . $url . ')';
473
  } elseif ($type === 'import') {
474
+ $replace[] = '@import "' . $url . '"';
475
  }
476
  }
477
 
530
  );
531
 
532
  return preg_replace_callback(
533
+ '/(?<=[: ])(' . implode('|', array_keys($colors)) . ')(?=[; }])/i',
534
  function ($match) use ($colors) {
535
  return $colors[strtoupper($match[0])];
536
  },
553
  );
554
 
555
  $callback = function ($match) use ($weights) {
556
+ return $match[1] . $weights[$match[2]];
557
  };
558
 
559
+ return preg_replace_callback('/(font-weight\s*:\s*)(' . implode('|', array_keys($weights)) . ')(?=[;}])/', $callback, $content);
560
  }
561
 
562
  /**
568
  */
569
  protected function shortenZeroes($content)
570
  {
571
+ // we don't want to strip units in `calc()` expressions:
572
+ // `5px - 0px` is valid, but `5px - 0` is not
573
+ // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
574
+ // `10 * 0` is invalid
575
+ // we've extracted calcs earlier, so we don't need to worry about this
576
+
577
+ // reusable bits of code throughout these regexes:
578
+ // before & after are used to make sure we don't match lose unintended
579
+ // 0-like values (e.g. in #000, or in http://url/1.0)
580
+ // units can be stripped from 0 values, or used to recognize non 0
581
+ // values (where wa may be able to strip a .0 suffix)
582
+ $before = '(?<=[:(, ])';
583
+ $after = '(?=[ ,);}])';
584
+ $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
585
+
586
+ // strip units after zeroes (0px -> 0)
587
+ // NOTE: it should be safe to remove all units for a 0 value, but in
588
+ // practice, Webkit (especially Safari) seems to stumble over at least
589
+ // 0%, potentially other units as well. Only stripping 'px' for now.
590
+ // @see https://github.com/matthiasmullie/minify/issues/60
591
+ $content = preg_replace('/' . $before . '(-?0*(\.0+)?)(?<=0)px' . $after . '/', '\\1', $content);
592
+
593
+ // strip 0-digits (.0 -> 0)
594
+ $content = preg_replace('/' . $before . '\.0+' . $units . '?' . $after . '/', '0\\1', $content);
595
+ // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
596
+ $content = preg_replace('/' . $before . '(-?[0-9]+\.[0-9]+)0+' . $units . '?' . $after . '/', '\\1\\2', $content);
597
+ // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
598
+ $content = preg_replace('/' . $before . '(-?[0-9]+)\.0+' . $units . '?' . $after . '/', '\\1\\2', $content);
599
+ // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
600
+ $content = preg_replace('/' . $before . '(-?)0+([0-9]*\.[0-9]+)' . $units . '?' . $after . '/', '\\1\\2\\3', $content);
601
+
602
+ // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
603
+ $content = preg_replace('/' . $before . '-?0+' . $units . '?' . $after . '/', '0\\1', $content);
604
+
605
+ // IE doesn't seem to understand a unitless flex-basis value (correct -
606
+ // it goes against the spec), so let's add it in again (make it `%`,
607
+ // which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
608
+ // @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
609
+ $content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
610
+ $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
611
+
612
  return $content;
613
  }
614
 
636
  $minifier = $this;
637
  $callback = function ($match) use ($minifier) {
638
  $count = count($minifier->extracted);
639
+ $placeholder = '/*' . $count . '*/';
640
  $minifier->extracted[$placeholder] = $match[0];
641
 
642
  return $placeholder;
674
  // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
675
  // selectors like `div.weird- p`
676
  $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
677
+ $content = preg_replace('/:(' . implode('|', $pseudos) . ')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
678
 
679
  // remove semicolon/whitespace followed by closing bracket
680
  $content = str_replace(';}', '}', $content);
683
  }
684
 
685
  /**
686
+ * Replace all occurrences of functions that may contain math, where
687
+ * whitespace around operators needs to be preserved (e.g. calc, clamp).
688
  */
689
+ protected function extractMath()
690
  {
691
+ $functions = array('calc', 'clamp', 'min', 'max');
692
+ $pattern = '/\b(' . implode('|', $functions) . ')(\(.+?)(?=$|;|})/m';
693
+
694
  // PHP only supports $this inside anonymous functions since 5.4
695
  $minifier = $this;
696
+ $callback = function ($match) use ($minifier, $pattern, &$callback) {
697
+ $function = $match[1];
698
+ $length = strlen($match[2]);
699
  $expr = '';
700
  $opened = 0;
701
 
702
+ // the regular expression for extracting math has 1 significant problem:
703
+ // it can't determine the correct closing parenthesis...
704
+ // instead, it'll match a larger portion of code to where it's certain that
705
+ // the calc() musts have ended, and we'll figure out which is the correct
706
+ // closing parenthesis here, by counting how many have opened
707
+ for ($i = 0; $i < $length; ++$i) {
708
+ $char = $match[2][$i];
709
  $expr .= $char;
710
  if ($char === '(') {
711
+ ++$opened;
712
  } elseif ($char === ')' && --$opened === 0) {
713
  break;
714
  }
715
  }
 
 
716
 
717
+ // now that we've figured out where the calc() starts and ends, extract it
718
  $count = count($minifier->extracted);
719
+ $placeholder = 'math(' . $count . ')';
720
+ $minifier->extracted[$placeholder] = $function . '(' . trim(substr($expr, 1, -1)) . ')';
721
 
722
+ // and since we've captured more code than required, we may have some leftover
723
+ // calc() in here too - go recursive on the remaining but of code to go figure
724
+ // that out and extract what is needed
725
+ $rest = $minifier->str_replace_first($function . $expr, '', $match[0]);
726
+ $rest = preg_replace_callback($pattern, $callback, $rest);
727
+
728
+ return $placeholder . $rest;
729
  };
730
 
731
+ $this->registerPattern($pattern, $callback);
732
+ }
733
+
734
+ /**
735
+ * Replace custom properties, whose values may be used in scenarios where
736
+ * we wouldn't want them to be minified (e.g. inside calc).
737
+ */
738
+ protected function extractCustomProperties()
739
+ {
740
+ // PHP only supports $this inside anonymous functions since 5.4
741
+ $minifier = $this;
742
+ $this->registerPattern(
743
+ '/(?<=^|[;}{])\s*(--[^:;{}"\'\s]+)\s*:([^;{}]+)/m',
744
+ function ($match) use ($minifier) {
745
+ $placeholder = '--custom-' . count($minifier->extracted) . ':0';
746
+ $minifier->extracted[$placeholder] = $match[1] . ':' . trim($match[2]);
747
+
748
+ return $placeholder;
749
+ }
750
+ );
751
  }
752
 
753
  /**
libs/matthiasmullie/minify/src/Exception.php CHANGED
@@ -1,18 +1,20 @@
1
  <?php
 
2
  /**
3
- * Base Exception
4
  *
5
  * @deprecated Use Exceptions\BasicException instead
6
  *
7
  * @author Matthias Mullie <minify@mullie.eu>
8
  */
 
9
  namespace FVM\MatthiasMullie\Minify;
10
 
11
  /**
12
- * Base Exception Class
 
13
  * @deprecated Use Exceptions\BasicException instead
14
  *
15
- * @package Minify
16
  * @author Matthias Mullie <minify@mullie.eu>
17
  */
18
  abstract class Exception extends \Exception
1
  <?php
2
+
3
  /**
4
+ * Base Exception.
5
  *
6
  * @deprecated Use Exceptions\BasicException instead
7
  *
8
  * @author Matthias Mullie <minify@mullie.eu>
9
  */
10
+
11
  namespace FVM\MatthiasMullie\Minify;
12
 
13
  /**
14
+ * Base Exception Class.
15
+ *
16
  * @deprecated Use Exceptions\BasicException instead
17
  *
 
18
  * @author Matthias Mullie <minify@mullie.eu>
19
  */
20
  abstract class Exception extends \Exception
libs/matthiasmullie/minify/src/Exceptions/BasicException.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
 
2
  /**
3
- * Basic exception
4
  *
5
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
  *
@@ -8,14 +9,14 @@
8
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
  * @license MIT License
10
  */
 
11
  namespace FVM\MatthiasMullie\Minify\Exceptions;
12
 
13
  use FVM\MatthiasMullie\Minify\Exception;
14
 
15
  /**
16
- * Basic Exception Class
17
  *
18
- * @package Minify\Exception
19
  * @author Matthias Mullie <minify@mullie.eu>
20
  */
21
  abstract class BasicException extends Exception
1
  <?php
2
+
3
  /**
4
+ * Basic exception.
5
  *
6
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
7
  *
9
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
10
  * @license MIT License
11
  */
12
+
13
  namespace FVM\MatthiasMullie\Minify\Exceptions;
14
 
15
  use FVM\MatthiasMullie\Minify\Exception;
16
 
17
  /**
18
+ * Basic Exception Class.
19
  *
 
20
  * @author Matthias Mullie <minify@mullie.eu>
21
  */
22
  abstract class BasicException extends Exception
libs/matthiasmullie/minify/src/Exceptions/FileImportException.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
 
2
  /**
3
- * File Import Exception
4
  *
5
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
  *
@@ -8,12 +9,12 @@
8
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
  * @license MIT License
10
  */
 
11
  namespace FVM\MatthiasMullie\Minify\Exceptions;
12
 
13
  /**
14
- * File Import Exception Class
15
  *
16
- * @package Minify\Exception
17
  * @author Matthias Mullie <minify@mullie.eu>
18
  */
19
  class FileImportException extends BasicException
1
  <?php
2
+
3
  /**
4
+ * File Import Exception.
5
  *
6
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
7
  *
9
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
10
  * @license MIT License
11
  */
12
+
13
  namespace FVM\MatthiasMullie\Minify\Exceptions;
14
 
15
  /**
16
+ * File Import Exception Class.
17
  *
 
18
  * @author Matthias Mullie <minify@mullie.eu>
19
  */
20
  class FileImportException extends BasicException
libs/matthiasmullie/minify/src/Exceptions/IOException.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
 
2
  /**
3
- * IO Exception
4
  *
5
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
  *
@@ -8,12 +9,12 @@
8
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
  * @license MIT License
10
  */
 
11
  namespace FVM\MatthiasMullie\Minify\Exceptions;
12
 
13
  /**
14
- * IO Exception Class
15
  *
16
- * @package Minify\Exception
17
  * @author Matthias Mullie <minify@mullie.eu>
18
  */
19
  class IOException extends BasicException
1
  <?php
2
+
3
  /**
4
+ * IO Exception.
5
  *
6
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
7
  *
9
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
10
  * @license MIT License
11
  */
12
+
13
  namespace FVM\MatthiasMullie\Minify\Exceptions;
14
 
15
  /**
16
+ * IO Exception Class.
17
  *
 
18
  * @author Matthias Mullie <minify@mullie.eu>
19
  */
20
  class IOException extends BasicException
libs/matthiasmullie/minify/src/JS.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
 
2
  /**
3
- * JavaScript minifier
4
  *
5
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
  *
@@ -8,14 +9,14 @@
8
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
  * @license MIT License
10
  */
 
11
  namespace FVM\MatthiasMullie\Minify;
12
 
13
  /**
14
- * JavaScript Minifier Class
15
  *
16
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
17
  *
18
- * @package Minify
19
  * @author Matthias Mullie <minify@mullie.eu>
20
  * @author Tijs Verkoyen <minify@verkoyen.eu>
21
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
@@ -29,6 +30,8 @@ class JS extends Minify
29
  * Note that regular expressions using that bit must have the PCRE_UTF8
30
  * pattern modifier (/u) set.
31
  *
 
 
32
  * @var string
33
  */
34
  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';
@@ -124,16 +127,16 @@ class JS extends Minify
124
  */
125
  public function __construct()
126
  {
127
- call_user_func_array(array('parent', '__construct'), func_get_args());
128
 
129
- $dataDir = __DIR__.'/../data/js/';
130
  $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
131
- $this->keywordsReserved = file($dataDir.'keywords_reserved.txt', $options);
132
- $this->keywordsBefore = file($dataDir.'keywords_before.txt', $options);
133
- $this->keywordsAfter = file($dataDir.'keywords_after.txt', $options);
134
- $this->operators = file($dataDir.'operators.txt', $options);
135
- $this->operatorsBefore = file($dataDir.'operators_before.txt', $options);
136
- $this->operatorsAfter = file($dataDir.'operators_after.txt', $options);
137
  }
138
 
139
  /**
@@ -174,7 +177,7 @@ class JS extends Minify
174
  $js = $this->stripWhitespace($js);
175
 
176
  // combine js: separating the scripts by a ;
177
- $content .= $js.";";
178
  }
179
 
180
  // clean up leftover `;`s from the combination of multiple scripts
@@ -198,15 +201,25 @@ class JS extends Minify
198
  // PHP only supports $this inside anonymous functions since 5.4
199
  $minifier = $this;
200
  $callback = function ($match) use ($minifier) {
201
- $count = count($minifier->extracted);
202
- $placeholder = '/*'.$count.'*/';
203
- $minifier->extracted[$placeholder] = $match[0];
 
 
 
 
 
 
 
 
 
 
204
 
205
- return $placeholder;
206
  };
 
207
  // multi-line comments
208
- $this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
209
- $this->registerPattern('/\/\*.*?\*\//s', '');
210
 
211
  // single-line comments
212
  $this->registerPattern('/\/\/.*$/m', '');
@@ -235,7 +248,7 @@ class JS extends Minify
235
  $minifier = $this;
236
  $callback = function ($match) use ($minifier) {
237
  $count = count($minifier->extracted);
238
- $placeholder = '"'.$count.'"';
239
  $minifier->extracted[$placeholder] = $match[0];
240
 
241
  return $placeholder;
@@ -254,7 +267,7 @@ class JS extends Minify
254
  // of the RegExp methods (a `\` followed by a variable or value is
255
  // likely part of a division, not a regex)
256
  $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
257
- $before = '([=:,;\+\-\*\/\}\(\{\[&\|!]|^|'.implode('|', $keywords).')\s*';
258
  $propertiesAndMethods = array(
259
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
260
  'constructor',
@@ -274,8 +287,8 @@ class JS extends Minify
274
  );
275
  $delimiters = array_fill(0, count($propertiesAndMethods), '/');
276
  $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
277
- $after = '(?=\s*([\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).')))';
278
- $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
279
 
280
  // regular expressions following a `)` are rather annoying to detect...
281
  // quite often, `/` after `)` is a division operator & if it happens to
@@ -289,8 +302,8 @@ class JS extends Minify
289
  // if a regex following `)` is not followed by `.<property or method>`,
290
  // it's quite likely not a regex
291
  $before = '\)\s*';
292
- $after = '(?=\s*\.('.implode('|', $propertiesAndMethods).'))';
293
- $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
294
 
295
  // 1 more edge case: a regex can be followed by a lot more operators or
296
  // keywords if there's a newline (ASI) in between, where the operator
@@ -298,8 +311,8 @@ class JS extends Minify
298
  // (https://github.com/matthiasmullie/minify/issues/56)
299
  $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
300
  $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
301
- $after = '(?=\s*\n\s*('.implode('|', $operators).'))';
302
- $this->registerPattern('/'.$pattern.$after.'/', $callback);
303
  }
304
 
305
  /**
@@ -343,8 +356,8 @@ class JS extends Minify
343
  unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
344
  $content = preg_replace(
345
  array(
346
- '/('.implode('|', $operatorsBefore).')\s+/',
347
- '/\s+('.implode('|', $operatorsAfter).')/',
348
  ),
349
  '\\1',
350
  $content
@@ -361,8 +374,8 @@ class JS extends Minify
361
  );
362
 
363
  // collapse whitespace around reserved words into single space
364
- $content = preg_replace('/(^|[;\}\s])\K('.implode('|', $keywordsBefore).')\s+/', '\\2 ', $content);
365
- $content = preg_replace('/\s+('.implode('|', $keywordsAfter).')(?=([;\{\s]|$))/', ' \\1', $content);
366
 
367
  /*
368
  * We didn't strip whitespace after a couple of operators because they
@@ -372,8 +385,8 @@ class JS extends Minify
372
  */
373
  $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
374
  $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
375
- $content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
376
- $content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
377
 
378
  /*
379
  * Whitespace after `return` can be omitted in a few occasions
@@ -405,9 +418,26 @@ class JS extends Minify
405
  * to be the for-loop's body... Same goes for while loops.
406
  * I'm going to double that semicolon (if any) so after the next line,
407
  * which strips semicolons here & there, we're still left with this one.
 
 
 
 
 
 
 
 
408
  */
409
- $content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
 
 
 
410
  $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
 
 
 
 
 
 
411
  /*
412
  * Below will also keep `;` after a `do{}while();` along with `while();`
413
  * While these could be stripped after do-while, detecting this
@@ -465,7 +495,7 @@ class JS extends Minify
465
 
466
  // don't confuse = with other assignment shortcuts (e.g. +=)
467
  $chars = preg_quote('+-*\=<>%&|', $delimiter);
468
- $operators['='] = '(?<!['.$chars.'])\=';
469
 
470
  return $operators;
471
  }
@@ -487,7 +517,7 @@ class JS extends Minify
487
 
488
  // add word boundaries
489
  array_walk($keywords, function ($value) {
490
- return '\b'.$value.'\b';
491
  });
492
 
493
  $keywords = array_combine($keywords, $escaped);
@@ -524,11 +554,11 @@ class JS extends Minify
524
  * array['key-here'] can't be replaced by array.key-here since '-'
525
  * is not a valid character there.
526
  */
527
- if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
528
  return $match[0];
529
  }
530
 
531
- return '.'.$property;
532
  };
533
 
534
  /*
@@ -549,9 +579,9 @@ class JS extends Minify
549
  * separate look-behind assertions, one for each keyword.
550
  */
551
  $keywords = $this->getKeywordsForRegex($keywords);
552
- $keywords = '(?<!'.implode(')(?<!', $keywords).')';
553
 
554
- return preg_replace_callback('/(?<='.$previousChar.'|\])'.$keywords.'\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
555
  }
556
 
557
  /**
@@ -575,7 +605,7 @@ class JS extends Minify
575
  return $match[0];
576
  }
577
 
578
- return $match[1].($match[2] === 'true' ? '!0' : '!1');
579
  };
580
  $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
581
 
1
  <?php
2
+
3
  /**
4
+ * JavaScript minifier.
5
  *
6
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
7
  *
9
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
10
  * @license MIT License
11
  */
12
+
13
  namespace FVM\MatthiasMullie\Minify;
14
 
15
  /**
16
+ * JavaScript Minifier Class.
17
  *
18
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
19
  *
 
20
  * @author Matthias Mullie <minify@mullie.eu>
21
  * @author Tijs Verkoyen <minify@verkoyen.eu>
22
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
30
  * Note that regular expressions using that bit must have the PCRE_UTF8
31
  * pattern modifier (/u) set.
32
  *
33
+ * @internal
34
+ *
35
  * @var string
36
  */
37
  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';
127
  */
128
  public function __construct()
129
  {
130
+ call_user_func_array(array('\\FVM\\MatthiasMullie\Minify\\Minify', '__construct'), func_get_args());
131
 
132
+ $dataDir = __DIR__ . '/../data/js/';
133
  $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
134
+ $this->keywordsReserved = file($dataDir . 'keywords_reserved.txt', $options);
135
+ $this->keywordsBefore = file($dataDir . 'keywords_before.txt', $options);
136
+ $this->keywordsAfter = file($dataDir . 'keywords_after.txt', $options);
137
+ $this->operators = file($dataDir . 'operators.txt', $options);
138
+ $this->operatorsBefore = file($dataDir . 'operators_before.txt', $options);
139
+ $this->operatorsAfter = file($dataDir . 'operators_after.txt', $options);
140
  }
141
 
142
  /**
177
  $js = $this->stripWhitespace($js);
178
 
179
  // combine js: separating the scripts by a ;
180
+ $content .= $js . ';';
181
  }
182
 
183
  // clean up leftover `;`s from the combination of multiple scripts
201
  // PHP only supports $this inside anonymous functions since 5.4
202
  $minifier = $this;
203
  $callback = function ($match) use ($minifier) {
204
+ if (
205
+ substr($match[2], 0, 1) === '!' ||
206
+ strpos($match[2], '@license') !== false ||
207
+ strpos($match[2], '@preserve') !== false
208
+ ) {
209
+ // preserve multi-line comments that start with /*!
210
+ // or contain @license or @preserve annotations
211
+ $count = count($minifier->extracted);
212
+ $placeholder = '/*' . $count . '*/';
213
+ $minifier->extracted[$placeholder] = $match[0];
214
+
215
+ return $match[1] . $placeholder . $match[3];
216
+ }
217
 
218
+ return $match[1] . $match[3];
219
  };
220
+
221
  // multi-line comments
222
+ $this->registerPattern('/(\n?)\/\*(.*?)\*\/(\n?)/s', $callback);
 
223
 
224
  // single-line comments
225
  $this->registerPattern('/\/\/.*$/m', '');
248
  $minifier = $this;
249
  $callback = function ($match) use ($minifier) {
250
  $count = count($minifier->extracted);
251
+ $placeholder = '"' . $count . '"';
252
  $minifier->extracted[$placeholder] = $match[0];
253
 
254
  return $placeholder;
267
  // of the RegExp methods (a `\` followed by a variable or value is
268
  // likely part of a division, not a regex)
269
  $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
270
+ $before = '(^|[=:,;\+\-\*\?\/\}\(\{\[&\|!]|' . implode('|', $keywords) . ')\s*';
271
  $propertiesAndMethods = array(
272
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
273
  'constructor',
287
  );
288
  $delimiters = array_fill(0, count($propertiesAndMethods), '/');
289
  $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
290
+ $after = '(?=\s*([\.,;:\)\}&\|+]|\/\/|$|\.(' . implode('|', $propertiesAndMethods) . ')))';
291
+ $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
292
 
293
  // regular expressions following a `)` are rather annoying to detect...
294
  // quite often, `/` after `)` is a division operator & if it happens to
302
  // if a regex following `)` is not followed by `.<property or method>`,
303
  // it's quite likely not a regex
304
  $before = '\)\s*';
305
+ $after = '(?=\s*\.(' . implode('|', $propertiesAndMethods) . '))';
306
+ $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
307
 
308
  // 1 more edge case: a regex can be followed by a lot more operators or
309
  // keywords if there's a newline (ASI) in between, where the operator
311
  // (https://github.com/matthiasmullie/minify/issues/56)
312
  $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
313
  $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
314
+ $after = '(?=\s*\n\s*(' . implode('|', $operators) . '))';
315
+ $this->registerPattern('/' . $pattern . $after . '/', $callback);
316
  }
317
 
318
  /**
356
  unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
357
  $content = preg_replace(
358
  array(
359
+ '/(' . implode('|', $operatorsBefore) . ')\s+/',
360
+ '/\s+(' . implode('|', $operatorsAfter) . ')/',
361
  ),
362
  '\\1',
363
  $content
374
  );
375
 
376
  // collapse whitespace around reserved words into single space
377
+ $content = preg_replace('/(^|[;\}\s])\K(' . implode('|', $keywordsBefore) . ')\s+/', '\\2 ', $content);
378
+ $content = preg_replace('/\s+(' . implode('|', $keywordsAfter) . ')(?=([;\{\s]|$))/', ' \\1', $content);
379
 
380
  /*
381
  * We didn't strip whitespace after a couple of operators because they
385
  */
386
  $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
387
  $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
388
+ $content = preg_replace('/(' . implode('|', $operatorsDiffBefore) . ')[^\S\n]+/', '\\1', $content);
389
+ $content = preg_replace('/[^\S\n]+(' . implode('|', $operatorsDiffAfter) . ')/', '\\1', $content);
390
 
391
  /*
392
  * Whitespace after `return` can be omitted in a few occasions
418
  * to be the for-loop's body... Same goes for while loops.
419
  * I'm going to double that semicolon (if any) so after the next line,
420
  * which strips semicolons here & there, we're still left with this one.
421
+ * Note the special recursive construct in the three inner parts of the for:
422
+ * (\{([^\{\}]*(?-2))*[^\{\}]*\})? - it is intended to match inline
423
+ * functions bodies, e.g.: i<arr.map(function(e){return e}).length.
424
+ * Also note that the construct is applied only once and multiplied
425
+ * for each part of the for, otherwise it risks a catastrophic backtracking.
426
+ * The limitation is that it will not allow closures in more than one
427
+ * of the three parts for a specific for() case.
428
+ * REGEX throwing catastrophic backtracking: $content = preg_replace('/(for\([^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*\));(\}|$)/s', '\\1;;\\8', $content);
429
  */
430
+ $content = preg_replace('/(for\((?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
431
+ $content = preg_replace('/(for\([^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
432
+ $content = preg_replace('/(for\([^;\{]*;[^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*)\));(\}|$)/s', '\\1;;\\4', $content);
433
+
434
  $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
435
+
436
+ /*
437
+ * Do the same for the if's that don't have a body but are followed by ;}
438
+ */
439
+ $content = preg_replace('/(\bif\s*\([^{;]*\));\}/s', '\\1;;}', $content);
440
+
441
  /*
442
  * Below will also keep `;` after a `do{}while();` along with `while();`
443
  * While these could be stripped after do-while, detecting this
495
 
496
  // don't confuse = with other assignment shortcuts (e.g. +=)
497
  $chars = preg_quote('+-*\=<>%&|', $delimiter);
498
+ $operators['='] = '(?<![' . $chars . '])\=';
499
 
500
  return $operators;
501
  }
517
 
518
  // add word boundaries
519
  array_walk($keywords, function ($value) {
520
+ return '\b' . $value . '\b';
521
  });
522
 
523
  $keywords = array_combine($keywords, $escaped);
554
  * array['key-here'] can't be replaced by array.key-here since '-'
555
  * is not a valid character there.
556
  */
557
+ if (!preg_match('/^' . $minifier::REGEX_VARIABLE . '$/u', $property)) {
558
  return $match[0];
559
  }
560
 
561
+ return '.' . $property;
562
  };
563
 
564
  /*
579
  * separate look-behind assertions, one for each keyword.
580
  */
581
  $keywords = $this->getKeywordsForRegex($keywords);
582
+ $keywords = '(?<!' . implode(')(?<!', $keywords) . ')';
583
 
584
+ return preg_replace_callback('/(?<=' . $previousChar . '|\])' . $keywords . '\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
585
  }
586
 
587
  /**
605
  return $match[0];
606
  }
607
 
608
+ return $match[1] . ($match[2] === 'true' ? '!0' : '!1');
609
  };
610
  $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
611
 
libs/matthiasmullie/minify/src/Minify.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
 
2
  /**
3
- * Abstract minifier class
4
  *
5
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
  *
@@ -8,6 +9,7 @@
8
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
  * @license MIT License
10
  */
 
11
  namespace FVM\MatthiasMullie\Minify;
12
 
13
  use FVM\MatthiasMullie\Minify\Exceptions\IOException;
@@ -18,7 +20,6 @@ use Psr\Cache\CacheItemInterface;
18
  *
19
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
20
  *
21
- * @package Minify
22
  * @author Matthias Mullie <minify@mullie.eu>
23
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
24
  * @license MIT License
@@ -44,6 +45,8 @@ abstract class Minify
44
  * been extracted from the JS source code, so we can reliably match "code",
45
  * without having to worry about potential "code-like" characters inside.
46
  *
 
 
47
  * @var string[]
48
  */
49
  public $extracted = array();
@@ -105,7 +108,7 @@ abstract class Minify
105
  * @param string|string[] $data
106
  *
107
  * @return static
108
- *
109
  * @throws IOException
110
  */
111
  public function addFile($data /* $data = null, ... */)
@@ -128,7 +131,7 @@ abstract class Minify
128
 
129
  // check if we can read the file
130
  if (!$this->canImportFile($path)) {
131
- throw new IOException('The file "'.$path.'" could not be opened for reading. Check if PHP has enough permissions.');
132
  }
133
 
134
  $this->add($path);
@@ -243,6 +246,9 @@ abstract class Minify
243
  /**
244
  * Register a pattern to execute against the source content.
245
  *
 
 
 
246
  * @param string $pattern PCRE pattern
247
  * @param string|callable $replacement Replacement value for matched pattern
248
  */
@@ -268,11 +274,13 @@ abstract class Minify
268
  */
269
  protected function replace($content)
270
  {
271
- $processed = '';
 
 
272
  $positions = array_fill(0, count($this->patterns), -1);
273
  $matches = array();
274
 
275
- while ($content) {
276
  // find first match for all patterns
277
  foreach ($this->patterns as $i => $pattern) {
278
  list($pattern, $replacement) = $pattern;
@@ -285,12 +293,12 @@ abstract class Minify
285
 
286
  // no need to re-run matches that are still in the part of the
287
  // content that hasn't been processed
288
- if ($positions[$i] >= 0) {
289
  continue;
290
  }
291
 
292
  $match = null;
293
- if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
294
  $matches[$i] = $match;
295
 
296
  // we'll store the match position as well; that way, we
@@ -307,61 +315,53 @@ abstract class Minify
307
 
308
  // no more matches to find: everything's been processed, break out
309
  if (!$matches) {
310
- $processed .= $content;
 
311
  break;
312
  }
313
 
314
  // see which of the patterns actually found the first thing (we'll
315
  // only want to execute that one, since we're unsure if what the
316
  // other found was not inside what the first found)
317
- $discardLength = min($positions);
318
- $firstPattern = array_search($discardLength, $positions);
319
- $match = $matches[$firstPattern][0][0];
320
 
321
  // execute the pattern that matches earliest in the content string
322
- list($pattern, $replacement) = $this->patterns[$firstPattern];
323
- $replacement = $this->replacePattern($pattern, $replacement, $content);
324
-
325
- // figure out which part of the string was unmatched; that's the
326
- // part we'll execute the patterns on again next
327
- $content = (string) substr($content, $discardLength);
328
- $unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
329
-
330
- // move the replaced part to $processed and prepare $content to
331
- // again match batch of patterns against
332
- $processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
333
- $content = $unmatched;
334
-
335
- // first match has been replaced & that content is to be left alone,
336
- // the next matches will start after this replacement, so we should
337
- // fix their offsets
338
- foreach ($positions as $i => $position) {
339
- $positions[$i] -= $discardLength + strlen($match);
340
- }
341
  }
342
 
343
- return $processed;
344
  }
345
 
346
  /**
347
- * This is where a pattern is matched against $content and the matches
348
- * are replaced by their respective value.
349
- * This function will be called plenty of times, where $content will always
350
- * move up 1 character.
351
  *
352
- * @param string $pattern Pattern to match
353
  * @param string|callable $replacement Replacement value
354
- * @param string $content Content to match pattern against
355
  *
356
  * @return string
357
  */
358
- protected function replacePattern($pattern, $replacement, $content)
359
  {
360
- if (is_callable($replacement)) {
361
- return preg_replace_callback($pattern, $replacement, $content, 1, $count);
362
- } else {
363
- return preg_replace($pattern, $replacement, $content, 1, $count);
 
 
364
  }
 
 
365
  }
366
 
367
  /**
@@ -395,8 +395,8 @@ abstract class Minify
395
  }
396
 
397
  $count = count($minifier->extracted);
398
- $placeholder = $match[1].$placeholderPrefix.$count.$match[1];
399
- $minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
400
 
401
  return $placeholder;
402
  };
@@ -413,7 +413,7 @@ abstract class Minify
413
  * considered as escape-char (times 2) and to get it in the regex,
414
  * escaped (times 2)
415
  */
416
- $this->registerPattern('/(['.$chars.'])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
417
  }
418
 
419
  /**
@@ -472,8 +472,8 @@ abstract class Minify
472
  */
473
  protected function openFileForWriting($path)
474
  {
475
- if (($handler = @fopen($path, 'w')) === false) {
476
- throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
477
  }
478
 
479
  return $handler;
@@ -490,8 +490,22 @@ abstract class Minify
490
  */
491
  protected function writeToFile($handler, $content, $path = '')
492
  {
493
- if (($result = @fwrite($handler, $content)) === false || ($result < strlen($content))) {
494
- throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
 
 
 
 
 
 
 
 
 
 
 
 
495
  }
 
 
496
  }
497
  }
1
  <?php
2
+
3
  /**
4
+ * Abstract minifier class.
5
  *
6
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
7
  *
9
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
10
  * @license MIT License
11
  */
12
+
13
  namespace FVM\MatthiasMullie\Minify;
14
 
15
  use FVM\MatthiasMullie\Minify\Exceptions\IOException;
20
  *
21
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
22
  *
 
23
  * @author Matthias Mullie <minify@mullie.eu>
24
  * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
25
  * @license MIT License
45
  * been extracted from the JS source code, so we can reliably match "code",
46
  * without having to worry about potential "code-like" characters inside.
47
  *
48
+ * @internal
49
+ *
50
  * @var string[]
51
  */
52
  public $extracted = array();
108
  * @param string|string[] $data
109
  *
110
  * @return static
111
+ *
112
  * @throws IOException
113
  */
114
  public function addFile($data /* $data = null, ... */)
131
 
132
  // check if we can read the file
133
  if (!$this->canImportFile($path)) {
134
+ throw new IOException('The file "' . $path . '" could not be opened for reading. Check if PHP has enough permissions.');
135
  }
136
 
137
  $this->add($path);
246
  /**
247
  * Register a pattern to execute against the source content.
248
  *
249
+ * If $replacement is a string, it must be plain text. Placeholders like $1 or \2 don't work.
250
+ * If you need that functionality, use a callback instead.
251
+ *
252
  * @param string $pattern PCRE pattern
253
  * @param string|callable $replacement Replacement value for matched pattern
254
  */
274
  */
275
  protected function replace($content)
276
  {
277
+ $contentLength = strlen($content);
278
+ $output = '';
279
+ $processedOffset = 0;
280
  $positions = array_fill(0, count($this->patterns), -1);
281
  $matches = array();
282
 
283
+ while ($processedOffset < $contentLength) {
284
  // find first match for all patterns
285
  foreach ($this->patterns as $i => $pattern) {
286
  list($pattern, $replacement) = $pattern;
293
 
294
  // no need to re-run matches that are still in the part of the
295
  // content that hasn't been processed
296
+ if ($positions[$i] >= $processedOffset) {
297
  continue;
298
  }
299
 
300
  $match = null;
301
+ if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE, $processedOffset)) {
302
  $matches[$i] = $match;
303
 
304
  // we'll store the match position as well; that way, we
315
 
316
  // no more matches to find: everything's been processed, break out
317
  if (!$matches) {
318
+ // output the remaining content
319
+ $output .= substr($content, $processedOffset);
320
  break;
321
  }
322
 
323
  // see which of the patterns actually found the first thing (we'll
324
  // only want to execute that one, since we're unsure if what the
325
  // other found was not inside what the first found)
326
+ $matchOffset = min($positions);
327
+ $firstPattern = array_search($matchOffset, $positions);
328
+ $match = $matches[$firstPattern];
329
 
330
  // execute the pattern that matches earliest in the content string
331
+ list(, $replacement) = $this->patterns[$firstPattern];
332
+
333
+ // add the part of the input between $processedOffset and the first match;
334
+ // that content wasn't matched by anything
335
+ $output .= substr($content, $processedOffset, $matchOffset - $processedOffset);
336
+ // add the replacement for the match
337
+ $output .= $this->executeReplacement($replacement, $match);
338
+ // advance $processedOffset past the match
339
+ $processedOffset = $matchOffset + strlen($match[0][0]);
 
 
 
 
 
 
 
 
 
 
340
  }
341
 
342
+ return $output;
343
  }
344
 
345
  /**
346
+ * If $replacement is a callback, execute it, passing in the match data.
347
+ * If it's a string, just pass it through.
 
 
348
  *
 
349
  * @param string|callable $replacement Replacement value
350
+ * @param array $match Match data, in PREG_OFFSET_CAPTURE form
351
  *
352
  * @return string
353
  */
354
+ protected function executeReplacement($replacement, $match)
355
  {
356
+ if (!is_callable($replacement)) {
357
+ return $replacement;
358
+ }
359
+ // convert $match from the PREG_OFFSET_CAPTURE form to the form the callback expects
360
+ foreach ($match as &$matchItem) {
361
+ $matchItem = $matchItem[0];
362
  }
363
+
364
+ return $replacement($match);
365
  }
366
 
367
  /**
395
  }
396
 
397
  $count = count($minifier->extracted);
398
+ $placeholder = $match[1] . $placeholderPrefix . $count . $match[1];
399
+ $minifier->extracted[$placeholder] = $match[1] . $match[2] . $match[1];
400
 
401
  return $placeholder;
402
  };
413
  * considered as escape-char (times 2) and to get it in the regex,
414
  * escaped (times 2)
415
  */
416
+ $this->registerPattern('/([' . $chars . '])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
417
  }
418
 
419
  /**
472
  */
473
  protected function openFileForWriting($path)
474
  {
475
+ if ($path === '' || ($handler = @fopen($path, 'w')) === false) {
476
+ throw new IOException('The file "' . $path . '" could not be opened for writing. Check if PHP has enough permissions.');
477
  }
478
 
479
  return $handler;
490
  */
491
  protected function writeToFile($handler, $content, $path = '')
492
  {
493
+ if (
494
+ !is_resource($handler) ||
495
+ ($result = @fwrite($handler, $content)) === false ||
496
+ ($result < strlen($content))
497
+ ) {
498
+ throw new IOException('The file "' . $path . '" could not be written to. Check your disk space and file permissions.');
499
+ }
500
+ }
501
+
502
+ protected static function str_replace_first($search, $replace, $subject)
503
+ {
504
+ $pos = strpos($subject, $search);
505
+ if ($pos !== false) {
506
+ return substr_replace($subject, $replace, $pos, strlen($search));
507
  }
508
+
509
+ return $subject;
510
  }
511
  }
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: Alignak
3
  Tags: PHP Minify, Lighthouse, GTmetrix, Pingdom, Pagespeed, Merging, Minification, Optimization, Speed, Performance, FVM
4
  Requires at least: 5.0
5
  Requires PHP: 7.0
6
- Stable tag: 3.3.1
7
- Tested up to: 6.0.2
8
  Text Domain: fast-velocity-minify
9
  License: GPLv3 or later
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
@@ -32,7 +32,7 @@ I can offer you additional `custom made` optimization on top of this plugin. If
32
  * Purge all caches on the entire network (linux): `wp site list --field=url | xargs -n1 -I % wp --url=% fvm purge`
33
 
34
  = How to customize the cache path ? =
35
- You need a public directory to store and serve minified cache files. If you need to customize the path and url, you need to edit your `wp-config.php` and add both `define('FVM_DIR', '/path/to/example.com/your/public/directory');` and `define('FVM_URL', 'https://example.com/your/public/directory');` .
36
 
37
 
38
  == Installation ==
@@ -49,6 +49,16 @@ You need a public directory to store and serve minified cache files. If you need
49
 
50
  == Changelog ==
51
 
 
 
 
 
 
 
 
 
 
 
52
  = 3.3.1 [2022.10.06] =
53
  * bug fix
54
 
3
  Tags: PHP Minify, Lighthouse, GTmetrix, Pingdom, Pagespeed, Merging, Minification, Optimization, Speed, Performance, FVM
4
  Requires at least: 5.0
5
  Requires PHP: 7.0
6
+ Stable tag: 3.3.3
7
+ Tested up to: 6.1.3
8
  Text Domain: fast-velocity-minify
9
  License: GPLv3 or later
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
32
  * Purge all caches on the entire network (linux): `wp site list --field=url | xargs -n1 -I % wp --url=% fvm purge`
33
 
34
  = How to customize the cache path ? =
35
+ You need a public directory to store and serve minified cache files. If you need to customize the path and url, you need to edit your `wp-config.php` and add both `define('FVM_CACHE_DIR', '/absolute/path/to/example.com/your/public/directory');` and `define('FVM_CACHE_URL', 'https://example.com/your/public/directory');` .
36
 
37
 
38
  == Installation ==
49
 
50
  == Changelog ==
51
 
52
+ = 3.3.3 [2022.12.11] =
53
+ * custom cache directory fixes
54
+ * upgraded matthiasmullie's PHP Minify to 1.3.70
55
+ * move the viewport meta tag up, before preloading tags
56
+
57
+ = 3.3.2 [2022.10.07] =
58
+ * improved compatibility with WP Links and All in One SEO
59
+ * additional constant for custom initialization
60
+ * better output buffer processor
61
+
62
  = 3.3.1 [2022.10.06] =
63
  * bug fix
64