Fast Velocity Minify - Version 2.2.7

Version Description

[2018.02.19] = * fixed a bug with the blacklist functionality * replaced PHP Minify with JSMin as the default JS minification * replaced PHP Minify with CSSTidy as the default CSS minification * replaced PHP Minify with Minify HTML as the default HTML minification * moved the intermediary cache from transients to disk files

Download this release

Release Info

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

Code changes from version 2.2.6 to 2.2.7

Files changed (39) hide show
  1. fvm.php +13 -10
  2. inc/functions-serverinfo.php +6 -5
  3. inc/functions.php +153 -74
  4. libs/CSSTidy/class.csstidy.php +1296 -0
  5. libs/CSSTidy/class.csstidy_optimise.php +963 -0
  6. libs/CSSTidy/class.csstidy_print.php +446 -0
  7. libs/CSSTidy/data.inc.php +646 -0
  8. libs/CSSTidy/lang.inc.php +309 -0
  9. libs/JSMin/jsmin.php +386 -0
  10. libs/matthiasmullie/index.html +0 -0
  11. libs/matthiasmullie/minify/bin/index.html +0 -0
  12. libs/matthiasmullie/minify/bin/minifycss +0 -45
  13. libs/matthiasmullie/minify/bin/minifyjs +0 -45
  14. libs/matthiasmullie/minify/data/index.html +0 -0
  15. libs/matthiasmullie/minify/data/js/index.html +0 -0
  16. libs/matthiasmullie/minify/data/js/keywords_after.txt +0 -7
  17. libs/matthiasmullie/minify/data/js/keywords_before.txt +0 -26
  18. libs/matthiasmullie/minify/data/js/keywords_reserved.txt +0 -63
  19. libs/matthiasmullie/minify/data/js/operators.txt +0 -46
  20. libs/matthiasmullie/minify/data/js/operators_after.txt +0 -43
  21. libs/matthiasmullie/minify/data/js/operators_before.txt +0 -43
  22. libs/matthiasmullie/minify/index.html +0 -0
  23. libs/matthiasmullie/minify/src/CSS.php +0 -735
  24. libs/matthiasmullie/minify/src/Exception.php +0 -20
  25. libs/matthiasmullie/minify/src/Exceptions/BasicException.php +0 -23
  26. libs/matthiasmullie/minify/src/Exceptions/FileImportException.php +0 -21
  27. libs/matthiasmullie/minify/src/Exceptions/IOException.php +0 -21
  28. libs/matthiasmullie/minify/src/Exceptions/index.html +0 -0
  29. libs/matthiasmullie/minify/src/JS.php +0 -577
  30. libs/matthiasmullie/minify/src/Minify.php +0 -444
  31. libs/matthiasmullie/minify/src/index.html +0 -0
  32. libs/matthiasmullie/path-converter/index.html +0 -0
  33. libs/matthiasmullie/path-converter/src/Converter.php +0 -195
  34. libs/matthiasmullie/path-converter/src/ConverterInterface.php +0 -24
  35. libs/matthiasmullie/path-converter/src/NoConverter.php +0 -23
  36. libs/matthiasmullie/path-converter/src/index.html +0 -0
  37. libs/mrclay/HTML.php +0 -241
  38. libs/mrclay/index.html +0 -0
  39. readme.txt +58 -91
fvm.php CHANGED
@@ -5,7 +5,7 @@ Plugin URI: http://fastvelocity.com
5
  Description: Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by merging and minifying CSS and JavaScript files into groups, compressing HTML and other speed optimizations.
6
  Author: Raul Peixoto
7
  Author URI: http://fastvelocity.com
8
- Version: 2.2.6
9
  License: GPL2
10
 
11
  ------------------------------------------------------------------------
@@ -127,7 +127,7 @@ $fvm_cdn_url = get_option('fastvelocity_min_fvm_cdn_url');
127
  $used_css_files = array();
128
 
129
  # default blacklist
130
- $exc = array('/html5shiv.js', '/excanvas.js', '/avada-ie9.js', '/respond.js', '/respond.min.js', '/selectivizr.js', '/Avada/assets/css/ie.css', '/html5.js', '/IE9.js', '/fusion-ie9.js', '/vc_lte_ie9.min.css', '/old-ie.css', '/ie.css', '/vc-ie8.min.css', '/mailchimp-for-wp/assets/js/third-party/placeholders.min.js', '/assets/js/plugins/wp-enqueue/min/webfontloader.js', '/a.optnmstr.com/app/js/api.min.js');
131
  if(!is_array($blacklist) || strlen(implode($blacklist)) == 0) { update_option('fastvelocity_min_blacklist', implode("\n", $exc)); }
132
 
133
  # default ignore list
@@ -862,7 +862,7 @@ $is_footer = 0; if (isset($wp_scripts->registered[$handle]->extra["group"]) || i
862
  # IE only files don't increment things
863
  $ieonly = fastvelocity_ie_blacklist($hurl);
864
  if($ieonly == true) { continue; }
865
-
866
  # skip ignore list, scripts with conditionals, external scripts
867
  if ((!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && fvm_internal_url($hurl, $wp_home)) || empty($hurl)) {
868
 
@@ -887,6 +887,9 @@ $is_footer = 0; if (isset($wp_scripts->registered[$handle]->extra["group"]) || i
887
  endforeach;
888
 
889
 
 
 
 
890
  # loop through header scripts and merge
891
  for($i=0,$l=count($header);$i<$l;$i++) {
892
  if(!isset($header[$i]['handle'])) {
@@ -915,9 +918,9 @@ for($i=0,$l=count($header);$i<$l;$i++) {
915
  $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
916
  $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
917
 
918
- # get css from hurl, if available and valid
919
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
920
- $newcode = false; $newcode = get_transient($tkey);
921
  if ( $newcode === false) {
922
  $res = fvm_download_and_cache($hurl, $tkey, null, $disable_js_minification, 'js', $handle);
923
  if(is_array($res)) {
@@ -1050,7 +1053,7 @@ for($i=0,$l=count($footer);$i<$l;$i++) {
1050
 
1051
  # get css from hurl, if available and valid
1052
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
1053
- $newcode = false; $newcode = get_transient($tkey);
1054
  if ( $newcode === false) {
1055
  $res = fvm_download_and_cache($hurl, $tkey, null, $disable_js_minification, 'js', $handle);
1056
  if(is_array($res)) {
@@ -1255,7 +1258,7 @@ if(!$skip_google_fonts && count($google_fonts) > 0) {
1255
 
1256
  # google fonts download and inlining, ignore logs
1257
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $concat_google_fonts);
1258
- $newcode = false; $newcode = get_transient($tkey);
1259
  if ( $newcode === false) {
1260
  $res = fvm_download_and_cache($concat_google_fonts, $tkey, null, $disable_css_minification, 'css');
1261
  if(is_array($res)) { $newcode = $res['code']; }
@@ -1383,7 +1386,7 @@ for($i=0,$l=count($header);$i<$l;$i++) {
1383
 
1384
  # get css from hurl, if available and valid
1385
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
1386
- $newcode = false; $newcode = get_transient($tkey);
1387
  if ( $newcode === false) {
1388
  $res = fvm_download_and_cache($hurl, $tkey, null, $disable_css_minification, 'css', $handle);
1389
  if(is_array($res)) {
@@ -1519,7 +1522,7 @@ if(!$skip_google_fonts && count($google_fonts) > 0) {
1519
 
1520
  # google fonts download and inlining, ignore logs
1521
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $concat_google_fonts);
1522
- $newcode = false; $newcode = get_transient($tkey);
1523
  if ( $newcode === false) {
1524
  $res = fvm_download_and_cache($concat_google_fonts, $tkey, null, $disable_css_minification, 'css');
1525
  if(is_array($res)) { $newcode = $res['code']; }
@@ -1644,7 +1647,7 @@ for($i=0,$l=count($footer);$i<$l;$i++) {
1644
 
1645
  # get css from hurl, if available and valid
1646
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
1647
- $newcode = false; $newcode = get_transient($tkey);
1648
  if ( $newcode === false) {
1649
  $res = fvm_download_and_cache($hurl, $tkey, null, $disable_css_minification, 'css', $handle);
1650
  if(is_array($res)) {
5
  Description: Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by merging and minifying CSS and JavaScript files into groups, compressing HTML and other speed optimizations.
6
  Author: Raul Peixoto
7
  Author URI: http://fastvelocity.com
8
+ Version: 2.2.7
9
  License: GPL2
10
 
11
  ------------------------------------------------------------------------
127
  $used_css_files = array();
128
 
129
  # default blacklist
130
+ $exc = array('/html5shiv.js', '/html5shiv-printshiv.min.js', '/excanvas.js', '/avada-ie9.js', '/respond.js', '/respond.min.js', '/selectivizr.js', '/Avada/assets/css/ie.css', '/html5.js', '/IE9.js', '/fusion-ie9.js', '/vc_lte_ie9.min.css', '/old-ie.css', '/ie.css', '/vc-ie8.min.css', '/mailchimp-for-wp/assets/js/third-party/placeholders.min.js', '/assets/js/plugins/wp-enqueue/min/webfontloader.js', '/a.optnmstr.com/app/js/api.min.js');
131
  if(!is_array($blacklist) || strlen(implode($blacklist)) == 0) { update_option('fastvelocity_min_blacklist', implode("\n", $exc)); }
132
 
133
  # default ignore list
862
  # IE only files don't increment things
863
  $ieonly = fastvelocity_ie_blacklist($hurl);
864
  if($ieonly == true) { continue; }
865
+
866
  # skip ignore list, scripts with conditionals, external scripts
867
  if ((!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && fvm_internal_url($hurl, $wp_home)) || empty($hurl)) {
868
 
887
  endforeach;
888
 
889
 
890
+
891
+
892
+
893
  # loop through header scripts and merge
894
  for($i=0,$l=count($header);$i<$l;$i++) {
895
  if(!isset($header[$i]['handle'])) {
918
  $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
919
  $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
920
 
921
+ # get code from hurl, if available and valid
922
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
923
+ $newcode = false; $newcode = fvm_get_transient($tkey);
924
  if ( $newcode === false) {
925
  $res = fvm_download_and_cache($hurl, $tkey, null, $disable_js_minification, 'js', $handle);
926
  if(is_array($res)) {
1053
 
1054
  # get css from hurl, if available and valid
1055
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
1056
+ $newcode = false; $newcode = fvm_get_transient($tkey);
1057
  if ( $newcode === false) {
1058
  $res = fvm_download_and_cache($hurl, $tkey, null, $disable_js_minification, 'js', $handle);
1059
  if(is_array($res)) {
1258
 
1259
  # google fonts download and inlining, ignore logs
1260
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $concat_google_fonts);
1261
+ $newcode = false; $newcode = fvm_get_transient($tkey);
1262
  if ( $newcode === false) {
1263
  $res = fvm_download_and_cache($concat_google_fonts, $tkey, null, $disable_css_minification, 'css');
1264
  if(is_array($res)) { $newcode = $res['code']; }
1386
 
1387
  # get css from hurl, if available and valid
1388
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
1389
+ $newcode = false; $newcode = fvm_get_transient($tkey);
1390
  if ( $newcode === false) {
1391
  $res = fvm_download_and_cache($hurl, $tkey, null, $disable_css_minification, 'css', $handle);
1392
  if(is_array($res)) {
1522
 
1523
  # google fonts download and inlining, ignore logs
1524
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $concat_google_fonts);
1525
+ $newcode = false; $newcode = fvm_get_transient($tkey);
1526
  if ( $newcode === false) {
1527
  $res = fvm_download_and_cache($concat_google_fonts, $tkey, null, $disable_css_minification, 'css');
1528
  if(is_array($res)) { $newcode = $res['code']; }
1647
 
1648
  # get css from hurl, if available and valid
1649
  $tkey = 'fvm-cache-'.$ctime.hash('adler32', $hurl);
1650
+ $newcode = false; $newcode = fvm_get_transient($tkey);
1651
  if ( $newcode === false) {
1652
  $res = fvm_download_and_cache($hurl, $tkey, null, $disable_css_minification, 'css', $handle);
1653
  if(is_array($res)) {
inc/functions-serverinfo.php CHANGED
@@ -108,6 +108,7 @@ function fvm_get_generalinfo() {
108
  ### Function: Format Bytes Into TiB/GiB/MiB/KiB/Bytes
109
  if(!function_exists('fvm_format_filesize')) {
110
  function fvm_format_filesize($rawSize) {
 
111
  if($rawSize / 1099511627776 > 1) {
112
  return number_format_i18n($rawSize/1099511627776, 1).' '.__('TiB', 'fvm-serverinfo');
113
  } elseif($rawSize / 1073741824 > 1) {
@@ -229,10 +230,10 @@ if(!function_exists('fvm_get_mysql_version')) {
229
  if(!function_exists('fvm_get_mysql_data_usage')) {
230
  function fvm_get_mysql_data_usage() {
231
  global $wpdb;
232
- $data_usage = '';
233
  $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
234
- foreach($tablesstatus as $tablestatus) {
235
- $data_usage += $tablestatus->Data_length;
236
  }
237
  if (!$data_usage) {
238
  $data_usage = __('N/A', 'fvm-serverinfo');
@@ -246,10 +247,10 @@ if(!function_exists('fvm_get_mysql_data_usage')) {
246
  if(!function_exists('fvm_get_mysql_index_usage')) {
247
  function fvm_get_mysql_index_usage() {
248
  global $wpdb;
249
- $index_usage = '';
250
  $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
251
  foreach($tablesstatus as $tablestatus) {
252
- $index_usage += $tablestatus->Index_length;
253
  }
254
  if (!$index_usage){
255
  $index_usage = __('N/A', 'fvm-serverinfo');
108
  ### Function: Format Bytes Into TiB/GiB/MiB/KiB/Bytes
109
  if(!function_exists('fvm_format_filesize')) {
110
  function fvm_format_filesize($rawSize) {
111
+ if(!is_numeric($rawSize)) { return __('unknown', 'fvm-serverinfo'); }
112
  if($rawSize / 1099511627776 > 1) {
113
  return number_format_i18n($rawSize/1099511627776, 1).' '.__('TiB', 'fvm-serverinfo');
114
  } elseif($rawSize / 1073741824 > 1) {
230
  if(!function_exists('fvm_get_mysql_data_usage')) {
231
  function fvm_get_mysql_data_usage() {
232
  global $wpdb;
233
+ $data_usage = 0;
234
  $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
235
+ foreach($tablesstatus as $tablestatus) {
236
+ if(is_numeric($tablestatus->Data_length)) { $data_usage += $tablestatus->Data_length; }
237
  }
238
  if (!$data_usage) {
239
  $data_usage = __('N/A', 'fvm-serverinfo');
247
  if(!function_exists('fvm_get_mysql_index_usage')) {
248
  function fvm_get_mysql_index_usage() {
249
  global $wpdb;
250
+ $index_usage = 0;
251
  $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
252
  foreach($tablesstatus as $tablestatus) {
253
+ if(is_numeric($tablestatus->Index_length)) { $index_usage += $tablestatus->Index_length; }
254
  }
255
  if (!$index_usage){
256
  $index_usage = __('N/A', 'fvm-serverinfo');
inc/functions.php CHANGED
@@ -3,21 +3,119 @@
3
  # handle better utf-8 and unicode encoding
4
  if(function_exists('mb_internal_encoding')) { mb_internal_encoding('UTF-8'); }
5
 
6
- # Consider fallback to PHP Minify [2017.12.17] from https://github.com/matthiasmullie/minify (must be defined on the outer scope)
7
- $path = $plugindir . 'libs/matthiasmullie';
8
- require_once $path . '/minify/src/Minify.php';
9
- require_once $path . '/minify/src/CSS.php';
10
- require_once $path . '/minify/src/JS.php';
11
- require_once $path . '/minify/src/Exception.php';
12
- require_once $path . '/minify/src/Exceptions/BasicException.php';
13
- require_once $path . '/minify/src/Exceptions/FileImportException.php';
14
- require_once $path . '/minify/src/Exceptions/IOException.php';
15
- require_once $path . '/path-converter/src/ConverterInterface.php';
16
- require_once $path . '/path-converter/src/Converter.php';
17
- use MatthiasMullie\Minify;
18
-
19
- # use HTML minification
20
- require_once ($plugindir . 'libs/mrclay/HTML.php');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  # get cache directories and urls
23
  function fvm_cachepath() {
@@ -133,7 +231,7 @@ function fastvelocity_min_in_arrayi($hurl, $ignore){
133
  foreach ($ignore as $i) {
134
  $i = str_ireplace(array('http://', 'https://'), '//', $i); # better compatibility
135
  $i = strtok(urldecode(rawurldecode($i)), '?'); # no query string, decode entities
136
- $i = trim(trim($i, '/'), '*'); # wildcard removal
137
  if (stripos($hurl, $i) !== false) { return true; }
138
  }
139
  }
@@ -149,12 +247,19 @@ function fvm_compat_urls($code) {
149
  }
150
 
151
 
152
- # minify css string with PHP Minify
153
  function fastvelocity_min_minify_css_string($css) {
154
- $minifier = new Minify\CSS($css);
155
- $minifier->setMaxImportSize(15); # [css only] embed assets up to 15 Kb (default 5Kb) - processes gif, png, jpg, jpeg, svg & woff
156
- $min = $minifier->minify();
157
- if($min !== false) { return fvm_compat_urls($min); }
 
 
 
 
 
 
 
158
  return fvm_compat_urls($css);
159
  }
160
 
@@ -191,7 +296,7 @@ if(!$disable_js_minification) {
191
  }
192
 
193
  # needed when merging js files
194
- $js = $js."\r\n;";
195
 
196
  # return html
197
  return $js;
@@ -224,9 +329,9 @@ if($use_yui == true && function_exists('exec') && exec('command -v java >/dev/nu
224
  }
225
  }
226
 
227
- # PHP Minify [2016.08.01] from https://github.com/matthiasmullie/minify
228
- $minifier = new Minify\JS($js);
229
- $min = $minifier->minify();
230
  if($min !== false && (strlen(trim($js)) == strlen(trim($min)) || strlen(trim($min)) > 0)) { return fvm_compat_urls($min); }
231
 
232
  # if we are here, something went wrong and minification didn't work
@@ -236,16 +341,16 @@ return fvm_compat_urls($js);
236
 
237
  # functions, minify html
238
  function fastvelocity_min_minify_html($html) {
239
- return fastvelocity_min_Minify_HTML::minify($html);
240
  }
241
 
242
  # functions to minify HTML
243
  function fastvelocity_min_html_compression_finish($html) { return fastvelocity_min_minify_html($html); }
244
  function fastvelocity_min_html_compression_start() {
245
- if (fastvelocity_exclude_contents() == true) { return; }
246
- $use_alt_html_minification = get_option('fastvelocity_min_use_alt_html_minification', '0');
247
- if($use_alt_html_minification == '1') { ob_start('fastvelocity_min_minify_alt_html'); }
248
- else { ob_start('fastvelocity_min_html_compression_finish'); }
249
  }
250
 
251
  # alternative html minification, minimal
@@ -261,16 +366,15 @@ return $html;
261
  function fastvelocity_rrmdir($dir) {
262
  if(is_dir(rtrim($dir, '/'))) {
263
  if ($handle = opendir($dir.'/')) {
264
- while (false !== ($file = readdir($handle))) {
265
- @unlink($dir.'/'.$file);
266
- }
 
267
  closedir($handle); }
268
  }
269
  }
270
 
271
 
272
-
273
-
274
  # return size in human format
275
  function fastvelocity_format_filesize($bytes, $decimals = 2) {
276
  $units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' );
@@ -360,8 +464,7 @@ if (stripos($hurl, $wp_domain) !== false) {
360
  if (file_exists($f)) {
361
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
362
  else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
363
- set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
364
- fvm_update_transient_keys($tkey); # keep track
365
  $log = "$printurl --- Debug: $printhandle File was opened from $f ---\n";
366
  return array('log'=>$log, 'code'=>$code);
367
  }
@@ -373,8 +476,7 @@ if (stripos($hurl, $wp_domain) !== false) {
373
  if (file_exists($f)) {
374
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
375
  else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
376
- set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
377
- fvm_update_transient_keys($tkey); # keep track
378
  $log = "$printurl --- Debug: $printhandle File was opened from $f ---\n";
379
  return array('log'=>$log, 'code'=>$code);
380
  }
@@ -388,8 +490,7 @@ $code = fastvelocity_download($hurl, $tkey, $ttl);
388
  if($code !== false) {
389
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, $code, $disable_minification); }
390
  else { $code = fastvelocity_min_get_css($hurl, $code.$inline, $disable_minification); }
391
- set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
392
- fvm_update_transient_keys($tkey); # keep track
393
  $log = "$printurl --- Debug: $printhandle Fetched url at $hurl \n";
394
  return array('log'=>$log, 'code'=>$code);
395
  }
@@ -402,8 +503,7 @@ if(stripos($hurl, $wp_domain) !== false && home_url() != site_url()) {
402
  if($code !== false) {
403
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, $code, $disable_minification); }
404
  else { $code = fastvelocity_min_get_css($hurl, $code.$inline, $disable_minification); }
405
- set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
406
- fvm_update_transient_keys($tkey); # keep track
407
  $log = "$printurl --- Debug: $printhandle Fetched url at $hurl \n";
408
  return array('log'=>$log, 'code'=>$code);
409
  }
@@ -418,8 +518,7 @@ if (stripos($hurl, $wp_domain) !== false) {
418
  if (file_exists($f)) {
419
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
420
  else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
421
- set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
422
- fvm_update_transient_keys($tkey); # keep track
423
  $log = "$printurl --- Debug: $printhandle File was opened from $f ---\n";
424
  return array('log'=>$log, 'code'=>$code);
425
  }
@@ -431,8 +530,7 @@ if (stripos($hurl, $wp_domain) !== false) {
431
  if (file_exists($f)) {
432
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
433
  else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
434
- set_transient($tkey, $code, 7 * DAY_IN_SECONDS );
435
- fvm_update_transient_keys($tkey); # keep track
436
  $log = "$printurl --- Debug: $printhandle File was opened from $f ---\n";
437
  return array('log'=>$log, 'code'=>$code);
438
  }
@@ -643,27 +741,11 @@ function fvm_get_protocol($url) {
643
  return $url;
644
  }
645
 
646
- # keep track of transients, whenever we save a transient.
647
- function fvm_update_transient_keys($new_transient_key) {
648
- $transient_keys = get_option('fvm_transient_keys'); # get
649
- $transient_keys[]= $new_transient_key; # append
650
- update_option( 'fvm_transient_keys', $transient_keys); # save
651
- }
652
-
653
- # keep track of transients, dump our plugin transients as needed
654
- function fvm_transients_purge() {
655
- update_option('fvm-last-cache-update', time()); # last cache update to now
656
- $transient_keys = get_option( 'fvm_transient_keys' ); # get
657
- update_option( 'fvm_transient_keys', array()); # reset
658
- foreach( $transient_keys as $t ) { delete_transient( $t ); } # delete
659
- }
660
 
661
 
662
  # purge all caches
663
  function fvm_purge_all() {
664
 
665
-
666
-
667
  # get cache directories and urls
668
  $cachepath = fvm_cachepath();
669
  $tmpdir = $cachepath['tmpdir'];
@@ -671,9 +753,8 @@ $cachedir = $cachepath['cachedir'];
671
  $cachedirurl = $cachepath['cachedirurl'];
672
 
673
  # delete minification files and transients
674
- if(!is_dir($cachedir)) { return false; }
675
- fastvelocity_rrmdir($cachedir);
676
- fvm_transients_purge();
677
 
678
  return true;
679
  }
@@ -750,22 +831,21 @@ function fastvelocity_ie_blacklist($url) {
750
  function fastvelocity_download($url, $tkey, $ttl) {
751
 
752
  # rate limit requests, prevent slowdowns
753
- $rtlim = false; $rtlim = get_transient($tkey.'_access');
754
  if ( $rtlim !== false) { return false; }
755
 
756
  # info (needed for google fonts woff files + hinted fonts)
757
  $uagent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586';
758
- $data = false; $data = get_transient($tkey);
759
  if ( $data === false) {
760
 
761
- # get updated list from our api and cache it for 24 hours
762
  $response = wp_remote_get($url, array('user-agent'=>$uagent, 'timeout' => 7, 'httpversion' => '1.1', 'sslverify'=>false));
763
  $res_code = wp_remote_retrieve_response_code($response);
764
  if($res_code == '200') {
765
  $data = wp_remote_retrieve_body($response);
766
  if(strlen($data) > 1 && $ttl > 0) {
767
- set_transient($tkey, $data, $ttl);
768
- fvm_update_transient_keys($tkey); # keep track
769
  return $data;
770
  }
771
  }
@@ -774,14 +854,13 @@ function fastvelocity_download($url, $tkey, $ttl) {
774
  if(function_exists('curl_version')) {
775
  $curl = fvm_file_get_contents_curl($url, $uagent);
776
  if(!empty($curl) && strlen($curl) > 1 && $ttl > 0) {
777
- set_transient($tkey, $data, $ttl);
778
- fvm_update_transient_keys($tkey); # keep track
779
  return $data;
780
  }
781
  }
782
 
783
  # error
784
- set_transient($tkey.'_access', "Failed to fetch: $url on ".current_time('timestamp'), $ttl);
785
  return false;
786
  }
787
 
3
  # handle better utf-8 and unicode encoding
4
  if(function_exists('mb_internal_encoding')) { mb_internal_encoding('UTF-8'); }
5
 
6
+ # minification libraries
7
+ require_once ($plugindir . 'libs/JSMin/jsmin.php');
8
+ require_once ($plugindir . 'libs/CSSTidy/class.csstidy.php');
9
+
10
+
11
+ # html minification function from https://wordpress.org/plugins/minify-html-markup/
12
+ function fvm_minify_html_output($buffer) {
13
+ if ( substr( ltrim( $buffer ), 0, 5) == '<?xml' ) {
14
+ return ($buffer);
15
+ }
16
+
17
+ # defaults
18
+ $minify_html_comments = get_option('fastvelocity_min_strip_htmlcomments', 'no');
19
+ $minify_javascript = 'yes';
20
+ $minify_html_utf8 = 'yes';
21
+ $minify_html_xhtml = 'no';
22
+ $minify_html_relative = 'no';
23
+ $minify_html_scheme = 'yes';
24
+
25
+ # utf8 or not?
26
+ if ( $minify_html_utf8 == 'yes' && mb_detect_encoding($buffer, 'UTF-8', true) ) {
27
+ $mod = '/u';
28
+ } else {
29
+ $mod = '/s';
30
+ }
31
+
32
+
33
+ $buffer = str_replace(array (chr(13) . chr(10), chr(9)), array (chr(10), ''), $buffer);
34
+ $buffer = str_ireplace(array ('<script', '/script>', '<pre', '/pre>', '<textarea', '/textarea>', '<style', '/style>'), array ('M1N1FY-ST4RT<script', '/script>M1N1FY-3ND', 'M1N1FY-ST4RT<pre', '/pre>M1N1FY-3ND', 'M1N1FY-ST4RT<textarea', '/textarea>M1N1FY-3ND', 'M1N1FY-ST4RT<style', '/style>M1N1FY-3ND'), $buffer);
35
+ $split = explode('M1N1FY-3ND', $buffer);
36
+ $buffer = '';
37
+ for ($i=0; $i<count($split); $i++) {
38
+ $ii = strpos($split[$i], 'M1N1FY-ST4RT');
39
+ if ($ii !== false) {
40
+ $process = substr($split[$i], 0, $ii);
41
+ $asis = substr($split[$i], $ii + 12);
42
+ if (substr($asis, 0, 7) == '<script') {
43
+ $split2 = explode(chr(10), $asis);
44
+ $asis = '';
45
+ for ($iii = 0; $iii < count($split2); $iii ++) {
46
+ if ($split2[$iii])
47
+ $asis .= trim($split2[$iii]) . chr(10);
48
+ if ( $minify_javascript != 'no' )
49
+ if (strpos($split2[$iii], '//') !== false && substr(trim($split2[$iii]), -1) == ';' )
50
+ $asis .= chr(10);
51
+ }
52
+ if ($asis)
53
+ $asis = substr($asis, 0, -1);
54
+ if ( $minify_html_comments != 'no' )
55
+ $asis = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $asis);
56
+ if ( $minify_javascript != 'no' )
57
+ $asis = str_replace(array (';' . chr(10), '>' . chr(10), '{' . chr(10), '}' . chr(10), ',' . chr(10)), array(';', '>', '{', '}', ','), $asis);
58
+ } else if (substr($asis, 0, 6) == '<style') {
59
+ $asis = preg_replace(array ('/\>[^\S ]+' . $mod, '/[^\S ]+\<' . $mod, '/(\s)+' . $mod), array('>', '<', '\\1'), $asis);
60
+ if ( $minify_html_comments != 'no' )
61
+ $asis = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $asis);
62
+ $asis = str_replace(array (chr(10), ' {', '{ ', ' }', '} ', '( ', ' )', ' :', ': ', ' ;', '; ', ' ,', ', ', ';}'), array('', '{', '{', '}', '}', '(', ')', ':', ':', ';', ';', ',', ',', '}'), $asis);
63
+ }
64
+ } else {
65
+ $process = $split[$i];
66
+ $asis = '';
67
+ }
68
+ $process = preg_replace(array ('/\>[^\S ]+' . $mod, '/[^\S ]+\<' . $mod, '/(\s)+' . $mod), array('>', '<', '\\1'), $process);
69
+ if ( $minify_html_comments != 'no' )
70
+ $process = preg_replace('/<!--(?!\s*(?:\[if [^\]]+]|<!|>))(?:(?!-->).)*-->' . $mod, '', $process);
71
+ $buffer .= $process.$asis;
72
+ }
73
+
74
+ $buffer = str_replace(array (chr(10) . '<script', chr(10) . '<style', '*/' . chr(10), 'M1N1FY-ST4RT'), array('<script', '<style', '*/', ''), $buffer);
75
+
76
+ # extra
77
+ if ( $minify_html_xhtml == 'yes' && strtolower( substr( ltrim( $buffer ), 0, 15 ) ) == '<!doctype html>' )
78
+ $buffer = str_replace( ' />', '>', $buffer );
79
+ if ( $minify_html_relative == 'yes' )
80
+ $buffer = str_replace( array ( 'https://' . $_SERVER['HTTP_HOST'] . '/', 'http://' . $_SERVER['HTTP_HOST'] . '/', '//' . $_SERVER['HTTP_HOST'] . '/' ), array( '/', '/', '/' ), $buffer );
81
+ if ( $minify_html_scheme == 'yes' )
82
+ $buffer = str_replace( array( 'http://', 'https://' ), '//', $buffer );
83
+ return ($buffer);
84
+ }
85
+
86
+
87
+ # transients on the disk
88
+ function fvm_get_transient($key, $ttl=NULL) {
89
+
90
+ $cachepath = fvm_cachepath();
91
+ $tmpdir = $cachepath['tmpdir'];
92
+ clearstatcache();
93
+ if(file_exists($tmpdir.'/'.$key)) {
94
+
95
+ # expired?
96
+ if(!is_null($ttl) && filemtime($tmpdir.'/'.$key) < time()-$ttl) {
97
+ @unlink($tmpdir.'/'.$key);
98
+ return false;
99
+ }
100
+
101
+ # return
102
+ return file_get_contents($tmpdir.'/'.$key);
103
+ } else {
104
+ return false;
105
+ }
106
+ }
107
+
108
+ function fvm_set_transient($key, $code) {
109
+ if(is_null($code) || empty($code)) { return false; }
110
+ $cachepath = fvm_cachepath();
111
+ $tmpdir = $cachepath['tmpdir'];
112
+ clearstatcache();
113
+ if(!file_exists($tmpdir.'/'.$key) || (file_exists($tmpdir.'/'.$key) && filesize($tmpdir.'/'.$key) == 0)) {
114
+ file_put_contents($tmpdir.'/'.$key, $code);
115
+ }
116
+ }
117
+
118
+
119
 
120
  # get cache directories and urls
121
  function fvm_cachepath() {
231
  foreach ($ignore as $i) {
232
  $i = str_ireplace(array('http://', 'https://'), '//', $i); # better compatibility
233
  $i = strtok(urldecode(rawurldecode($i)), '?'); # no query string, decode entities
234
+ $i = trim(trim(trim(rtrim($i, '/')), '*')); # wildcard char removal
235
  if (stripos($hurl, $i) !== false) { return true; }
236
  }
237
  }
247
  }
248
 
249
 
250
+ # minify css string with CSSTidy http://manpages.ubuntu.com/manpages/xenial/man1/csstidy.1.html
251
  function fastvelocity_min_minify_css_string($css) {
252
+ $tidy = new csstidy();
253
+ $tidy->set_cfg('preserve_css',true);
254
+ $tidy->set_cfg('merge_selectors', 1);
255
+ $tidy->set_cfg('template', 'highest');
256
+ $tidy->parse($css);
257
+ $min = $tidy->print->plain();
258
+ if($min !== false) {
259
+ return fvm_compat_urls($min);
260
+ }
261
+
262
+ # fallback
263
  return fvm_compat_urls($css);
264
  }
265
 
296
  }
297
 
298
  # needed when merging js files
299
+ $js = trim($js); if(!empty($js) && substr($js, 0, 1) != ';') { $js = ';'.$js; }
300
 
301
  # return html
302
  return $js;
329
  }
330
  }
331
 
332
+
333
+ # JSMin https://github.com/rgrove/jsmin-php
334
+ $min = JSMin::minify($js);
335
  if($min !== false && (strlen(trim($js)) == strlen(trim($min)) || strlen(trim($min)) > 0)) { return fvm_compat_urls($min); }
336
 
337
  # if we are here, something went wrong and minification didn't work
341
 
342
  # functions, minify html
343
  function fastvelocity_min_minify_html($html) {
344
+ return fvm_minify_html_output($html);
345
  }
346
 
347
  # functions to minify HTML
348
  function fastvelocity_min_html_compression_finish($html) { return fastvelocity_min_minify_html($html); }
349
  function fastvelocity_min_html_compression_start() {
350
+ if (fastvelocity_exclude_contents() == true) { return; }
351
+ $use_alt_html_minification = get_option('fastvelocity_min_use_alt_html_minification', '0');
352
+ if($use_alt_html_minification == '1') { ob_start('fastvelocity_min_minify_alt_html'); }
353
+ else { ob_start('fastvelocity_min_html_compression_finish'); }
354
  }
355
 
356
  # alternative html minification, minimal
366
  function fastvelocity_rrmdir($dir) {
367
  if(is_dir(rtrim($dir, '/'))) {
368
  if ($handle = opendir($dir.'/')) {
369
+ while (false !== ($file = readdir($handle))) {
370
+ if ($file == '.' || $file == '..') { continue; }
371
+ if(is_file($dir.'/'.$file)) { @unlink($dir.'/'.$file); }
372
+ }
373
  closedir($handle); }
374
  }
375
  }
376
 
377
 
 
 
378
  # return size in human format
379
  function fastvelocity_format_filesize($bytes, $decimals = 2) {
380
  $units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' );
464
  if (file_exists($f)) {
465
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
466
  else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
467
+ fvm_set_transient($tkey, $code);
 
468
  $log = "$printurl --- Debug: $printhandle File was opened from $f ---\n";
469
  return array('log'=>$log, 'code'=>$code);
470
  }
476
  if (file_exists($f)) {
477
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
478
  else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
479
+ fvm_set_transient($tkey, $code);
 
480
  $log = "$printurl --- Debug: $printhandle File was opened from $f ---\n";
481
  return array('log'=>$log, 'code'=>$code);
482
  }
490
  if($code !== false) {
491
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, $code, $disable_minification); }
492
  else { $code = fastvelocity_min_get_css($hurl, $code.$inline, $disable_minification); }
493
+ fvm_set_transient($tkey, $code);
 
494
  $log = "$printurl --- Debug: $printhandle Fetched url at $hurl \n";
495
  return array('log'=>$log, 'code'=>$code);
496
  }
503
  if($code !== false) {
504
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, $code, $disable_minification); }
505
  else { $code = fastvelocity_min_get_css($hurl, $code.$inline, $disable_minification); }
506
+ fvm_set_transient($tkey, $code);
 
507
  $log = "$printurl --- Debug: $printhandle Fetched url at $hurl \n";
508
  return array('log'=>$log, 'code'=>$code);
509
  }
518
  if (file_exists($f)) {
519
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
520
  else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
521
+ fvm_set_transient($tkey, $code);
 
522
  $log = "$printurl --- Debug: $printhandle File was opened from $f ---\n";
523
  return array('log'=>$log, 'code'=>$code);
524
  }
530
  if (file_exists($f)) {
531
  if($type == 'js') { $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification); }
532
  else { $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification); }
533
+ fvm_set_transient($tkey, $code);
 
534
  $log = "$printurl --- Debug: $printhandle File was opened from $f ---\n";
535
  return array('log'=>$log, 'code'=>$code);
536
  }
741
  return $url;
742
  }
743
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
744
 
745
 
746
  # purge all caches
747
  function fvm_purge_all() {
748
 
 
 
749
  # get cache directories and urls
750
  $cachepath = fvm_cachepath();
751
  $tmpdir = $cachepath['tmpdir'];
753
  $cachedirurl = $cachepath['cachedirurl'];
754
 
755
  # delete minification files and transients
756
+ if(is_dir($cachedir)) { fastvelocity_rrmdir($cachedir); }
757
+ if(is_dir($tmpdir)) { fastvelocity_rrmdir($tmpdir); }
 
758
 
759
  return true;
760
  }
831
  function fastvelocity_download($url, $tkey, $ttl) {
832
 
833
  # rate limit requests, prevent slowdowns
834
+ $rtlim = false; $rtlim = fvm_get_transient($tkey.'_access', $ttl);
835
  if ( $rtlim !== false) { return false; }
836
 
837
  # info (needed for google fonts woff files + hinted fonts)
838
  $uagent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586';
839
+ $data = false; $data = fvm_get_transient($tkey, $ttl);
840
  if ( $data === false) {
841
 
842
+ # get contents and cache for the ttl time
843
  $response = wp_remote_get($url, array('user-agent'=>$uagent, 'timeout' => 7, 'httpversion' => '1.1', 'sslverify'=>false));
844
  $res_code = wp_remote_retrieve_response_code($response);
845
  if($res_code == '200') {
846
  $data = wp_remote_retrieve_body($response);
847
  if(strlen($data) > 1 && $ttl > 0) {
848
+ fvm_set_transient($tkey, $data);
 
849
  return $data;
850
  }
851
  }
854
  if(function_exists('curl_version')) {
855
  $curl = fvm_file_get_contents_curl($url, $uagent);
856
  if(!empty($curl) && strlen($curl) > 1 && $ttl > 0) {
857
+ fvm_set_transient($tkey, $curl);
 
858
  return $data;
859
  }
860
  }
861
 
862
  # error
863
+ fvm_set_transient($tkey, '_access', "Failed to fetch: $url on ".current_time('timestamp'));
864
  return false;
865
  }
866
 
libs/CSSTidy/class.csstidy.php ADDED
@@ -0,0 +1,1296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * CSSTidy - CSS Parser and Optimiser
5
+ *
6
+ * CSS Parser class
7
+ *
8
+ * Copyright 2005, 2006, 2007 Florian Schmitz
9
+ *
10
+ * This file is part of CSSTidy.
11
+ *
12
+ * CSSTidy is free software; you can redistribute it and/or modify
13
+ * it under the terms of the GNU Lesser General Public License as published by
14
+ * the Free Software Foundation; either version 2.1 of the License, or
15
+ * (at your option) any later version.
16
+ *
17
+ * CSSTidy is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ * GNU Lesser General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU Lesser General Public License
23
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
24
+ *
25
+ * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
26
+ * @package csstidy
27
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
28
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
29
+ * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
30
+ * @author Cedric Morin (cedric at yterium dot com) 2010-2012
31
+ * @author Christopher Finke (cfinke at gmail.com) 2012
32
+ * @author Mark Scherer (remove $GLOBALS once and for all + PHP5.4 comp) 2012
33
+ */
34
+
35
+ /**
36
+ * Defines ctype functions if required.
37
+ *
38
+ * @TODO: Make these methods of CSSTidy.
39
+ * @since 1.0.0
40
+ */
41
+ if (!function_exists('ctype_space')){
42
+ /* ctype_space Check for whitespace character(s) */
43
+ function ctype_space($text){
44
+ return (1===preg_match("/^[ \r\n\t\f]+$/", $text));
45
+ }
46
+ }
47
+ if (!function_exists('ctype_alpha')){
48
+ /* ctype_alpha Check for alphabetic character(s) */
49
+ function ctype_alpha($text){
50
+ return (1===preg_match('/^[a-zA-Z]+$/', $text));
51
+ }
52
+ }
53
+ if (!function_exists('ctype_xdigit')){
54
+ /* ctype_xdigit Check for HEX character(s) */
55
+ function ctype_xdigit($text){
56
+ return (1===preg_match('/^[a-fA-F0-9]+$/', $text));
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Defines constants
62
+ * @todo //TODO: make them class constants of csstidy
63
+ */
64
+ define('AT_START', 1);
65
+ define('AT_END', 2);
66
+ define('SEL_START', 3);
67
+ define('SEL_END', 4);
68
+ define('PROPERTY', 5);
69
+ define('VALUE', 6);
70
+ define('COMMENT', 7);
71
+ define('DEFAULT_AT', 41);
72
+
73
+ /**
74
+ * Contains a class for printing CSS code
75
+ *
76
+ * @version 1.0
77
+ */
78
+ require('class.csstidy_print.php');
79
+
80
+ /**
81
+ * Contains a class for optimising CSS code
82
+ *
83
+ * @version 1.0
84
+ */
85
+ require('class.csstidy_optimise.php');
86
+
87
+ /**
88
+ * CSS Parser class
89
+ *
90
+ * This class represents a CSS parser which reads CSS code and saves it in an array.
91
+ * In opposite to most other CSS parsers, it does not use regular expressions and
92
+ * thus has full CSS2 support and a higher reliability.
93
+ * Additional to that it applies some optimisations and fixes to the CSS code.
94
+ * An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php
95
+ * @package csstidy
96
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2006
97
+ * @version 1.5.6
98
+ */
99
+ class csstidy {
100
+
101
+ /**
102
+ * Saves the parsed CSS. This array is empty if preserve_css is on.
103
+ * @var array
104
+ * @access public
105
+ */
106
+ public $css = array();
107
+ /**
108
+ * Saves the parsed CSS (raw)
109
+ * @var array
110
+ * @access private
111
+ */
112
+ public $tokens = array();
113
+ /**
114
+ * Printer class
115
+ * @see csstidy_print
116
+ * @var object
117
+ * @access public
118
+ */
119
+ public $print;
120
+ /**
121
+ * Optimiser class
122
+ * @see csstidy_optimise
123
+ * @var object
124
+ * @access private
125
+ */
126
+ public $optimise;
127
+ /**
128
+ * Saves the CSS charset (@charset)
129
+ * @var string
130
+ * @access private
131
+ */
132
+ public $charset = '';
133
+ /**
134
+ * Saves all @import URLs
135
+ * @var array
136
+ * @access private
137
+ */
138
+ public $import = array();
139
+ /**
140
+ * Saves the namespace
141
+ * @var string
142
+ * @access private
143
+ */
144
+ public $namespace = '';
145
+ /**
146
+ * Contains the version of csstidy
147
+ * @var string
148
+ * @access private
149
+ */
150
+ public $version = '1.5.6';
151
+ /**
152
+ * Stores the settings
153
+ * @var array
154
+ * @access private
155
+ */
156
+ public $settings = array();
157
+ /**
158
+ * Saves the parser-status.
159
+ *
160
+ * Possible values:
161
+ * - is = in selector
162
+ * - ip = in property
163
+ * - iv = in value
164
+ * - instr = in string (started at " or ' or ( )
165
+ * - ic = in comment (ignore everything)
166
+ * - at = in @-block
167
+ *
168
+ * @var string
169
+ * @access private
170
+ */
171
+ public $status = 'is';
172
+ /**
173
+ * Saves the current at rule (@media)
174
+ * @var string
175
+ * @access private
176
+ */
177
+ public $at = '';
178
+ /**
179
+ * Saves the at rule for next selector (during @font-face or other @)
180
+ * @var string
181
+ * @access private
182
+ */
183
+ public $next_selector_at = '';
184
+
185
+ /**
186
+ * Saves the current selector
187
+ * @var string
188
+ * @access private
189
+ */
190
+ public $selector = '';
191
+ /**
192
+ * Saves the current property
193
+ * @var string
194
+ * @access private
195
+ */
196
+ public $property = '';
197
+ /**
198
+ * Saves the position of , in selectors
199
+ * @var array
200
+ * @access private
201
+ */
202
+ public $sel_separate = array();
203
+ /**
204
+ * Saves the current value
205
+ * @var string
206
+ * @access private
207
+ */
208
+ public $value = '';
209
+ /**
210
+ * Saves the current sub-value
211
+ *
212
+ * Example for a subvalue:
213
+ * background:url(foo.png) red no-repeat;
214
+ * "url(foo.png)", "red", and "no-repeat" are subvalues,
215
+ * seperated by whitespace
216
+ * @var string
217
+ * @access private
218
+ */
219
+ public $sub_value = '';
220
+ /**
221
+ * Array which saves all subvalues for a property.
222
+ * @var array
223
+ * @see sub_value
224
+ * @access private
225
+ */
226
+ public $sub_value_arr = array();
227
+ /**
228
+ * Saves the stack of characters that opened the current strings
229
+ * @var array
230
+ * @access private
231
+ */
232
+ public $str_char = array();
233
+ public $cur_string = array();
234
+ /**
235
+ * Status from which the parser switched to ic or instr
236
+ * @var array
237
+ * @access private
238
+ */
239
+ public $from = array();
240
+ /**
241
+ /**
242
+ * =true if in invalid at-rule
243
+ * @var bool
244
+ * @access private
245
+ */
246
+ public $invalid_at = false;
247
+ /**
248
+ * =true if something has been added to the current selector
249
+ * @var bool
250
+ * @access private
251
+ */
252
+ public $added = false;
253
+ /**
254
+ * Array which saves the message log
255
+ * @var array
256
+ * @access private
257
+ */
258
+ public $log = array();
259
+ /**
260
+ * Saves the line number
261
+ * @var integer
262
+ * @access private
263
+ */
264
+ public $line = 1;
265
+ /**
266
+ * Marks if we need to leave quotes for a string
267
+ * @var array
268
+ * @access private
269
+ */
270
+ public $quoted_string = array();
271
+
272
+ /**
273
+ * List of tokens
274
+ * @var string
275
+ */
276
+ public $tokens_list = "";
277
+
278
+ /**
279
+ * Various CSS Data for CSSTidy
280
+ * @var array
281
+ */
282
+ public $data = array();
283
+
284
+ /**
285
+ * Loads standard template and sets default settings
286
+ * @access private
287
+ * @version 1.3
288
+ */
289
+ public function __construct() {
290
+ $data = array();
291
+ include('data.inc.php');
292
+ $this->data = $data;
293
+
294
+ $this->settings['remove_bslash'] = true;
295
+ $this->settings['compress_colors'] = true;
296
+ $this->settings['compress_font-weight'] = true;
297
+ $this->settings['lowercase_s'] = false;
298
+ /*
299
+ 1 common shorthands optimization
300
+ 2 + font property optimization
301
+ 3 + background property optimization
302
+ */
303
+ $this->settings['optimise_shorthands'] = 1;
304
+ $this->settings['remove_last_;'] = true;
305
+ $this->settings['space_before_important'] = false;
306
+ /* rewrite all properties with low case, better for later gzip OK, safe*/
307
+ $this->settings['case_properties'] = 1;
308
+ /* sort properties in alpabetic order, better for later gzip
309
+ * but can cause trouble in case of overiding same propertie or using hack
310
+ */
311
+ $this->settings['sort_properties'] = false;
312
+ /*
313
+ 1, 3, 5, etc -- enable sorting selectors inside @media: a{}b{}c{}
314
+ 2, 5, 8, etc -- enable sorting selectors inside one CSS declaration: a,b,c{}
315
+ preserve order by default cause it can break functionnality
316
+ */
317
+ $this->settings['sort_selectors'] = 0;
318
+ /* is dangeroues to be used: CSS is broken sometimes */
319
+ $this->settings['merge_selectors'] = 0;
320
+ /* preserve or not browser hacks */
321
+ $this->settings['discard_invalid_selectors'] = false;
322
+ $this->settings['discard_invalid_properties'] = false;
323
+ $this->settings['css_level'] = 'CSS3.0';
324
+ $this->settings['preserve_css'] = false;
325
+ $this->settings['timestamp'] = false;
326
+ $this->settings['template'] = ''; // say that propertie exist
327
+ $this->set_cfg('template','default'); // call load_template
328
+ $this->optimise = new csstidy_optimise($this);
329
+
330
+ $this->tokens_list = & $this->data['csstidy']['tokens'];
331
+ }
332
+
333
+ /**
334
+ * Get the value of a setting.
335
+ * @param string $setting
336
+ * @access public
337
+ * @return mixed
338
+ * @version 1.0
339
+ */
340
+ public function get_cfg($setting) {
341
+ if (isset($this->settings[$setting])) {
342
+ return $this->settings[$setting];
343
+ }
344
+ return false;
345
+ }
346
+
347
+ /**
348
+ * Load a template
349
+ * @param string $template used by set_cfg to load a template via a configuration setting
350
+ * @access private
351
+ * @version 1.4
352
+ */
353
+ public function _load_template($template) {
354
+ switch ($template) {
355
+ case 'default':
356
+ $this->load_template('default');
357
+ break;
358
+
359
+ case 'highest':
360
+ $this->load_template('highest_compression');
361
+ break;
362
+
363
+ case 'high':
364
+ $this->load_template('high_compression');
365
+ break;
366
+
367
+ case 'low':
368
+ $this->load_template('low_compression');
369
+ break;
370
+
371
+ default:
372
+ $this->load_template($template);
373
+ break;
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Set the value of a setting.
379
+ * @param string $setting
380
+ * @param mixed $value
381
+ * @access public
382
+ * @return bool
383
+ * @version 1.0
384
+ */
385
+ public function set_cfg($setting, $value=null) {
386
+ if (is_array($setting) && $value === null) {
387
+ foreach ($setting as $setprop => $setval) {
388
+ $this->settings[$setprop] = $setval;
389
+ }
390
+ if (array_key_exists('template', $setting)) {
391
+ $this->_load_template($this->settings['template']);
392
+ }
393
+ return true;
394
+ } elseif (isset($this->settings[$setting]) && $value !== '') {
395
+ $this->settings[$setting] = $value;
396
+ if ($setting === 'template') {
397
+ $this->_load_template($this->settings['template']);
398
+ }
399
+ return true;
400
+ }
401
+ return false;
402
+ }
403
+
404
+ /**
405
+ * Adds a token to $this->tokens
406
+ * @param mixed $type
407
+ * @param string $data
408
+ * @param bool $do add a token even if preserve_css is off
409
+ * @access private
410
+ * @version 1.0
411
+ */
412
+ public function _add_token($type, $data, $do = false) {
413
+ if ($this->get_cfg('preserve_css') || $do) {
414
+ $this->tokens[] = array($type, ($type == COMMENT) ? $data : trim($data));
415
+ }
416
+ }
417
+
418
+ /**
419
+ * Add a message to the message log
420
+ * @param string $message
421
+ * @param string $type
422
+ * @param integer $line
423
+ * @access private
424
+ * @version 1.0
425
+ */
426
+ public function log($message, $type, $line = -1) {
427
+ if ($line === -1) {
428
+ $line = $this->line;
429
+ }
430
+ $line = intval($line);
431
+ $add = array('m' => $message, 't' => $type);
432
+ if (!isset($this->log[$line]) || !in_array($add, $this->log[$line])) {
433
+ $this->log[$line][] = $add;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Parse unicode notations and find a replacement character
439
+ * @param string $string
440
+ * @param integer $i
441
+ * @access private
442
+ * @return string
443
+ * @version 1.2
444
+ */
445
+ public function _unicode(&$string, &$i) {
446
+ ++$i;
447
+ $add = '';
448
+ $replaced = false;
449
+
450
+ while ($i < strlen($string) && (ctype_xdigit($string{$i}) || ctype_space($string{$i})) && strlen($add) < 6) {
451
+ $add .= $string{$i};
452
+
453
+ if (ctype_space($string{$i})) {
454
+ break;
455
+ }
456
+ $i++;
457
+ }
458
+
459
+ if (hexdec($add) > 47 && hexdec($add) < 58 || hexdec($add) > 64 && hexdec($add) < 91 || hexdec($add) > 96 && hexdec($add) < 123) {
460
+ $this->log('Replaced unicode notation: Changed \\' . $add . ' to ' . chr(hexdec($add)), 'Information');
461
+ $add = chr(hexdec($add));
462
+ $replaced = true;
463
+ } else {
464
+ $add = trim('\\' . $add);
465
+ }
466
+
467
+ if (@ctype_xdigit($string{$i + 1}) && ctype_space($string{$i})
468
+ && !$replaced || !ctype_space($string{$i})) {
469
+ $i--;
470
+ }
471
+
472
+ if ($add !== '\\' || !$this->get_cfg('remove_bslash') || strpos($this->tokens_list, $string{$i + 1}) !== false) {
473
+ return $add;
474
+ }
475
+
476
+ if ($add === '\\') {
477
+ $this->log('Removed unnecessary backslash', 'Information');
478
+ }
479
+ return '';
480
+ }
481
+
482
+ /**
483
+ * Write formatted output to a file
484
+ * @param string $filename
485
+ * @param string $doctype when printing formatted, is a shorthand for the document type
486
+ * @param bool $externalcss when printing formatted, indicates whether styles to be attached internally or as an external stylesheet
487
+ * @param string $title when printing formatted, is the title to be added in the head of the document
488
+ * @param string $lang when printing formatted, gives a two-letter language code to be added to the output
489
+ * @access public
490
+ * @version 1.4
491
+ */
492
+ public function write_page($filename, $doctype='xhtml1.1', $externalcss=true, $title='', $lang='en') {
493
+ $this->write($filename, true);
494
+ }
495
+
496
+ /**
497
+ * Write plain output to a file
498
+ * @param string $filename
499
+ * @param bool $formatted whether to print formatted or not
500
+ * @param string $doctype when printing formatted, is a shorthand for the document type
501
+ * @param bool $externalcss when printing formatted, indicates whether styles to be attached internally or as an external stylesheet
502
+ * @param string $title when printing formatted, is the title to be added in the head of the document
503
+ * @param string $lang when printing formatted, gives a two-letter language code to be added to the output
504
+ * @param bool $pre_code whether to add pre and code tags around the code (for light HTML formatted templates)
505
+ * @access public
506
+ * @version 1.4
507
+ */
508
+ public function write($filename, $formatted=false, $doctype='xhtml1.1', $externalcss=true, $title='', $lang='en', $pre_code=true) {
509
+ $filename .= ( $formatted) ? '.xhtml' : '.css';
510
+
511
+ if (!is_dir('temp')) {
512
+ $madedir = mkdir('temp');
513
+ if (!$madedir) {
514
+ print 'Could not make directory "temp" in ' . dirname(__FILE__);
515
+ exit;
516
+ }
517
+ }
518
+ $handle = fopen('temp/' . $filename, 'w');
519
+ if ($handle) {
520
+ if (!$formatted) {
521
+ fwrite($handle, $this->print->plain());
522
+ } else {
523
+ fwrite($handle, $this->print->formatted_page($doctype, $externalcss, $title, $lang, $pre_code));
524
+ }
525
+ }
526
+ fclose($handle);
527
+ }
528
+
529
+ /**
530
+ * Loads a new template
531
+ * @param string $content either filename (if $from_file == true), content of a template file, "high_compression", "highest_compression", "low_compression", or "default"
532
+ * @param bool $from_file uses $content as filename if true
533
+ * @access public
534
+ * @version 1.1
535
+ * @see http://csstidy.sourceforge.net/templates.php
536
+ */
537
+ public function load_template($content, $from_file=true) {
538
+ $predefined_templates = & $this->data['csstidy']['predefined_templates'];
539
+ if ($content === 'high_compression' || $content === 'default' || $content === 'highest_compression' || $content === 'low_compression') {
540
+ $this->template = $predefined_templates[$content];
541
+ return;
542
+ }
543
+
544
+
545
+ if ($from_file) {
546
+ $content = strip_tags(file_get_contents($content), '<span>');
547
+ }
548
+ $content = str_replace("\r\n", "\n", $content); // Unify newlines (because the output also only uses \n)
549
+ $template = explode('|', $content);
550
+
551
+ for ($i = 0; $i < count($template); $i++) {
552
+ $this->template[$i] = $template[$i];
553
+ }
554
+ }
555
+
556
+ /**
557
+ * Starts parsing from URL
558
+ * @param string $url
559
+ * @access public
560
+ * @version 1.0
561
+ */
562
+ public function parse_from_url($url) {
563
+ return $this->parse(@file_get_contents($url));
564
+ }
565
+
566
+ /**
567
+ * Checks if there is a token at the current position
568
+ * @param string $string
569
+ * @param integer $i
570
+ * @access public
571
+ * @version 1.11
572
+ */
573
+ public function is_token(&$string, $i) {
574
+ return (strpos($this->tokens_list, $string{$i}) !== false && !$this->escaped($string, $i));
575
+ }
576
+
577
+ /**
578
+ * Parses CSS in $string. The code is saved as array in $this->css
579
+ * @param string $string the CSS code
580
+ * @access public
581
+ * @return bool
582
+ * @version 1.1
583
+ */
584
+ public function parse($string) {
585
+ // Temporarily set locale to en_US in order to handle floats properly
586
+ $old = @setlocale(LC_ALL, 0);
587
+ @setlocale(LC_ALL, 'C');
588
+
589
+ // PHP bug? Settings need to be refreshed in PHP4
590
+ $this->print = new csstidy_print($this);
591
+ $this->optimise = new csstidy_optimise($this);
592
+
593
+ $all_properties = & $this->data['csstidy']['all_properties'];
594
+ $at_rules = & $this->data['csstidy']['at_rules'];
595
+ $quoted_string_properties = & $this->data['csstidy']['quoted_string_properties'];
596
+
597
+ $this->css = array();
598
+ $this->print->input_css = $string;
599
+ $string = str_replace("\r\n", "\n", $string) . ' ';
600
+ $cur_comment = '';
601
+
602
+ for ($i = 0, $size = strlen($string); $i < $size; $i++) {
603
+ if ($string{$i} === "\n" || $string{$i} === "\r") {
604
+ ++$this->line;
605
+ }
606
+
607
+ switch ($this->status) {
608
+ /* Case in at-block */
609
+ case 'at':
610
+ if ($this->is_token($string, $i)) {
611
+ if ($string{$i} === '/' && @$string{$i + 1} === '*') {
612
+ $this->status = 'ic';
613
+ ++$i;
614
+ $this->from[] = 'at';
615
+ } elseif ($string{$i} === '{') {
616
+ $this->status = 'is';
617
+ $this->at = $this->css_new_media_section($this->at);
618
+ $this->_add_token(AT_START, $this->at);
619
+ } elseif ($string{$i} === ',') {
620
+ $this->at = trim($this->at) . ',';
621
+ } elseif ($string{$i} === '\\') {
622
+ $this->at .= $this->_unicode($string, $i);
623
+ }
624
+ // fix for complicated media, i.e @media screen and (-webkit-min-device-pixel-ratio:1.5)
625
+ elseif (in_array($string{$i}, array('(', ')', ':', '.', '/'))) {
626
+ $this->at .= $string{$i};
627
+ }
628
+ } else {
629
+ $lastpos = strlen($this->at) - 1;
630
+ if (!( (ctype_space($this->at{$lastpos}) || $this->is_token($this->at, $lastpos) && $this->at{$lastpos} === ',') && ctype_space($string{$i}))) {
631
+ $this->at .= $string{$i};
632
+ }
633
+ }
634
+ break;
635
+
636
+ /* Case in-selector */
637
+ case 'is':
638
+ if ($this->is_token($string, $i)) {
639
+ if ($string{$i} === '/' && @$string{$i + 1} === '*' && trim($this->selector) == '') {
640
+ $this->status = 'ic';
641
+ ++$i;
642
+ $this->from[] = 'is';
643
+ } elseif ($string{$i} === '@' && trim($this->selector) == '') {
644
+ // Check for at-rule
645
+ $this->invalid_at = true;
646
+ foreach ($at_rules as $name => $type) {
647
+ if (!strcasecmp(substr($string, $i + 1, strlen($name)), $name)) {
648
+ ($type === 'at') ? $this->at = '@' . $name : $this->selector = '@' . $name;
649
+ if ($type === 'atis') {
650
+ $this->next_selector_at = ($this->next_selector_at?$this->next_selector_at:($this->at?$this->at:DEFAULT_AT));
651
+ $this->at = $this->css_new_media_section(' ');
652
+ $type = 'is';
653
+ }
654
+ $this->status = $type;
655
+ $i += strlen($name);
656
+ $this->invalid_at = false;
657
+ }
658
+ }
659
+
660
+ if ($this->invalid_at) {
661
+ $this->selector = '@';
662
+ $invalid_at_name = '';
663
+ for ($j = $i + 1; $j < $size; ++$j) {
664
+ if (!ctype_alpha($string{$j})) {
665
+ break;
666
+ }
667
+ $invalid_at_name .= $string{$j};
668
+ }
669
+ $this->log('Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning');
670
+ }
671
+ } elseif (($string{$i} === '"' || $string{$i} === "'")) {
672
+ $this->cur_string[] = $string{$i};
673
+ $this->status = 'instr';
674
+ $this->str_char[] = $string{$i};
675
+ $this->from[] = 'is';
676
+ /* fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */
677
+ $this->quoted_string[] = ($string{$i - 1} === '=' );
678
+ } elseif ($this->invalid_at && $string{$i} === ';') {
679
+ $this->invalid_at = false;
680
+ $this->status = 'is';
681
+ if ($this->next_selector_at) {
682
+ $this->at = $this->css_new_media_section($this->next_selector_at);
683
+ $this->next_selector_at = '';
684
+ }
685
+ } elseif ($string{$i} === '{') {
686
+ $this->status = 'ip';
687
+ if ($this->at == '') {
688
+ $this->at = $this->css_new_media_section(DEFAULT_AT);
689
+ }
690
+ $this->selector = $this->css_new_selector($this->at,$this->selector);
691
+ $this->_add_token(SEL_START, $this->selector);
692
+ $this->added = false;
693
+ } elseif ($string{$i} === '}') {
694
+ $this->_add_token(AT_END, $this->at);
695
+ $this->at = '';
696
+ $this->selector = '';
697
+ $this->sel_separate = array();
698
+ } elseif ($string{$i} === ',') {
699
+ $this->selector = trim($this->selector) . ',';
700
+ $this->sel_separate[] = strlen($this->selector);
701
+ } elseif ($string{$i} === '\\') {
702
+ $this->selector .= $this->_unicode($string, $i);
703
+ } elseif ($string{$i} === '*' && @in_array($string{$i + 1}, array('.', '#', '[', ':')) && ($i==0 OR $string{$i - 1}!=='/')) {
704
+ // remove unnecessary universal selector, FS#147, but not comment in selector
705
+ } else {
706
+ $this->selector .= $string{$i};
707
+ }
708
+ } else {
709
+ $lastpos = strlen($this->selector) - 1;
710
+ if ($lastpos == -1 || !( (ctype_space($this->selector{$lastpos}) || $this->is_token($this->selector, $lastpos) && $this->selector{$lastpos} === ',') && ctype_space($string{$i}))) {
711
+ $this->selector .= $string{$i};
712
+ }
713
+ }
714
+ break;
715
+
716
+ /* Case in-property */
717
+ case 'ip':
718
+ if ($this->is_token($string, $i)) {
719
+ if (($string{$i} === ':' || $string{$i} === '=') && $this->property != '') {
720
+ $this->status = 'iv';
721
+ if (!$this->get_cfg('discard_invalid_properties') || $this->property_is_valid($this->property)) {
722
+ $this->property = $this->css_new_property($this->at,$this->selector,$this->property);
723
+ $this->_add_token(PROPERTY, $this->property);
724
+ }
725
+ } elseif ($string{$i} === '/' && @$string{$i + 1} === '*' && $this->property == '') {
726
+ $this->status = 'ic';
727
+ ++$i;
728
+ $this->from[] = 'ip';
729
+ } elseif ($string{$i} === '}') {
730
+ $this->explode_selectors();
731
+ $this->status = 'is';
732
+ $this->invalid_at = false;
733
+ $this->_add_token(SEL_END, $this->selector);
734
+ $this->selector = '';
735
+ $this->property = '';
736
+ if ($this->next_selector_at) {
737
+ $this->at = $this->css_new_media_section($this->next_selector_at);
738
+ $this->next_selector_at = '';
739
+ }
740
+ } elseif ($string{$i} === ';') {
741
+ $this->property = '';
742
+ } elseif ($string{$i} === '\\') {
743
+ $this->property .= $this->_unicode($string, $i);
744
+ }
745
+ // else this is dumb IE a hack, keep it
746
+ // including //
747
+ elseif (($this->property === '' && !ctype_space($string{$i}))
748
+ || ($this->property === '/' || $string{$i} === '/')) {
749
+ $this->property .= $string{$i};
750
+ }
751
+ } elseif (!ctype_space($string{$i})) {
752
+ $this->property .= $string{$i};
753
+ }
754
+ break;
755
+
756
+ /* Case in-value */
757
+ case 'iv':
758
+ $pn = (($string{$i} === "\n" || $string{$i} === "\r") && $this->property_is_next($string, $i + 1) || $i == strlen($string) - 1);
759
+ if ($this->is_token($string, $i) || $pn) {
760
+ if ($string{$i} === '/' && @$string{$i + 1} === '*') {
761
+ $this->status = 'ic';
762
+ ++$i;
763
+ $this->from[] = 'iv';
764
+ } elseif (($string{$i} === '"' || $string{$i} === "'" || $string{$i} === '(')) {
765
+ $this->cur_string[] = $string{$i};
766
+ $this->str_char[] = ($string{$i} === '(') ? ')' : $string{$i};
767
+ $this->status = 'instr';
768
+ $this->from[] = 'iv';
769
+ $this->quoted_string[] = in_array(strtolower($this->property), $quoted_string_properties);
770
+ } elseif ($string{$i} === ',') {
771
+ $this->sub_value = trim($this->sub_value) . ',';
772
+ } elseif ($string{$i} === '\\') {
773
+ $this->sub_value .= $this->_unicode($string, $i);
774
+ } elseif ($string{$i} === ';' || $pn) {
775
+ if ($this->selector{0} === '@' && isset($at_rules[substr($this->selector, 1)]) && $at_rules[substr($this->selector, 1)] === 'iv') {
776
+ /* Add quotes to charset, import, namespace */
777
+ $this->sub_value_arr[] = trim($this->sub_value);
778
+
779
+ $this->status = 'is';
780
+
781
+ switch ($this->selector) {
782
+ case '@charset': $this->charset = '"'.$this->sub_value_arr[0].'"';
783
+ break;
784
+ case '@namespace': $this->namespace = implode(' ', $this->sub_value_arr);
785
+ break;
786
+ case '@import': $this->import[] = implode(' ', $this->sub_value_arr);
787
+ break;
788
+ }
789
+
790
+ $this->sub_value_arr = array();
791
+ $this->sub_value = '';
792
+ $this->selector = '';
793
+ $this->sel_separate = array();
794
+ } else {
795
+ $this->status = 'ip';
796
+ }
797
+ } elseif ($string{$i} !== '}') {
798
+ $this->sub_value .= $string{$i};
799
+ }
800
+ if (($string{$i} === '}' || $string{$i} === ';' || $pn) && !empty($this->selector)) {
801
+ if ($this->at == '') {
802
+ $this->at = $this->css_new_media_section(DEFAULT_AT);
803
+ }
804
+
805
+ // case settings
806
+ if ($this->get_cfg('lowercase_s')) {
807
+ $this->selector = strtolower($this->selector);
808
+ }
809
+ $this->property = strtolower($this->property);
810
+
811
+ $this->optimise->subvalue();
812
+ if ($this->sub_value != '') {
813
+ $this->sub_value_arr[] = $this->sub_value;
814
+ $this->sub_value = '';
815
+ }
816
+
817
+ $this->value = '';
818
+ while (count($this->sub_value_arr)) {
819
+ $sub = array_shift($this->sub_value_arr);
820
+ if (strstr($this->selector, 'font-face')) {
821
+ $sub = $this->quote_font_format($sub);
822
+ }
823
+
824
+ if ($sub != '')
825
+ $this->value .= ((!strlen($this->value) || substr($this->value,-1,1) === ',')?'':' ').$sub;
826
+ }
827
+
828
+ $this->optimise->value();
829
+
830
+ $valid = $this->property_is_valid($this->property);
831
+ if ((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid)) {
832
+ $this->css_add_property($this->at, $this->selector, $this->property, $this->value);
833
+ $this->_add_token(VALUE, $this->value);
834
+ $this->optimise->shorthands();
835
+ }
836
+ if (!$valid) {
837
+ if ($this->get_cfg('discard_invalid_properties')) {
838
+ $this->log('Removed invalid property: ' . $this->property, 'Warning');
839
+ } else {
840
+ $this->log('Invalid property in ' . strtoupper($this->get_cfg('css_level')) . ': ' . $this->property, 'Warning');
841
+ }
842
+ }
843
+
844
+ $this->property = '';
845
+ $this->sub_value_arr = array();
846
+ $this->value = '';
847
+ }
848
+ if ($string{$i} === '}') {
849
+ $this->explode_selectors();
850
+ $this->_add_token(SEL_END, $this->selector);
851
+ $this->status = 'is';
852
+ $this->invalid_at = false;
853
+ $this->selector = '';
854
+ if ($this->next_selector_at) {
855
+ $this->at = $this->css_new_media_section($this->next_selector_at);
856
+ $this->next_selector_at = '';
857
+ }
858
+ }
859
+ } elseif (!$pn) {
860
+ $this->sub_value .= $string{$i};
861
+
862
+ if (ctype_space($string{$i})) {
863
+ $this->optimise->subvalue();
864
+ if ($this->sub_value != '') {
865
+ $this->sub_value_arr[] = $this->sub_value;
866
+ $this->sub_value = '';
867
+ }
868
+ }
869
+ }
870
+ break;
871
+
872
+ /* Case in string */
873
+ case 'instr':
874
+ $_str_char = $this->str_char[count($this->str_char)-1];
875
+ $_cur_string = $this->cur_string[count($this->cur_string)-1];
876
+ $_quoted_string = $this->quoted_string[count($this->quoted_string)-1];
877
+ $temp_add = $string{$i};
878
+
879
+ // Add another string to the stack. Strings can't be nested inside of quotes, only parentheses, but
880
+ // parentheticals can be nested more than once.
881
+ if ($_str_char === ")" && ($string{$i} === "(" || $string{$i} === '"' || $string{$i} === '\'') && !$this->escaped($string, $i)) {
882
+ $this->cur_string[] = $string{$i};
883
+ $this->str_char[] = $string{$i} === '(' ? ')' : $string{$i};
884
+ $this->from[] = 'instr';
885
+ $this->quoted_string[] = ($_str_char === ')' && $string{$i} !== '(' && trim($_cur_string)==='(')?$_quoted_string:!($string{$i} === '(');
886
+ continue;
887
+ }
888
+
889
+ if ($_str_char !== ")" && ($string{$i} === "\n" || $string{$i} === "\r") && !($string{$i - 1} === '\\' && !$this->escaped($string, $i - 1))) {
890
+ $temp_add = "\\A";
891
+ $this->log('Fixed incorrect newline in string', 'Warning');
892
+ }
893
+
894
+ $_cur_string .= $temp_add;
895
+
896
+ if ($string{$i} === $_str_char && !$this->escaped($string, $i)) {
897
+ $this->status = array_pop($this->from);
898
+
899
+ if (!preg_match('|[' . implode('', $this->data['csstidy']['whitespace']) . ']|uis', $_cur_string) && $this->property !== 'content') {
900
+ if (!$_quoted_string) {
901
+ if ($_str_char !== ')') {
902
+ // Convert properties like
903
+ // font-family: 'Arial';
904
+ // to
905
+ // font-family: Arial;
906
+ // or
907
+ // url("abc")
908
+ // to
909
+ // url(abc)
910
+ $_cur_string = substr($_cur_string, 1, -1);
911
+ }
912
+ } else {
913
+ $_quoted_string = false;
914
+ }
915
+ }
916
+
917
+ array_pop($this->cur_string);
918
+ array_pop($this->quoted_string);
919
+ array_pop($this->str_char);
920
+
921
+ if ($_str_char === ')') {
922
+ $_cur_string = '(' . trim(substr($_cur_string, 1, -1)) . ')';
923
+ }
924
+
925
+ if ($this->status === 'iv') {
926
+ if (!$_quoted_string) {
927
+ if (strpos($_cur_string,',') !== false)
928
+ // we can on only remove space next to ','
929
+ $_cur_string = implode(',', array_map('trim', explode(',',$_cur_string)));
930
+ // and multiple spaces (too expensive)
931
+ if (strpos($_cur_string, ' ') !== false)
932
+ $_cur_string = preg_replace(",\s+,", ' ', $_cur_string);
933
+ }
934
+ $this->sub_value .= $_cur_string;
935
+ } elseif ($this->status === 'is') {
936
+ $this->selector .= $_cur_string;
937
+ } elseif ($this->status === 'instr') {
938
+ $this->cur_string[count($this->cur_string)-1] .= $_cur_string;
939
+ }
940
+ } else {
941
+ $this->cur_string[count($this->cur_string)-1] = $_cur_string;
942
+ }
943
+ break;
944
+
945
+ /* Case in-comment */
946
+ case 'ic':
947
+ if ($string{$i} === '*' && $string{$i + 1} === '/') {
948
+ $this->status = array_pop($this->from);
949
+ $i++;
950
+ $this->_add_token(COMMENT, $cur_comment);
951
+ $cur_comment = '';
952
+ } else {
953
+ $cur_comment .= $string{$i};
954
+ }
955
+ break;
956
+ }
957
+ }
958
+
959
+ $this->optimise->postparse();
960
+
961
+ $this->print->_reset();
962
+
963
+ @setlocale(LC_ALL, $old); // Set locale back to original setting
964
+
965
+ return!(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace));
966
+ }
967
+
968
+
969
+ /**
970
+ * format() in font-face needs quoted values for somes browser (FF at least)
971
+ *
972
+ * @param $value
973
+ * @return string
974
+ */
975
+ public function quote_font_format($value) {
976
+ if (strncmp($value,'format',6) == 0) {
977
+ $p = strpos($value,')',7);
978
+ $end = substr($value,$p);
979
+ $format_strings = $this->parse_string_list(substr($value, 7, $p-7));
980
+ if (!$format_strings) {
981
+ $value = '';
982
+ } else {
983
+ $value = 'format(';
984
+
985
+ foreach ($format_strings as $format_string) {
986
+ $value .= '"' . str_replace('"', '\\"', $format_string) . '",';
987
+ }
988
+
989
+ $value = substr($value, 0, -1) . $end;
990
+ }
991
+ }
992
+ return $value;
993
+ }
994
+
995
+ /**
996
+ * Explodes selectors
997
+ * @access private
998
+ * @version 1.0
999
+ */
1000
+ public function explode_selectors() {
1001
+ // Explode multiple selectors
1002
+ if ($this->get_cfg('merge_selectors') === 1) {
1003
+ $new_sels = array();
1004
+ $lastpos = 0;
1005
+ $this->sel_separate[] = strlen($this->selector);
1006
+ foreach ($this->sel_separate as $num => $pos) {
1007
+ if ($num == count($this->sel_separate) - 1) {
1008
+ $pos += 1;
1009
+ }
1010
+
1011
+ $new_sels[] = substr($this->selector, $lastpos, $pos - $lastpos - 1);
1012
+ $lastpos = $pos;
1013
+ }
1014
+
1015
+ if (count($new_sels) > 1) {
1016
+ foreach ($new_sels as $selector) {
1017
+ if (isset($this->css[$this->at][$this->selector])) {
1018
+ $this->merge_css_blocks($this->at, $selector, $this->css[$this->at][$this->selector]);
1019
+ }
1020
+ }
1021
+ unset($this->css[$this->at][$this->selector]);
1022
+ }
1023
+ }
1024
+ $this->sel_separate = array();
1025
+ }
1026
+
1027
+ /**
1028
+ * Checks if a character is escaped (and returns true if it is)
1029
+ * @param string $string
1030
+ * @param integer $pos
1031
+ * @access public
1032
+ * @return bool
1033
+ * @version 1.02
1034
+ */
1035
+ static function escaped(&$string, $pos) {
1036
+ return!(@($string{$pos - 1} !== '\\') || csstidy::escaped($string, $pos - 1));
1037
+ }
1038
+
1039
+ /**
1040
+ * Adds a property with value to the existing CSS code
1041
+ * @param string $media
1042
+ * @param string $selector
1043
+ * @param string $property
1044
+ * @param string $new_val
1045
+ * @access private
1046
+ * @version 1.2
1047
+ */
1048
+ public function css_add_property($media, $selector, $property, $new_val) {
1049
+ if ($this->get_cfg('preserve_css') || trim($new_val) == '') {
1050
+ return;
1051
+ }
1052
+
1053
+ $this->added = true;
1054
+ if (isset($this->css[$media][$selector][$property])) {
1055
+ if (($this->is_important($this->css[$media][$selector][$property]) && $this->is_important($new_val)) || !$this->is_important($this->css[$media][$selector][$property])) {
1056
+ $this->css[$media][$selector][$property] = trim($new_val);
1057
+ }
1058
+ } else {
1059
+ $this->css[$media][$selector][$property] = trim($new_val);
1060
+ }
1061
+ }
1062
+
1063
+ /**
1064
+ * Start a new media section.
1065
+ * Check if the media is not already known,
1066
+ * else rename it with extra spaces
1067
+ * to avoid merging
1068
+ *
1069
+ * @param string $media
1070
+ * @return string
1071
+ */
1072
+ public function css_new_media_section($media) {
1073
+ if ($this->get_cfg('preserve_css')) {
1074
+ return $media;
1075
+ }
1076
+
1077
+ // if the last @media is the same as this
1078
+ // keep it
1079
+ if (!$this->css || !is_array($this->css) || empty($this->css)) {
1080
+ return $media;
1081
+ }
1082
+ end($this->css);
1083
+ list($at,) = each($this->css);
1084
+ if ($at == $media) {
1085
+ return $media;
1086
+ }
1087
+ while (isset($this->css[$media]))
1088
+ if (is_numeric($media))
1089
+ $media++;
1090
+ else
1091
+ $media .= ' ';
1092
+ return $media;
1093
+ }
1094
+
1095
+ /**
1096
+ * Start a new selector.
1097
+ * If already referenced in this media section,
1098
+ * rename it with extra space to avoid merging
1099
+ * except if merging is required,
1100
+ * or last selector is the same (merge siblings)
1101
+ *
1102
+ * never merge @font-face
1103
+ *
1104
+ * @param string $media
1105
+ * @param string $selector
1106
+ * @return string
1107
+ */
1108
+ public function css_new_selector($media,$selector) {
1109
+ if ($this->get_cfg('preserve_css')) {
1110
+ return $selector;
1111
+ }
1112
+ $selector = trim($selector);
1113
+ if (strncmp($selector,'@font-face',10)!=0) {
1114
+ if ($this->settings['merge_selectors'] != false)
1115
+ return $selector;
1116
+
1117
+ if (!$this->css || !isset($this->css[$media]) || !$this->css[$media])
1118
+ return $selector;
1119
+
1120
+ // if last is the same, keep it
1121
+ end($this->css[$media]);
1122
+ list($sel,) = each($this->css[$media]);
1123
+ if ($sel == $selector) {
1124
+ return $selector;
1125
+ }
1126
+ }
1127
+
1128
+ while (isset($this->css[$media][$selector]))
1129
+ $selector .= ' ';
1130
+ return $selector;
1131
+ }
1132
+
1133
+ /**
1134
+ * Start a new propertie.
1135
+ * If already references in this selector,
1136
+ * rename it with extra space to avoid override
1137
+ *
1138
+ * @param string $media
1139
+ * @param string $selector
1140
+ * @param string $property
1141
+ * @return string
1142
+ */
1143
+ public function css_new_property($media, $selector, $property) {
1144
+ if ($this->get_cfg('preserve_css')) {
1145
+ return $property;
1146
+ }
1147
+ if (!$this->css || !isset($this->css[$media][$selector]) || !$this->css[$media][$selector])
1148
+ return $property;
1149
+
1150
+ while (isset($this->css[$media][$selector][$property]))
1151
+ $property .= ' ';
1152
+
1153
+ return $property;
1154
+ }
1155
+
1156
+ /**
1157
+ * Adds CSS to an existing media/selector
1158
+ * @param string $media
1159
+ * @param string $selector
1160
+ * @param array $css_add
1161
+ * @access private
1162
+ * @version 1.1
1163
+ */
1164
+ public function merge_css_blocks($media, $selector, $css_add) {
1165
+ foreach ($css_add as $property => $value) {
1166
+ $this->css_add_property($media, $selector, $property, $value, false);
1167
+ }
1168
+ }
1169
+
1170
+ /**
1171
+ * Checks if $value is !important.
1172
+ * @param string $value
1173
+ * @return bool
1174
+ * @access public
1175
+ * @version 1.0
1176
+ */
1177
+ public function is_important(&$value) {
1178
+ return (
1179
+ strpos($value, '!') !== false // quick test
1180
+ AND !strcasecmp(substr(str_replace($this->data['csstidy']['whitespace'], '', $value), -10, 10), '!important'));
1181
+ }
1182
+
1183
+ /**
1184
+ * Returns a value without !important
1185
+ * @param string $value
1186
+ * @return string
1187
+ * @access public
1188
+ * @version 1.0
1189
+ */
1190
+ public function gvw_important($value) {
1191
+ if ($this->is_important($value)) {
1192
+ $value = trim($value);
1193
+ $value = substr($value, 0, -9);
1194
+ $value = trim($value);
1195
+ $value = substr($value, 0, -1);
1196
+ $value = trim($value);
1197
+ return $value;
1198
+ }
1199
+ return $value;
1200
+ }
1201
+
1202
+ /**
1203
+ * Checks if the next word in a string from pos is a CSS property
1204
+ * @param string $istring
1205
+ * @param integer $pos
1206
+ * @return bool
1207
+ * @access private
1208
+ * @version 1.2
1209
+ */
1210
+ public function property_is_next($istring, $pos) {
1211
+ $all_properties = & $this->data['csstidy']['all_properties'];
1212
+ $istring = substr($istring, $pos, strlen($istring) - $pos);
1213
+ $pos = strpos($istring, ':');
1214
+ if ($pos === false) {
1215
+ return false;
1216
+ }
1217
+ $istring = strtolower(trim(substr($istring, 0, $pos)));
1218
+ if (isset($all_properties[$istring])) {
1219
+ $this->log('Added semicolon to the end of declaration', 'Warning');
1220
+ return true;
1221
+ }
1222
+ return false;
1223
+ }
1224
+
1225
+ /**
1226
+ * Checks if a property is valid
1227
+ * @param string $property
1228
+ * @return bool;
1229
+ * @access public
1230
+ * @version 1.0
1231
+ */
1232
+ public function property_is_valid($property) {
1233
+ if (in_array(trim($property), $this->data['csstidy']['multiple_properties'])) $property = trim($property);
1234
+ $all_properties = & $this->data['csstidy']['all_properties'];
1235
+ return (isset($all_properties[$property]) && strpos($all_properties[$property], strtoupper($this->get_cfg('css_level'))) !== false );
1236
+ }
1237
+
1238
+ /**
1239
+ * Accepts a list of strings (e.g., the argument to format() in a @font-face src property)
1240
+ * and returns a list of the strings. Converts things like:
1241
+ *
1242
+ * format(abc) => format("abc")
1243
+ * format(abc def) => format("abc","def")
1244
+ * format(abc "def") => format("abc","def")
1245
+ * format(abc, def, ghi) => format("abc","def","ghi")
1246
+ * format("abc",'def') => format("abc","def")
1247
+ * format("abc, def, ghi") => format("abc, def, ghi")
1248
+ *
1249
+ * @param string
1250
+ * @return array
1251
+ */
1252
+
1253
+ public function parse_string_list($value) {
1254
+ $value = trim($value);
1255
+
1256
+ // Case: empty
1257
+ if (!$value) return array();
1258
+
1259
+ $strings = array();
1260
+
1261
+ $in_str = false;
1262
+ $current_string = '';
1263
+
1264
+ for ($i = 0, $_len = strlen($value); $i < $_len; $i++) {
1265
+ if (($value{$i} === ',' || $value{$i} === ' ') && $in_str === true) {
1266
+ $in_str = false;
1267
+ $strings[] = $current_string;
1268
+ $current_string = '';
1269
+ } elseif ($value{$i} === '"' || $value{$i} === "'") {
1270
+ if ($in_str === $value{$i}) {
1271
+ $strings[] = $current_string;
1272
+ $in_str = false;
1273
+ $current_string = '';
1274
+ continue;
1275
+ } elseif (!$in_str) {
1276
+ $in_str = $value{$i};
1277
+ }
1278
+ } else {
1279
+ if ($in_str) {
1280
+ $current_string .= $value{$i};
1281
+ } else {
1282
+ if (!preg_match("/[\s,]/", $value{$i})) {
1283
+ $in_str = true;
1284
+ $current_string = $value{$i};
1285
+ }
1286
+ }
1287
+ }
1288
+ }
1289
+
1290
+ if ($current_string) {
1291
+ $strings[] = $current_string;
1292
+ }
1293
+
1294
+ return $strings;
1295
+ }
1296
+ }
libs/CSSTidy/class.csstidy_optimise.php ADDED
@@ -0,0 +1,963 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * CSSTidy - CSS Parser and Optimiser
5
+ *
6
+ * CSS Optimising Class
7
+ * This class optimises CSS data generated by csstidy.
8
+ *
9
+ * Copyright 2005, 2006, 2007 Florian Schmitz
10
+ *
11
+ * This file is part of CSSTidy.
12
+ *
13
+ * CSSTidy is free software; you can redistribute it and/or modify
14
+ * it under the terms of the GNU Lesser General Public License as published by
15
+ * the Free Software Foundation; either version 2.1 of the License, or
16
+ * (at your option) any later version.
17
+ *
18
+ * CSSTidy is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ * GNU Lesser General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU Lesser General Public License
24
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
25
+ *
26
+ * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
27
+ * @package csstidy
28
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
29
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
30
+ * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
31
+ * @author Cedric Morin (cedric at yterium dot com) 2010-2012
32
+ */
33
+
34
+ /**
35
+ * CSS Optimising Class
36
+ *
37
+ * This class optimises CSS data generated by csstidy.
38
+ *
39
+ * @package csstidy
40
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2006
41
+ * @version 1.0
42
+ */
43
+ class csstidy_optimise {
44
+
45
+ /**
46
+ * csstidy object
47
+ * @var object
48
+ */
49
+ public $parser;
50
+
51
+ /**
52
+ * Constructor
53
+ * @param array $css contains the class csstidy
54
+ * @access private
55
+ * @version 1.0
56
+ */
57
+ public function __construct($css) {
58
+ $this->parser = $css;
59
+ $this->css = & $css->css;
60
+ $this->sub_value = & $css->sub_value;
61
+ $this->at = & $css->at;
62
+ $this->selector = & $css->selector;
63
+ $this->property = & $css->property;
64
+ $this->value = & $css->value;
65
+ }
66
+
67
+ /**
68
+ * Optimises $css after parsing
69
+ * @access public
70
+ * @version 1.0
71
+ */
72
+ public function postparse() {
73
+ if ($this->parser->get_cfg('preserve_css')) {
74
+ return;
75
+ }
76
+
77
+ if ((int)$this->parser->get_cfg('merge_selectors') === 2) {
78
+ foreach ($this->css as $medium => $value) {
79
+ $this->merge_selectors($this->css[$medium]);
80
+ }
81
+ }
82
+
83
+ if ($this->parser->get_cfg('discard_invalid_selectors')) {
84
+ foreach ($this->css as $medium => $value) {
85
+ $this->discard_invalid_selectors($this->css[$medium]);
86
+ }
87
+ }
88
+
89
+ if ($this->parser->get_cfg('optimise_shorthands') > 0) {
90
+ foreach ($this->css as $medium => $value) {
91
+ foreach ($value as $selector => $value1) {
92
+ $this->css[$medium][$selector] = $this->merge_4value_shorthands($this->css[$medium][$selector]);
93
+
94
+ if ($this->parser->get_cfg('optimise_shorthands') < 2) {
95
+ continue;
96
+ }
97
+
98
+ $this->css[$medium][$selector] = $this->merge_font($this->css[$medium][$selector]);
99
+
100
+ if ($this->parser->get_cfg('optimise_shorthands') < 3) {
101
+ continue;
102
+ }
103
+
104
+ $this->css[$medium][$selector] = $this->merge_bg($this->css[$medium][$selector]);
105
+ if (empty($this->css[$medium][$selector])) {
106
+ unset($this->css[$medium][$selector]);
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Optimises values
115
+ * @access public
116
+ * @version 1.0
117
+ */
118
+ public function value() {
119
+ $shorthands = & $this->parser->data['csstidy']['shorthands'];
120
+
121
+ // optimise shorthand properties
122
+ if (isset($shorthands[$this->property])) {
123
+ $temp = $this->shorthand($this->value); // FIXME - move
124
+ if ($temp != $this->value) {
125
+ $this->parser->log('Optimised shorthand notation (' . $this->property . '): Changed "' . $this->value . '" to "' . $temp . '"', 'Information');
126
+ }
127
+ $this->value = $temp;
128
+ }
129
+
130
+ // Remove whitespace at ! important
131
+ if ($this->value != $this->compress_important($this->value)) {
132
+ $this->parser->log('Optimised !important', 'Information');
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Optimises shorthands
138
+ * @access public
139
+ * @version 1.0
140
+ */
141
+ public function shorthands() {
142
+ $shorthands = & $this->parser->data['csstidy']['shorthands'];
143
+
144
+ if (!$this->parser->get_cfg('optimise_shorthands') || $this->parser->get_cfg('preserve_css')) {
145
+ return;
146
+ }
147
+
148
+ if ($this->property === 'font' && $this->parser->get_cfg('optimise_shorthands') > 1) {
149
+ $this->css[$this->at][$this->selector]['font']='';
150
+ $this->parser->merge_css_blocks($this->at, $this->selector, $this->dissolve_short_font($this->value));
151
+ }
152
+ if ($this->property === 'background' && $this->parser->get_cfg('optimise_shorthands') > 2) {
153
+ $this->css[$this->at][$this->selector]['background']='';
154
+ $this->parser->merge_css_blocks($this->at, $this->selector, $this->dissolve_short_bg($this->value));
155
+ }
156
+ if (isset($shorthands[$this->property])) {
157
+ $this->parser->merge_css_blocks($this->at, $this->selector, $this->dissolve_4value_shorthands($this->property, $this->value));
158
+ if (is_array($shorthands[$this->property])) {
159
+ $this->css[$this->at][$this->selector][$this->property] = '';
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Optimises a sub-value
166
+ * @access public
167
+ * @version 1.0
168
+ */
169
+ public function subvalue() {
170
+ $replace_colors = & $this->parser->data['csstidy']['replace_colors'];
171
+
172
+ $this->sub_value = trim($this->sub_value);
173
+ if ($this->sub_value == '') { // caution : '0'
174
+ return;
175
+ }
176
+
177
+ $important = '';
178
+ if ($this->parser->is_important($this->sub_value)) {
179
+ $important = '!important';
180
+ }
181
+ $this->sub_value = $this->parser->gvw_important($this->sub_value);
182
+
183
+ // Compress font-weight
184
+ if ($this->property === 'font-weight' && $this->parser->get_cfg('compress_font-weight')) {
185
+ if ($this->sub_value === 'bold') {
186
+ $this->sub_value = '700';
187
+ $this->parser->log('Optimised font-weight: Changed "bold" to "700"', 'Information');
188
+ } elseif ($this->sub_value === 'normal') {
189
+ $this->sub_value = '400';
190
+ $this->parser->log('Optimised font-weight: Changed "normal" to "400"', 'Information');
191
+ }
192
+ }
193
+
194
+ $temp = $this->compress_numbers($this->sub_value);
195
+ if (strcasecmp($temp, $this->sub_value) !== 0) {
196
+ if (strlen($temp) > strlen($this->sub_value)) {
197
+ $this->parser->log('Fixed invalid number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning');
198
+ } else {
199
+ $this->parser->log('Optimised number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information');
200
+ }
201
+ $this->sub_value = $temp;
202
+ }
203
+ if ($this->parser->get_cfg('compress_colors')) {
204
+ $temp = $this->cut_color($this->sub_value);
205
+ if ($temp !== $this->sub_value) {
206
+ if (isset($replace_colors[$this->sub_value])) {
207
+ $this->parser->log('Fixed invalid color name: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning');
208
+ } else {
209
+ $this->parser->log('Optimised color: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information');
210
+ }
211
+ $this->sub_value = $temp;
212
+ }
213
+ }
214
+ $this->sub_value .= $important;
215
+ }
216
+
217
+ /**
218
+ * Compresses shorthand values. Example: margin:1px 1px 1px 1px -> margin:1px
219
+ * @param string $value
220
+ * @access public
221
+ * @return string
222
+ * @version 1.0
223
+ */
224
+ public function shorthand($value) {
225
+ $important = '';
226
+ if ($this->parser->is_important($value)) {
227
+ $values = $this->parser->gvw_important($value);
228
+ $important = '!important';
229
+ }
230
+ else
231
+ $values = $value;
232
+
233
+ $values = explode(' ', $values);
234
+ switch (count($values)) {
235
+ case 4:
236
+ if ($values[0] == $values[1] && $values[0] == $values[2] && $values[0] == $values[3]) {
237
+ return $values[0] . $important;
238
+ } elseif ($values[1] == $values[3] && $values[0] == $values[2]) {
239
+ return $values[0] . ' ' . $values[1] . $important;
240
+ } elseif ($values[1] == $values[3]) {
241
+ return $values[0] . ' ' . $values[1] . ' ' . $values[2] . $important;
242
+ }
243
+ break;
244
+
245
+ case 3:
246
+ if ($values[0] == $values[1] && $values[0] == $values[2]) {
247
+ return $values[0] . $important;
248
+ } elseif ($values[0] == $values[2]) {
249
+ return $values[0] . ' ' . $values[1] . $important;
250
+ }
251
+ break;
252
+
253
+ case 2:
254
+ if ($values[0] == $values[1]) {
255
+ return $values[0] . $important;
256
+ }
257
+ break;
258
+ }
259
+
260
+ return $value;
261
+ }
262
+
263
+ /**
264
+ * Removes unnecessary whitespace in ! important
265
+ * @param string $string
266
+ * @return string
267
+ * @access public
268
+ * @version 1.1
269
+ */
270
+ public function compress_important(&$string) {
271
+ if ($this->parser->is_important($string)) {
272
+ $important = $this->parser->get_cfg('space_before_important') ? ' !important' : '!important';
273
+ $string = $this->parser->gvw_important($string) . $important;
274
+ }
275
+ return $string;
276
+ }
277
+
278
+ /**
279
+ * Color compression function. Converts all rgb() values to #-values and uses the short-form if possible. Also replaces 4 color names by #-values.
280
+ * @param string $color
281
+ * @return string
282
+ * @version 1.1
283
+ */
284
+ public function cut_color($color) {
285
+ $replace_colors = & $this->parser->data['csstidy']['replace_colors'];
286
+
287
+ // if it's a string, don't touch !
288
+ if (strncmp($color, "'", 1) == 0 || strncmp($color, '"', 1) == 0)
289
+ return $color;
290
+
291
+ /* expressions complexes de type gradient */
292
+ if (strpos($color, '(') !== false && strncmp($color, 'rgb(' ,4) != 0) {
293
+ // on ne touche pas aux couleurs dans les expression ms, c'est trop sensible
294
+ if (stripos($color, 'progid:') !== false)
295
+ return $color;
296
+ preg_match_all(",rgb\([^)]+\),i", $color, $matches, PREG_SET_ORDER);
297
+ if (count($matches)) {
298
+ foreach ($matches as $m) {
299
+ $color = str_replace($m[0], $this->cut_color($m[0]), $color);
300
+ }
301
+ }
302
+ preg_match_all(",#[0-9a-f]{6}(?=[^0-9a-f]),i", $color, $matches, PREG_SET_ORDER);
303
+ if (count($matches)) {
304
+ foreach ($matches as $m) {
305
+ $color = str_replace($m[0],$this->cut_color($m[0]), $color);
306
+ }
307
+ }
308
+ return $color;
309
+ }
310
+
311
+ // rgb(0,0,0) -> #000000 (or #000 in this case later)
312
+ if (strncasecmp($color, 'rgb(', 4)==0) {
313
+ $color_tmp = substr($color, 4, strlen($color) - 5);
314
+ $color_tmp = explode(',', $color_tmp);
315
+ for ($i = 0; $i < count($color_tmp); $i++) {
316
+ $color_tmp[$i] = trim($color_tmp[$i]);
317
+ if (substr($color_tmp[$i], -1) === '%') {
318
+ $color_tmp[$i] = round((255 * $color_tmp[$i]) / 100);
319
+ }
320
+ if ($color_tmp[$i] > 255)
321
+ $color_tmp[$i] = 255;
322
+ }
323
+ $color = '#';
324
+ for ($i = 0; $i < 3; $i++) {
325
+ if ($color_tmp[$i] < 16) {
326
+ $color .= '0' . dechex($color_tmp[$i]);
327
+ } else {
328
+ $color .= dechex($color_tmp[$i]);
329
+ }
330
+ }
331
+ }
332
+
333
+ // Fix bad color names
334
+ if (isset($replace_colors[strtolower($color)])) {
335
+ $color = $replace_colors[strtolower($color)];
336
+ }
337
+
338
+ // #aabbcc -> #abc
339
+ if (strlen($color) == 7) {
340
+ $color_temp = strtolower($color);
341
+ if ($color_temp{0} === '#' && $color_temp{1} == $color_temp{2} && $color_temp{3} == $color_temp{4} && $color_temp{5} == $color_temp{6}) {
342
+ $color = '#' . $color{1} . $color{3} . $color{5};
343
+ }
344
+ }
345
+
346
+ switch (strtolower($color)) {
347
+ /* color name -> hex code */
348
+ case 'black': return '#000';
349
+ case 'fuchsia': return '#f0f';
350
+ case 'white': return '#fff';
351
+ case 'yellow': return '#ff0';
352
+
353
+ /* hex code -> color name */
354
+ case '#800000': return 'maroon';
355
+ case '#ffa500': return 'orange';
356
+ case '#808000': return 'olive';
357
+ case '#800080': return 'purple';
358
+ case '#008000': return 'green';
359
+ case '#000080': return 'navy';
360
+ case '#008080': return 'teal';
361
+ case '#c0c0c0': return 'silver';
362
+ case '#808080': return 'gray';
363
+ case '#f00': return 'red';
364
+ }
365
+
366
+ return $color;
367
+ }
368
+
369
+ /**
370
+ * Compresses numbers (ie. 1.0 becomes 1 or 1.100 becomes 1.1 )
371
+ * @param string $subvalue
372
+ * @return string
373
+ * @version 1.2
374
+ */
375
+ public function compress_numbers($subvalue) {
376
+ $unit_values = & $this->parser->data['csstidy']['unit_values'];
377
+ $color_values = & $this->parser->data['csstidy']['color_values'];
378
+
379
+ // for font:1em/1em sans-serif...;
380
+ if ($this->property === 'font') {
381
+ $temp = explode('/', $subvalue);
382
+ } else {
383
+ $temp = array($subvalue);
384
+ }
385
+
386
+ for ($l = 0; $l < count($temp); $l++) {
387
+ // if we are not dealing with a number at this point, do not optimise anything
388
+ $number = $this->AnalyseCssNumber($temp[$l]);
389
+ if ($number === false) {
390
+ return $subvalue;
391
+ }
392
+
393
+ // Fix bad colors
394
+ if (in_array($this->property, $color_values)) {
395
+ $temp[$l] = '#' . $temp[$l];
396
+ continue;
397
+ }
398
+
399
+ if (abs($number[0]) > 0) {
400
+ if ($number[1] == '' && in_array($this->property, $unit_values, true)) {
401
+ $number[1] = 'px';
402
+ }
403
+ } elseif ($number[1] != 's' && $number[1] != 'ms') {
404
+ $number[1] = '';
405
+ }
406
+
407
+ $temp[$l] = $number[0] . $number[1];
408
+ }
409
+
410
+ return ((count($temp) > 1) ? $temp[0] . '/' . $temp[1] : $temp[0]);
411
+ }
412
+
413
+ /**
414
+ * Checks if a given string is a CSS valid number. If it is,
415
+ * an array containing the value and unit is returned
416
+ * @param string $string
417
+ * @return array ('unit' if unit is found or '' if no unit exists, number value) or false if no number
418
+ */
419
+ public function AnalyseCssNumber($string) {
420
+ // most simple checks first
421
+ if (strlen($string) == 0 || ctype_alpha($string{0})) {
422
+ return false;
423
+ }
424
+
425
+ $units = & $this->parser->data['csstidy']['units'];
426
+ $return = array(0, '');
427
+
428
+ $return[0] = floatval($string);
429
+ if (abs($return[0]) > 0 && abs($return[0]) < 1) {
430
+ if ($return[0] < 0) {
431
+ $return[0] = '-' . ltrim(substr($return[0], 1), '0');
432
+ } else {
433
+ $return[0] = ltrim($return[0], '0');
434
+ }
435
+ }
436
+
437
+ // Look for unit and split from value if exists
438
+ foreach ($units as $unit) {
439
+ $expectUnitAt = strlen($string) - strlen($unit);
440
+ if (!($unitInString = stristr($string, $unit))) { // mb_strpos() fails with "false"
441
+ continue;
442
+ }
443
+ $actualPosition = strpos($string, $unitInString);
444
+ if ($expectUnitAt === $actualPosition) {
445
+ $return[1] = $unit;
446
+ $string = substr($string, 0, - strlen($unit));
447
+ break;
448
+ }
449
+ }
450
+ if (!is_numeric($string)) {
451
+ return false;
452
+ }
453
+ return $return;
454
+ }
455
+
456
+ /**
457
+ * Merges selectors with same properties. Example: a{color:red} b{color:red} -> a,b{color:red}
458
+ * Very basic and has at least one bug. Hopefully there is a replacement soon.
459
+ * @param array $array
460
+ * @return array
461
+ * @access public
462
+ * @version 1.2
463
+ */
464
+ public function merge_selectors(&$array) {
465
+ $css = $array;
466
+ foreach ($css as $key => $value) {
467
+ if (!isset($css[$key])) {
468
+ continue;
469
+ }
470
+ $newsel = '';
471
+
472
+ // Check if properties also exist in another selector
473
+ $keys = array();
474
+ // PHP bug (?) without $css = $array; here
475
+ foreach ($css as $selector => $vali) {
476
+ if ($selector == $key) {
477
+ continue;
478
+ }
479
+
480
+ if ($css[$key] === $vali) {
481
+ $keys[] = $selector;
482
+ }
483
+ }
484
+
485
+ if (!empty($keys)) {
486
+ $newsel = $key;
487
+ unset($css[$key]);
488
+ foreach ($keys as $selector) {
489
+ unset($css[$selector]);
490
+ $newsel .= ',' . $selector;
491
+ }
492
+ $css[$newsel] = $value;
493
+ }
494
+ }
495
+ $array = $css;
496
+ }
497
+
498
+ /**
499
+ * Removes invalid selectors and their corresponding rule-sets as
500
+ * defined by 4.1.7 in REC-CSS2. This is a very rudimentary check
501
+ * and should be replaced by a full-blown parsing algorithm or
502
+ * regular expression
503
+ * @version 1.4
504
+ */
505
+ public function discard_invalid_selectors(&$array) {
506
+ $invalid = array('+' => true, '~' => true, ',' => true, '>' => true);
507
+ foreach ($array as $selector => $decls) {
508
+ $ok = true;
509
+ $selectors = array_map('trim', explode(',', $selector));
510
+ foreach ($selectors as $s) {
511
+ $simple_selectors = preg_split('/\s*[+>~\s]\s*/', $s);
512
+ foreach ($simple_selectors as $ss) {
513
+ if ($ss === '')
514
+ $ok = false;
515
+ // could also check $ss for internal structure,
516
+ // but that probably would be too slow
517
+ }
518
+ }
519
+ if (!$ok)
520
+ unset($array[$selector]);
521
+ }
522
+ }
523
+
524
+ /**
525
+ * Dissolves properties like padding:10px 10px 10px to padding-top:10px;padding-bottom:10px;...
526
+ * @param string $property
527
+ * @param string $value
528
+ * @return array
529
+ * @version 1.0
530
+ * @see merge_4value_shorthands()
531
+ */
532
+ public function dissolve_4value_shorthands($property, $value) {
533
+ $shorthands = & $this->parser->data['csstidy']['shorthands'];
534
+ if (!is_array($shorthands[$property])) {
535
+ $return[$property] = $value;
536
+ return $return;
537
+ }
538
+
539
+ $important = '';
540
+ if ($this->parser->is_important($value)) {
541
+ $value = $this->parser->gvw_important($value);
542
+ $important = '!important';
543
+ }
544
+ $values = explode(' ', $value);
545
+
546
+
547
+ $return = array();
548
+ if (count($values) == 4) {
549
+ for ($i = 0; $i < 4; $i++) {
550
+ $return[$shorthands[$property][$i]] = $values[$i] . $important;
551
+ }
552
+ } elseif (count($values) == 3) {
553
+ $return[$shorthands[$property][0]] = $values[0] . $important;
554
+ $return[$shorthands[$property][1]] = $values[1] . $important;
555
+ $return[$shorthands[$property][3]] = $values[1] . $important;
556
+ $return[$shorthands[$property][2]] = $values[2] . $important;
557
+ } elseif (count($values) == 2) {
558
+ for ($i = 0; $i < 4; $i++) {
559
+ $return[$shorthands[$property][$i]] = (($i % 2 != 0)) ? $values[1] . $important : $values[0] . $important;
560
+ }
561
+ } else {
562
+ for ($i = 0; $i < 4; $i++) {
563
+ $return[$shorthands[$property][$i]] = $values[0] . $important;
564
+ }
565
+ }
566
+
567
+ return $return;
568
+ }
569
+
570
+ /**
571
+ * Explodes a string as explode() does, however, not if $sep is escaped or within a string.
572
+ * @param string $sep seperator
573
+ * @param string $string
574
+ * @return array
575
+ * @version 1.0
576
+ */
577
+ public function explode_ws($sep, $string) {
578
+ $status = 'st';
579
+ $to = '';
580
+
581
+ $output = array();
582
+ $num = 0;
583
+ for ($i = 0, $len = strlen($string); $i < $len; $i++) {
584
+ switch ($status) {
585
+ case 'st':
586
+ if ($string{$i} == $sep && !$this->parser->escaped($string, $i)) {
587
+ ++$num;
588
+ } elseif ($string{$i} === '"' || $string{$i} === '\'' || $string{$i} === '(' && !$this->parser->escaped($string, $i)) {
589
+ $status = 'str';
590
+ $to = ($string{$i} === '(') ? ')' : $string{$i};
591
+ (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
592
+ } else {
593
+ (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
594
+ }
595
+ break;
596
+
597
+ case 'str':
598
+ if ($string{$i} == $to && !$this->parser->escaped($string, $i)) {
599
+ $status = 'st';
600
+ }
601
+ (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
602
+ break;
603
+ }
604
+ }
605
+
606
+ if (isset($output[0])) {
607
+ return $output;
608
+ } else {
609
+ return array($output);
610
+ }
611
+ }
612
+
613
+ /**
614
+ * Merges Shorthand properties again, the opposite of dissolve_4value_shorthands()
615
+ * @param array $array
616
+ * @return array
617
+ * @version 1.2
618
+ * @see dissolve_4value_shorthands()
619
+ */
620
+ public function merge_4value_shorthands($array) {
621
+ $return = $array;
622
+ $shorthands = & $this->parser->data['csstidy']['shorthands'];
623
+
624
+ foreach ($shorthands as $key => $value) {
625
+ if (isset($array[$value[0]]) && isset($array[$value[1]])
626
+ && isset($array[$value[2]]) && isset($array[$value[3]]) && $value !== 0) {
627
+ $return[$key] = '';
628
+
629
+ $important = '';
630
+ for ($i = 0; $i < 4; $i++) {
631
+ $val = $array[$value[$i]];
632
+ if ($this->parser->is_important($val)) {
633
+ $important = '!important';
634
+ $return[$key] .= $this->parser->gvw_important($val) . ' ';
635
+ } else {
636
+ $return[$key] .= $val . ' ';
637
+ }
638
+ unset($return[$value[$i]]);
639
+ }
640
+ $return[$key] = $this->shorthand(trim($return[$key] . $important));
641
+ }
642
+ }
643
+ return $return;
644
+ }
645
+
646
+ /**
647
+ * Dissolve background property
648
+ * @param string $str_value
649
+ * @return array
650
+ * @version 1.0
651
+ * @see merge_bg()
652
+ * @todo full CSS 3 compliance
653
+ */
654
+ public function dissolve_short_bg($str_value) {
655
+ // don't try to explose background gradient !
656
+ if (stripos($str_value, 'gradient(')!== false)
657
+ return array('background'=>$str_value);
658
+
659
+ $background_prop_default = & $this->parser->data['csstidy']['background_prop_default'];
660
+ $repeat = array('repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'space');
661
+ $attachment = array('scroll', 'fixed', 'local');
662
+ $clip = array('border', 'padding');
663
+ $origin = array('border', 'padding', 'content');
664
+ $pos = array('top', 'center', 'bottom', 'left', 'right');
665
+ $important = '';
666
+ $return = array('background-image' => null, 'background-size' => null, 'background-repeat' => null, 'background-position' => null, 'background-attachment' => null, 'background-clip' => null, 'background-origin' => null, 'background-color' => null);
667
+
668
+ if ($this->parser->is_important($str_value)) {
669
+ $important = ' !important';
670
+ $str_value = $this->parser->gvw_important($str_value);
671
+ }
672
+
673
+ $str_value = $this->explode_ws(',', $str_value);
674
+ for ($i = 0; $i < count($str_value); $i++) {
675
+ $have['clip'] = false;
676
+ $have['pos'] = false;
677
+ $have['color'] = false;
678
+ $have['bg'] = false;
679
+
680
+ if (is_array($str_value[$i])) {
681
+ $str_value[$i] = $str_value[$i][0];
682
+ }
683
+ $str_value[$i] = $this->explode_ws(' ', trim($str_value[$i]));
684
+
685
+ for ($j = 0; $j < count($str_value[$i]); $j++) {
686
+ if ($have['bg'] === false && (substr($str_value[$i][$j], 0, 4) === 'url(' || $str_value[$i][$j] === 'none')) {
687
+ $return['background-image'] .= $str_value[$i][$j] . ',';
688
+ $have['bg'] = true;
689
+ } elseif (in_array($str_value[$i][$j], $repeat, true)) {
690
+ $return['background-repeat'] .= $str_value[$i][$j] . ',';
691
+ } elseif (in_array($str_value[$i][$j], $attachment, true)) {
692
+ $return['background-attachment'] .= $str_value[$i][$j] . ',';
693
+ } elseif (in_array($str_value[$i][$j], $clip, true) && !$have['clip']) {
694
+ $return['background-clip'] .= $str_value[$i][$j] . ',';
695
+ $have['clip'] = true;
696
+ } elseif (in_array($str_value[$i][$j], $origin, true)) {
697
+ $return['background-origin'] .= $str_value[$i][$j] . ',';
698
+ } elseif ($str_value[$i][$j]{0} === '(') {
699
+ $return['background-size'] .= substr($str_value[$i][$j], 1, -1) . ',';
700
+ } elseif (in_array($str_value[$i][$j], $pos, true) || is_numeric($str_value[$i][$j]{0}) || $str_value[$i][$j]{0} === null || $str_value[$i][$j]{0} === '-' || $str_value[$i][$j]{0} === '.') {
701
+ $return['background-position'] .= $str_value[$i][$j];
702
+ if (!$have['pos'])
703
+ $return['background-position'] .= ' '; else
704
+ $return['background-position'].= ',';
705
+ $have['pos'] = true;
706
+ } elseif (!$have['color']) {
707
+ $return['background-color'] .= $str_value[$i][$j] . ',';
708
+ $have['color'] = true;
709
+ }
710
+ }
711
+ }
712
+
713
+ foreach ($background_prop_default as $bg_prop => $default_value) {
714
+ if ($return[$bg_prop] !== null) {
715
+ $return[$bg_prop] = substr($return[$bg_prop], 0, -1) . $important;
716
+ }
717
+ else
718
+ $return[$bg_prop] = $default_value . $important;
719
+ }
720
+ return $return;
721
+ }
722
+
723
+ /**
724
+ * Merges all background properties
725
+ * @param array $input_css
726
+ * @return array
727
+ * @version 1.0
728
+ * @see dissolve_short_bg()
729
+ * @todo full CSS 3 compliance
730
+ */
731
+ public function merge_bg($input_css) {
732
+ $background_prop_default = & $this->parser->data['csstidy']['background_prop_default'];
733
+ // Max number of background images. CSS3 not yet fully implemented
734
+ $number_of_values = @max(count($this->explode_ws(',', $input_css['background-image'])), count($this->explode_ws(',', $input_css['background-color'])), 1);
735
+ // Array with background images to check if BG image exists
736
+ $bg_img_array = @$this->explode_ws(',', $this->parser->gvw_important($input_css['background-image']));
737
+ $new_bg_value = '';
738
+ $important = '';
739
+
740
+ // if background properties is here and not empty, don't try anything
741
+ if (isset($input_css['background']) && $input_css['background'])
742
+ return $input_css;
743
+
744
+ for ($i = 0; $i < $number_of_values; $i++) {
745
+ foreach ($background_prop_default as $bg_property => $default_value) {
746
+ // Skip if property does not exist
747
+ if (!isset($input_css[$bg_property])) {
748
+ continue;
749
+ }
750
+
751
+ $cur_value = $input_css[$bg_property];
752
+ // skip all optimisation if gradient() somewhere
753
+ if (stripos($cur_value, 'gradient(') !== false)
754
+ return $input_css;
755
+
756
+ // Skip some properties if there is no background image
757
+ if ((!isset($bg_img_array[$i]) || $bg_img_array[$i] === 'none')
758
+ && ($bg_property === 'background-size' || $bg_property === 'background-position'
759
+ || $bg_property === 'background-attachment' || $bg_property === 'background-repeat')) {
760
+ continue;
761
+ }
762
+
763
+ // Remove !important
764
+ if ($this->parser->is_important($cur_value)) {
765
+ $important = ' !important';
766
+ $cur_value = $this->parser->gvw_important($cur_value);
767
+ }
768
+
769
+ // Do not add default values
770
+ if ($cur_value === $default_value) {
771
+ continue;
772
+ }
773
+
774
+ $temp = $this->explode_ws(',', $cur_value);
775
+
776
+ if (isset($temp[$i])) {
777
+ if ($bg_property === 'background-size') {
778
+ $new_bg_value .= '(' . $temp[$i] . ') ';
779
+ } else {
780
+ $new_bg_value .= $temp[$i] . ' ';
781
+ }
782
+ }
783
+ }
784
+
785
+ $new_bg_value = trim($new_bg_value);
786
+ if ($i != $number_of_values - 1)
787
+ $new_bg_value .= ',';
788
+ }
789
+
790
+ // Delete all background-properties
791
+ foreach ($background_prop_default as $bg_property => $default_value) {
792
+ unset($input_css[$bg_property]);
793
+ }
794
+
795
+ // Add new background property
796
+ if ($new_bg_value !== '')
797
+ $input_css['background'] = $new_bg_value . $important;
798
+ elseif(isset ($input_css['background']))
799
+ $input_css['background'] = 'none';
800
+
801
+ return $input_css;
802
+ }
803
+
804
+ /**
805
+ * Dissolve font property
806
+ * @param string $str_value
807
+ * @return array
808
+ * @version 1.3
809
+ * @see merge_font()
810
+ */
811
+ public function dissolve_short_font($str_value) {
812
+ $font_prop_default = & $this->parser->data['csstidy']['font_prop_default'];
813
+ $font_weight = array('normal', 'bold', 'bolder', 'lighter', 100, 200, 300, 400, 500, 600, 700, 800, 900);
814
+ $font_variant = array('normal', 'small-caps');
815
+ $font_style = array('normal', 'italic', 'oblique');
816
+ $important = '';
817
+ $return = array('font-style' => null, 'font-variant' => null, 'font-weight' => null, 'font-size' => null, 'line-height' => null, 'font-family' => null);
818
+
819
+ if ($this->parser->is_important($str_value)) {
820
+ $important = '!important';
821
+ $str_value = $this->parser->gvw_important($str_value);
822
+ }
823
+
824
+ $have['style'] = false;
825
+ $have['variant'] = false;
826
+ $have['weight'] = false;
827
+ $have['size'] = false;
828
+ // Detects if font-family consists of several words w/o quotes
829
+ $multiwords = false;
830
+
831
+ // Workaround with multiple font-family
832
+ $str_value = $this->explode_ws(',', trim($str_value));
833
+
834
+ $str_value[0] = $this->explode_ws(' ', trim($str_value[0]));
835
+
836
+ for ($j = 0; $j < count($str_value[0]); $j++) {
837
+ if ($have['weight'] === false && in_array($str_value[0][$j], $font_weight)) {
838
+ $return['font-weight'] = $str_value[0][$j];
839
+ $have['weight'] = true;
840
+ } elseif ($have['variant'] === false && in_array($str_value[0][$j], $font_variant)) {
841
+ $return['font-variant'] = $str_value[0][$j];
842
+ $have['variant'] = true;
843
+ } elseif ($have['style'] === false && in_array($str_value[0][$j], $font_style)) {
844
+ $return['font-style'] = $str_value[0][$j];
845
+ $have['style'] = true;
846
+ } elseif ($have['size'] === false && (is_numeric($str_value[0][$j]{0}) || $str_value[0][$j]{0} === null || $str_value[0][$j]{0} === '.')) {
847
+ $size = $this->explode_ws('/', trim($str_value[0][$j]));
848
+ $return['font-size'] = $size[0];
849
+ if (isset($size[1])) {
850
+ $return['line-height'] = $size[1];
851
+ } else {
852
+ $return['line-height'] = ''; // don't add 'normal' !
853
+ }
854
+ $have['size'] = true;
855
+ } else {
856
+ if (isset($return['font-family'])) {
857
+ $return['font-family'] .= ' ' . $str_value[0][$j];
858
+ $multiwords = true;
859
+ } else {
860
+ $return['font-family'] = $str_value[0][$j];
861
+ }
862
+ }
863
+ }
864
+ // add quotes if we have several qords in font-family
865
+ if ($multiwords !== false) {
866
+ $return['font-family'] = '"' . $return['font-family'] . '"';
867
+ }
868
+ $i = 1;
869
+ while (isset($str_value[$i])) {
870
+ $return['font-family'] .= ',' . trim($str_value[$i]);
871
+ $i++;
872
+ }
873
+
874
+ // Fix for 100 and more font-size
875
+ if ($have['size'] === false && isset($return['font-weight']) &&
876
+ is_numeric($return['font-weight']{0})) {
877
+ $return['font-size'] = $return['font-weight'];
878
+ unset($return['font-weight']);
879
+ }
880
+
881
+ foreach ($font_prop_default as $font_prop => $default_value) {
882
+ if ($return[$font_prop] !== null) {
883
+ $return[$font_prop] = $return[$font_prop] . $important;
884
+ }
885
+ else
886
+ $return[$font_prop] = $default_value . $important;
887
+ }
888
+ return $return;
889
+ }
890
+
891
+ /**
892
+ * Merges all fonts properties
893
+ * @param array $input_css
894
+ * @return array
895
+ * @version 1.3
896
+ * @see dissolve_short_font()
897
+ */
898
+ public function merge_font($input_css) {
899
+ $font_prop_default = & $this->parser->data['csstidy']['font_prop_default'];
900
+ $new_font_value = '';
901
+ $important = '';
902
+ // Skip if not font-family and font-size set
903
+ if (isset($input_css['font-family']) && isset($input_css['font-size']) && $input_css['font-family'] != 'inherit') {
904
+ // fix several words in font-family - add quotes
905
+ if (isset($input_css['font-family'])) {
906
+ $families = explode(',', $input_css['font-family']);
907
+ $result_families = array();
908
+ foreach ($families as $family) {
909
+ $family = trim($family);
910
+ $len = strlen($family);
911
+ if (strpos($family, ' ') &&
912
+ !(($family{0} === '"' && $family{$len - 1} === '"') ||
913
+ ($family{0} === "'" && $family{$len - 1} === "'"))) {
914
+ $family = '"' . $family . '"';
915
+ }
916
+ $result_families[] = $family;
917
+ }
918
+ $input_css['font-family'] = implode(',', $result_families);
919
+ }
920
+ foreach ($font_prop_default as $font_property => $default_value) {
921
+
922
+ // Skip if property does not exist
923
+ if (!isset($input_css[$font_property])) {
924
+ continue;
925
+ }
926
+
927
+ $cur_value = $input_css[$font_property];
928
+
929
+ // Skip if default value is used
930
+ if ($cur_value === $default_value) {
931
+ continue;
932
+ }
933
+
934
+ // Remove !important
935
+ if ($this->parser->is_important($cur_value)) {
936
+ $important = '!important';
937
+ $cur_value = $this->parser->gvw_important($cur_value);
938
+ }
939
+
940
+ $new_font_value .= $cur_value;
941
+ // Add delimiter
942
+ $new_font_value .= ( $font_property === 'font-size' &&
943
+ isset($input_css['line-height'])) ? '/' : ' ';
944
+ }
945
+
946
+ $new_font_value = trim($new_font_value);
947
+
948
+ // Delete all font-properties
949
+ foreach ($font_prop_default as $font_property => $default_value) {
950
+ if ($font_property !== 'font' || !$new_font_value)
951
+ unset($input_css[$font_property]);
952
+ }
953
+
954
+ // Add new font property
955
+ if ($new_font_value !== '') {
956
+ $input_css['font'] = $new_font_value . $important;
957
+ }
958
+ }
959
+
960
+ return $input_css;
961
+ }
962
+
963
+ }
libs/CSSTidy/class.csstidy_print.php ADDED
@@ -0,0 +1,446 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * CSSTidy - CSS Parser and Optimiser
5
+ *
6
+ * CSS Printing class
7
+ * This class prints CSS data generated by csstidy.
8
+ *
9
+ * Copyright 2005, 2006, 2007 Florian Schmitz
10
+ *
11
+ * This file is part of CSSTidy.
12
+ *
13
+ * CSSTidy is free software; you can redistribute it and/or modify
14
+ * it under the terms of the GNU Lesser General Public License as published by
15
+ * the Free Software Foundation; either version 2.1 of the License, or
16
+ * (at your option) any later version.
17
+ *
18
+ * CSSTidy is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ * GNU Lesser General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU Lesser General Public License
24
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
25
+ *
26
+ * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
27
+ * @package csstidy
28
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
29
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
30
+ * @author Cedric Morin (cedric at yterium dot com) 2010-2012
31
+ */
32
+
33
+ /**
34
+ * CSS Printing class
35
+ *
36
+ * This class prints CSS data generated by csstidy.
37
+ *
38
+ * @package csstidy
39
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2006
40
+ * @version 1.1.0
41
+ */
42
+ class csstidy_print {
43
+
44
+ /**
45
+ * csstidy object
46
+ * @var object
47
+ */
48
+ public $parser;
49
+
50
+ /**
51
+ * Saves the input CSS string
52
+ * @var string
53
+ * @access private
54
+ */
55
+ public $input_css = '';
56
+ /**
57
+ * Saves the formatted CSS string
58
+ * @var string
59
+ * @access public
60
+ */
61
+ public $output_css = '';
62
+ /**
63
+ * Saves the formatted CSS string (plain text)
64
+ * @var string
65
+ * @access public
66
+ */
67
+ public $output_css_plain = '';
68
+
69
+ /**
70
+ * Constructor
71
+ * @param array $css contains the class csstidy
72
+ * @access private
73
+ * @version 1.0
74
+ */
75
+ public function __construct($css) {
76
+ $this->parser = $css;
77
+ $this->css = & $css->css;
78
+ $this->template = & $css->template;
79
+ $this->tokens = & $css->tokens;
80
+ $this->charset = & $css->charset;
81
+ $this->import = & $css->import;
82
+ $this->namespace = & $css->namespace;
83
+ }
84
+
85
+ /**
86
+ * Resets output_css and output_css_plain (new css code)
87
+ * @access private
88
+ * @version 1.0
89
+ */
90
+ public function _reset() {
91
+ $this->output_css = '';
92
+ $this->output_css_plain = '';
93
+ }
94
+
95
+ /**
96
+ * Returns the CSS code as plain text
97
+ * @param string $default_media default @media to add to selectors without any @media
98
+ * @return string
99
+ * @access public
100
+ * @version 1.0
101
+ */
102
+ public function plain($default_media='') {
103
+ $this->_print(true, $default_media);
104
+ return $this->output_css_plain;
105
+ }
106
+
107
+ /**
108
+ * Returns the formatted CSS code
109
+ * @param string $default_media default @media to add to selectors without any @media
110
+ * @return string
111
+ * @access public
112
+ * @version 1.0
113
+ */
114
+ public function formatted($default_media='') {
115
+ $this->_print(false, $default_media);
116
+ return $this->output_css;
117
+ }
118
+
119
+ /**
120
+ * Returns the formatted CSS code to make a complete webpage
121
+ * @param string $doctype shorthand for the document type
122
+ * @param bool $externalcss indicates whether styles to be attached internally or as an external stylesheet
123
+ * @param string $title title to be added in the head of the document
124
+ * @param string $lang two-letter language code to be added to the output
125
+ * @return string
126
+ * @access public
127
+ * @version 1.4
128
+ */
129
+ public function formatted_page($doctype='html5', $externalcss=true, $title='', $lang='en') {
130
+ switch ($doctype) {
131
+ case 'html5':
132
+ $doctype_output = '<!DOCTYPE html>';
133
+ break;
134
+ case 'xhtml1.0strict':
135
+ $doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
136
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
137
+ break;
138
+ case 'xhtml1.1':
139
+ default:
140
+ $doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
141
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
142
+ break;
143
+ }
144
+
145
+ $output = $cssparsed = '';
146
+ $this->output_css_plain = & $output;
147
+
148
+ $output .= $doctype_output . "\n" . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . $lang . '"';
149
+ $output .= ( $doctype === 'xhtml1.1') ? '>' : ' lang="' . $lang . '">';
150
+ $output .= "\n<head>\n <title>$title</title>";
151
+
152
+ if ($externalcss) {
153
+ $output .= "\n <style type=\"text/css\">\n";
154
+ $cssparsed = file_get_contents('cssparsed.css');
155
+ $output .= $cssparsed; // Adds an invisible BOM or something, but not in css_optimised.php
156
+ $output .= "\n</style>";
157
+ } else {
158
+ $output .= "\n" . ' <link rel="stylesheet" type="text/css" href="cssparsed.css" />';
159
+ }
160
+ $output .= "\n</head>\n<body><code id=\"copytext\">";
161
+ $output .= $this->formatted();
162
+ $output .= '</code>' . "\n" . '</body></html>';
163
+ return $this->output_css_plain;
164
+ }
165
+
166
+ /**
167
+ * Returns the formatted CSS Code and saves it into $this->output_css and $this->output_css_plain
168
+ * @param bool $plain plain text or not
169
+ * @param string $default_media default @media to add to selectors without any @media
170
+ * @access private
171
+ * @version 2.0
172
+ */
173
+ public function _print($plain = false, $default_media='') {
174
+ if ($this->output_css && $this->output_css_plain) {
175
+ return;
176
+ }
177
+
178
+ $output = '';
179
+ if (!$this->parser->get_cfg('preserve_css')) {
180
+ $this->_convert_raw_css($default_media);
181
+ }
182
+
183
+ $template = & $this->template;
184
+
185
+ if ($plain) {
186
+ $template = array_map('strip_tags', $template);
187
+ }
188
+
189
+ if ($this->parser->get_cfg('timestamp')) {
190
+ array_unshift($this->tokens, array(COMMENT, ' CSSTidy ' . $this->parser->version . ': ' . date('r') . ' '));
191
+ }
192
+
193
+ if (!empty($this->charset)) {
194
+ $output .= $template[0] . '@charset ' . $template[5] . $this->charset . $template[6] . $template[13];
195
+ }
196
+
197
+ if (!empty($this->import)) {
198
+ for ($i = 0, $size = count($this->import); $i < $size; $i++) {
199
+ if (substr($this->import[$i], 0, 4) === 'url(' && substr($this->import[$i], -1, 1) === ')') {
200
+ $this->import[$i] = '"' . substr($this->import[$i], 4, -1) . '"';
201
+ $this->parser->log('Optimised @import : Removed "url("', 'Information');
202
+ }
203
+ else if (!preg_match('/^".+"$/',$this->import[$i])) {
204
+ // fixes a bug for @import ".." instead of the expected @import url("..")
205
+ // If it comes in due to @import ".." the "" will be missing and the output will become @import .. (which is an error)
206
+ $this->import[$i] = '"' . $this->import[$i] . '"';
207
+ }
208
+
209
+ $output .= $template[0] . '@import ' . $template[5] . $this->import[$i] . $template[6] . $template[13];
210
+ }
211
+ }
212
+
213
+ if (!empty($this->namespace)) {
214
+ if (($p=strpos($this->namespace,"url("))!==false && substr($this->namespace, -1, 1) === ')') {
215
+ $this->namespace = substr_replace($this->namespace,'"',$p,4);
216
+ $this->namespace = substr($this->namespace, 0, -1) . '"';
217
+ $this->parser->log('Optimised @namespace : Removed "url("', 'Information');
218
+ }
219
+ $output .= $template[0] . '@namespace ' . $template[5] . $this->namespace . $template[6] . $template[13];
220
+ }
221
+
222
+ $in_at_out = '';
223
+ $out = & $output;
224
+
225
+ foreach ($this->tokens as $key => $token) {
226
+ switch ($token[0]) {
227
+ case AT_START:
228
+ $out .= $template[0] . $this->_htmlsp($token[1], $plain) . $template[1];
229
+ $out = & $in_at_out;
230
+ break;
231
+
232
+ case SEL_START:
233
+ if ($this->parser->get_cfg('lowercase_s'))
234
+ $token[1] = strtolower($token[1]);
235
+ $out .= ( $token[1]{0} !== '@') ? $template[2] . $this->_htmlsp($token[1], $plain) : $template[0] . $this->_htmlsp($token[1], $plain);
236
+ $out .= $template[3];
237
+ break;
238
+
239
+ case PROPERTY:
240
+ if ($this->parser->get_cfg('case_properties') === 2) {
241
+ $token[1] = strtoupper($token[1]);
242
+ } elseif ($this->parser->get_cfg('case_properties') === 1) {
243
+ $token[1] = strtolower($token[1]);
244
+ }
245
+ $out .= $template[4] . $this->_htmlsp($token[1], $plain) . ':' . $template[5];
246
+ break;
247
+
248
+ case VALUE:
249
+ $out .= $this->_htmlsp($token[1], $plain);
250
+ if ($this->_seeknocomment($key, 1) == SEL_END && $this->parser->get_cfg('remove_last_;')) {
251
+ $out .= str_replace(';', '', $template[6]);
252
+ } else {
253
+ $out .= $template[6];
254
+ }
255
+ break;
256
+
257
+ case SEL_END:
258
+ $out .= $template[7];
259
+ if ($this->_seeknocomment($key, 1) != AT_END)
260
+ $out .= $template[8];
261
+ break;
262
+
263
+ case AT_END:
264
+ $out = & $output;
265
+ $in_at_out = str_replace("\n\n", "\r\n", $in_at_out); // don't fill empty lines
266
+ $in_at_out = str_replace("\n", "\n" . $template[10], $in_at_out);
267
+ $in_at_out = str_replace("\r\n", "\n\n", $in_at_out);
268
+ $out .= $template[10] . $in_at_out . $template[9];
269
+ $in_at_out = '';
270
+ break;
271
+
272
+ case COMMENT:
273
+ $out .= $template[11] . '/*' . $this->_htmlsp($token[1], $plain) . '*/' . $template[12];
274
+ break;
275
+ }
276
+ }
277
+
278
+ $output = trim($output);
279
+
280
+ if (!$plain) {
281
+ $this->output_css = $output;
282
+ $this->_print(true);
283
+ } else {
284
+ // If using spaces in the template, don't want these to appear in the plain output
285
+ $this->output_css_plain = str_replace('&#160;', '', $output);
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Gets the next token type which is $move away from $key, excluding comments
291
+ * @param integer $key current position
292
+ * @param integer $move move this far
293
+ * @return mixed a token type
294
+ * @access private
295
+ * @version 1.0
296
+ */
297
+ public function _seeknocomment($key, $move) {
298
+ $go = ($move > 0) ? 1 : -1;
299
+ for ($i = $key + 1; abs($key - $i) - 1 < abs($move); $i += $go) {
300
+ if (!isset($this->tokens[$i])) {
301
+ return;
302
+ }
303
+ if ($this->tokens[$i][0] == COMMENT) {
304
+ $move += 1;
305
+ continue;
306
+ }
307
+ return $this->tokens[$i][0];
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Converts $this->css array to a raw array ($this->tokens)
313
+ * @param string $default_media default @media to add to selectors without any @media
314
+ * @access private
315
+ * @version 1.0
316
+ */
317
+ public function _convert_raw_css($default_media='') {
318
+ $this->tokens = array();
319
+ $sort_selectors = $this->parser->get_cfg('sort_selectors');
320
+ $sort_properties = $this->parser->get_cfg('sort_properties');
321
+
322
+ foreach ($this->css as $medium => $val) {
323
+ if ($sort_selectors)
324
+ ksort($val);
325
+ if (intval($medium) < DEFAULT_AT) {
326
+ // un medium vide (contenant @font-face ou autre @) ne produit aucun conteneur
327
+ if (strlen(trim($medium))) {
328
+ $this->parser->_add_token(AT_START, $medium, true);
329
+ }
330
+ } elseif ($default_media) {
331
+ $this->parser->_add_token(AT_START, $default_media, true);
332
+ }
333
+
334
+ foreach ($val as $selector => $vali) {
335
+ if ($sort_properties)
336
+ ksort($vali);
337
+ $this->parser->_add_token(SEL_START, $selector, true);
338
+
339
+ $invalid = array(
340
+ '*' => array(), // IE7 hacks first
341
+ '_' => array(), // IE6 hacks
342
+ '/' => array(), // IE6 hacks
343
+ '-' => array() // IE6 hacks
344
+ );
345
+ foreach ($vali as $property => $valj) {
346
+ if (strncmp($property,"//",2)!==0) {
347
+ $matches = array();
348
+ if ($sort_properties && preg_match('/^(\*|_|\/|-)(?!(ms|moz|o\b|xv|atsc|wap|khtml|webkit|ah|hp|ro|rim|tc)-)/', $property, $matches)) {
349
+ $invalid[$matches[1]][$property] = $valj;
350
+ } else {
351
+ $this->parser->_add_token(PROPERTY, $property, true);
352
+ $this->parser->_add_token(VALUE, $valj, true);
353
+ }
354
+ }
355
+ }
356
+ foreach ($invalid as $prefix => $props) {
357
+ foreach ($props as $property => $valj) {
358
+ $this->parser->_add_token(PROPERTY, $property, true);
359
+ $this->parser->_add_token(VALUE, $valj, true);
360
+ }
361
+ }
362
+ $this->parser->_add_token(SEL_END, $selector, true);
363
+ }
364
+
365
+ if (intval($medium) < DEFAULT_AT) {
366
+ // un medium vide (contenant @font-face ou autre @) ne produit aucun conteneur
367
+ if (strlen(trim($medium))) {
368
+ $this->parser->_add_token(AT_END, $medium, true);
369
+ }
370
+ } elseif ($default_media) {
371
+ $this->parser->_add_token(AT_END, $default_media, true);
372
+ }
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Same as htmlspecialchars, only that chars are not replaced if $plain !== true. This makes print_code() cleaner.
378
+ * @param string $string
379
+ * @param bool $plain
380
+ * @return string
381
+ * @see csstidy_print::_print()
382
+ * @access private
383
+ * @version 1.0
384
+ */
385
+ public function _htmlsp($string, $plain) {
386
+ if (!$plain) {
387
+ return htmlspecialchars($string, ENT_QUOTES, 'utf-8');
388
+ }
389
+ return $string;
390
+ }
391
+
392
+ /**
393
+ * Get compression ratio
394
+ * @access public
395
+ * @return float
396
+ * @version 1.2
397
+ */
398
+ public function get_ratio() {
399
+ if (!$this->output_css_plain) {
400
+ $this->formatted();
401
+ }
402
+ return round((strlen($this->input_css) - strlen($this->output_css_plain)) / strlen($this->input_css), 3) * 100;
403
+ }
404
+
405
+ /**
406
+ * Get difference between the old and new code in bytes and prints the code if necessary.
407
+ * @access public
408
+ * @return string
409
+ * @version 1.1
410
+ */
411
+ public function get_diff() {
412
+ if (!$this->output_css_plain) {
413
+ $this->formatted();
414
+ }
415
+
416
+ $diff = strlen($this->output_css_plain) - strlen($this->input_css);
417
+
418
+ if ($diff > 0) {
419
+ return '+' . $diff;
420
+ } elseif ($diff == 0) {
421
+ return '+-' . $diff;
422
+ }
423
+
424
+ return $diff;
425
+ }
426
+
427
+ /**
428
+ * Get the size of either input or output CSS in KB
429
+ * @param string $loc default is "output"
430
+ * @access public
431
+ * @return integer
432
+ * @version 1.0
433
+ */
434
+ public function size($loc = 'output') {
435
+ if ($loc === 'output' && !$this->output_css) {
436
+ $this->formatted();
437
+ }
438
+
439
+ if ($loc === 'input') {
440
+ return (strlen($this->input_css) / 1000);
441
+ } else {
442
+ return (strlen($this->output_css_plain) / 1000);
443
+ }
444
+ }
445
+
446
+ }
libs/CSSTidy/data.inc.php ADDED
@@ -0,0 +1,646 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Various CSS Data for CSSTidy
4
+ *
5
+ * This file is part of CSSTidy.
6
+ *
7
+ * CSSTidy is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * CSSTidy is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with CSSTidy; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ *
21
+ * @license http://opensource.org/licenses/gpl-license.php GNU Public License
22
+ * @package csstidy
23
+ * @author Florian Schmitz (floele at gmail dot com) 2005
24
+ * @author Nikolay Matsievsky (speed at webo dot name) 2010
25
+ */
26
+
27
+ /**
28
+ * All whitespace allowed in CSS
29
+ *
30
+ * @global array $data['csstidy']['whitespace']
31
+ * @version 1.0
32
+ */
33
+ $data['csstidy']['whitespace'] = array(' ',"\n","\t","\r","\x0B");
34
+
35
+ /**
36
+ * All CSS tokens used by csstidy
37
+ *
38
+ * @global string $data['csstidy']['tokens']
39
+ * @version 1.0
40
+ */
41
+ $data['csstidy']['tokens'] = '/@}{;:=\'"(,\\!$%&)*+.<>?[]^`|~';
42
+
43
+ /**
44
+ * All CSS units (CSS 3 units included)
45
+ *
46
+ * @see compress_numbers()
47
+ * @global array $data['csstidy']['units']
48
+ * @version 1.0
49
+ */
50
+ $data['csstidy']['units'] = array('in','cm','mm','pt','pc','px','rem','em','%','ex','gd','vw','vh','vm','deg','grad','rad','turn','ms','s','khz','hz','ch','vmin','vmax','dpi','dpcm','dppx');
51
+
52
+ /**
53
+ * Available at-rules
54
+ *
55
+ * @global array $data['csstidy']['at_rules']
56
+ * @version 1.1
57
+ */
58
+ $data['csstidy']['at_rules'] = array('page' => 'is','font-face' => 'atis','charset' => 'iv', 'import' => 'iv','namespace' => 'iv','media' => 'at','keyframes' => 'at','-moz-keyframes' => 'at','-o-keyframes' => 'at','-webkit-keyframes' => 'at','-ms-keyframes' => 'at');
59
+
60
+ /**
61
+ * Properties that need a value with unit
62
+ *
63
+ * @todo CSS3 properties
64
+ * @see compress_numbers();
65
+ * @global array $data['csstidy']['unit_values']
66
+ * @version 1.2
67
+ */
68
+ $data['csstidy']['unit_values'] = array ('background', 'background-position', 'background-size', 'border', 'border-top', 'border-right', 'border-bottom', 'border-left', 'border-width',
69
+ 'border-top-width', 'border-right-width', 'border-left-width', 'border-bottom-width', 'bottom', 'border-spacing', 'column-gap', 'column-width',
70
+ 'font-size', 'height', 'left', 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'max-height',
71
+ 'max-width', 'min-height', 'min-width', 'outline', 'outline-width', 'padding', 'padding-top', 'padding-right',
72
+ 'padding-bottom', 'padding-left', 'perspective', 'right', 'top', 'text-indent', 'letter-spacing', 'word-spacing', 'width');
73
+
74
+ /**
75
+ * Properties that allow <color> as value
76
+ *
77
+ * @todo CSS3 properties
78
+ * @see compress_numbers();
79
+ * @global array $data['csstidy']['color_values']
80
+ * @version 1.0
81
+ */
82
+ $data['csstidy']['color_values'] = array();
83
+ $data['csstidy']['color_values'][] = 'background-color';
84
+ $data['csstidy']['color_values'][] = 'border-color';
85
+ $data['csstidy']['color_values'][] = 'border-top-color';
86
+ $data['csstidy']['color_values'][] = 'border-right-color';
87
+ $data['csstidy']['color_values'][] = 'border-bottom-color';
88
+ $data['csstidy']['color_values'][] = 'border-left-color';
89
+ $data['csstidy']['color_values'][] = 'color';
90
+ $data['csstidy']['color_values'][] = 'outline-color';
91
+ $data['csstidy']['color_values'][] = 'column-rule-color';
92
+
93
+ /**
94
+ * Default values for the background properties
95
+ *
96
+ * @todo Possibly property names will change during CSS3 development
97
+ * @global array $data['csstidy']['background_prop_default']
98
+ * @see dissolve_short_bg()
99
+ * @see merge_bg()
100
+ * @version 1.0
101
+ */
102
+ $data['csstidy']['background_prop_default'] = array();
103
+ $data['csstidy']['background_prop_default']['background-image'] = 'none';
104
+ $data['csstidy']['background_prop_default']['background-size'] = 'auto';
105
+ $data['csstidy']['background_prop_default']['background-repeat'] = 'repeat';
106
+ $data['csstidy']['background_prop_default']['background-position'] = '0 0';
107
+ $data['csstidy']['background_prop_default']['background-attachment'] = 'scroll';
108
+ $data['csstidy']['background_prop_default']['background-clip'] = 'border';
109
+ $data['csstidy']['background_prop_default']['background-origin'] = 'padding';
110
+ $data['csstidy']['background_prop_default']['background-color'] = 'transparent';
111
+
112
+ /**
113
+ * Default values for the font properties
114
+ *
115
+ * @global array $data['csstidy']['font_prop_default']
116
+ * @see merge_fonts()
117
+ * @version 1.3
118
+ */
119
+ $data['csstidy']['font_prop_default'] = array();
120
+ $data['csstidy']['font_prop_default']['font-style'] = 'normal';
121
+ $data['csstidy']['font_prop_default']['font-variant'] = 'normal';
122
+ $data['csstidy']['font_prop_default']['font-weight'] = 'normal';
123
+ $data['csstidy']['font_prop_default']['font-size'] = '';
124
+ $data['csstidy']['font_prop_default']['line-height'] = '';
125
+ $data['csstidy']['font_prop_default']['font-family'] = '';
126
+
127
+ /**
128
+ * A list of non-W3C color names which get replaced by their hex-codes
129
+ *
130
+ * @global array $data['csstidy']['replace_colors']
131
+ * @see cut_color()
132
+ * @version 1.0
133
+ */
134
+ $data['csstidy']['replace_colors'] = array();
135
+ $data['csstidy']['replace_colors']['aliceblue'] = '#f0f8ff';
136
+ $data['csstidy']['replace_colors']['antiquewhite'] = '#faebd7';
137
+ $data['csstidy']['replace_colors']['aquamarine'] = '#7fffd4';
138
+ $data['csstidy']['replace_colors']['azure'] = '#f0ffff';
139
+ $data['csstidy']['replace_colors']['beige'] = '#f5f5dc';
140
+ $data['csstidy']['replace_colors']['bisque'] = '#ffe4c4';
141
+ $data['csstidy']['replace_colors']['blanchedalmond'] = '#ffebcd';
142
+ $data['csstidy']['replace_colors']['blueviolet'] = '#8a2be2';
143
+ $data['csstidy']['replace_colors']['brown'] = '#a52a2a';
144
+ $data['csstidy']['replace_colors']['burlywood'] = '#deb887';
145
+ $data['csstidy']['replace_colors']['cadetblue'] = '#5f9ea0';
146
+ $data['csstidy']['replace_colors']['chartreuse'] = '#7fff00';
147
+ $data['csstidy']['replace_colors']['chocolate'] = '#d2691e';
148
+ $data['csstidy']['replace_colors']['coral'] = '#ff7f50';
149
+ $data['csstidy']['replace_colors']['cornflowerblue'] = '#6495ed';
150
+ $data['csstidy']['replace_colors']['cornsilk'] = '#fff8dc';
151
+ $data['csstidy']['replace_colors']['crimson'] = '#dc143c';
152
+ $data['csstidy']['replace_colors']['cyan'] = '#00ffff';
153
+ $data['csstidy']['replace_colors']['darkblue'] = '#00008b';
154
+ $data['csstidy']['replace_colors']['darkcyan'] = '#008b8b';
155
+ $data['csstidy']['replace_colors']['darkgoldenrod'] = '#b8860b';
156
+ $data['csstidy']['replace_colors']['darkgray'] = '#a9a9a9';
157
+ $data['csstidy']['replace_colors']['darkgreen'] = '#006400';
158
+ $data['csstidy']['replace_colors']['darkkhaki'] = '#bdb76b';
159
+ $data['csstidy']['replace_colors']['darkmagenta'] = '#8b008b';
160
+ $data['csstidy']['replace_colors']['darkolivegreen'] = '#556b2f';
161
+ $data['csstidy']['replace_colors']['darkorange'] = '#ff8c00';
162
+ $data['csstidy']['replace_colors']['darkorchid'] = '#9932cc';
163
+ $data['csstidy']['replace_colors']['darkred'] = '#8b0000';
164
+ $data['csstidy']['replace_colors']['darksalmon'] = '#e9967a';
165
+ $data['csstidy']['replace_colors']['darkseagreen'] = '#8fbc8f';
166
+ $data['csstidy']['replace_colors']['darkslateblue'] = '#483d8b';
167
+ $data['csstidy']['replace_colors']['darkslategray'] = '#2f4f4f';
168
+ $data['csstidy']['replace_colors']['darkturquoise'] = '#00ced1';
169
+ $data['csstidy']['replace_colors']['darkviolet'] = '#9400d3';
170
+ $data['csstidy']['replace_colors']['deeppink'] = '#ff1493';
171
+ $data['csstidy']['replace_colors']['deepskyblue'] = '#00bfff';
172
+ $data['csstidy']['replace_colors']['dimgray'] = '#696969';
173
+ $data['csstidy']['replace_colors']['dodgerblue'] = '#1e90ff';
174
+ $data['csstidy']['replace_colors']['feldspar'] = '#d19275';
175
+ $data['csstidy']['replace_colors']['firebrick'] = '#b22222';
176
+ $data['csstidy']['replace_colors']['floralwhite'] = '#fffaf0';
177
+ $data['csstidy']['replace_colors']['forestgreen'] = '#228b22';
178
+ $data['csstidy']['replace_colors']['gainsboro'] = '#dcdcdc';
179
+ $data['csstidy']['replace_colors']['ghostwhite'] = '#f8f8ff';
180
+ $data['csstidy']['replace_colors']['gold'] = '#ffd700';
181
+ $data['csstidy']['replace_colors']['goldenrod'] = '#daa520';
182
+ $data['csstidy']['replace_colors']['greenyellow'] = '#adff2f';
183
+ $data['csstidy']['replace_colors']['honeydew'] = '#f0fff0';
184
+ $data['csstidy']['replace_colors']['hotpink'] = '#ff69b4';
185
+ $data['csstidy']['replace_colors']['indianred'] = '#cd5c5c';
186
+ $data['csstidy']['replace_colors']['indigo'] = '#4b0082';
187
+ $data['csstidy']['replace_colors']['ivory'] = '#fffff0';
188
+ $data['csstidy']['replace_colors']['khaki'] = '#f0e68c';
189
+ $data['csstidy']['replace_colors']['lavender'] = '#e6e6fa';
190
+ $data['csstidy']['replace_colors']['lavenderblush'] = '#fff0f5';
191
+ $data['csstidy']['replace_colors']['lawngreen'] = '#7cfc00';
192
+ $data['csstidy']['replace_colors']['lemonchiffon'] = '#fffacd';
193
+ $data['csstidy']['replace_colors']['lightblue'] = '#add8e6';
194
+ $data['csstidy']['replace_colors']['lightcoral'] = '#f08080';
195
+ $data['csstidy']['replace_colors']['lightcyan'] = '#e0ffff';
196
+ $data['csstidy']['replace_colors']['lightgoldenrodyellow'] = '#fafad2';
197
+ $data['csstidy']['replace_colors']['lightgrey'] = '#d3d3d3';
198
+ $data['csstidy']['replace_colors']['lightgreen'] = '#90ee90';
199
+ $data['csstidy']['replace_colors']['lightpink'] = '#ffb6c1';
200
+ $data['csstidy']['replace_colors']['lightsalmon'] = '#ffa07a';
201
+ $data['csstidy']['replace_colors']['lightseagreen'] = '#20b2aa';
202
+ $data['csstidy']['replace_colors']['lightskyblue'] = '#87cefa';
203
+ $data['csstidy']['replace_colors']['lightslateblue'] = '#8470ff';
204
+ $data['csstidy']['replace_colors']['lightslategray'] = '#778899';
205
+ $data['csstidy']['replace_colors']['lightsteelblue'] = '#b0c4de';
206
+ $data['csstidy']['replace_colors']['lightyellow'] = '#ffffe0';
207
+ $data['csstidy']['replace_colors']['limegreen'] = '#32cd32';
208
+ $data['csstidy']['replace_colors']['linen'] = '#faf0e6';
209
+ $data['csstidy']['replace_colors']['magenta'] = '#ff00ff';
210
+ $data['csstidy']['replace_colors']['mediumaquamarine'] = '#66cdaa';
211
+ $data['csstidy']['replace_colors']['mediumblue'] = '#0000cd';
212
+ $data['csstidy']['replace_colors']['mediumorchid'] = '#ba55d3';
213
+ $data['csstidy']['replace_colors']['mediumpurple'] = '#9370d8';
214
+ $data['csstidy']['replace_colors']['mediumseagreen'] = '#3cb371';
215
+ $data['csstidy']['replace_colors']['mediumslateblue'] = '#7b68ee';
216
+ $data['csstidy']['replace_colors']['mediumspringgreen'] = '#00fa9a';
217
+ $data['csstidy']['replace_colors']['mediumturquoise'] = '#48d1cc';
218
+ $data['csstidy']['replace_colors']['mediumvioletred'] = '#c71585';
219
+ $data['csstidy']['replace_colors']['midnightblue'] = '#191970';
220
+ $data['csstidy']['replace_colors']['mintcream'] = '#f5fffa';
221
+ $data['csstidy']['replace_colors']['mistyrose'] = '#ffe4e1';
222
+ $data['csstidy']['replace_colors']['moccasin'] = '#ffe4b5';
223
+ $data['csstidy']['replace_colors']['navajowhite'] = '#ffdead';
224
+ $data['csstidy']['replace_colors']['oldlace'] = '#fdf5e6';
225
+ $data['csstidy']['replace_colors']['olivedrab'] = '#6b8e23';
226
+ $data['csstidy']['replace_colors']['orangered'] = '#ff4500';
227
+ $data['csstidy']['replace_colors']['orchid'] = '#da70d6';
228
+ $data['csstidy']['replace_colors']['palegoldenrod'] = '#eee8aa';
229
+ $data['csstidy']['replace_colors']['palegreen'] = '#98fb98';
230
+ $data['csstidy']['replace_colors']['paleturquoise'] = '#afeeee';
231
+ $data['csstidy']['replace_colors']['palevioletred'] = '#d87093';
232
+ $data['csstidy']['replace_colors']['papayawhip'] = '#ffefd5';
233
+ $data['csstidy']['replace_colors']['peachpuff'] = '#ffdab9';
234
+ $data['csstidy']['replace_colors']['peru'] = '#cd853f';
235
+ $data['csstidy']['replace_colors']['pink'] = '#ffc0cb';
236
+ $data['csstidy']['replace_colors']['plum'] = '#dda0dd';
237
+ $data['csstidy']['replace_colors']['powderblue'] = '#b0e0e6';
238
+ $data['csstidy']['replace_colors']['rosybrown'] = '#bc8f8f';
239
+ $data['csstidy']['replace_colors']['royalblue'] = '#4169e1';
240
+ $data['csstidy']['replace_colors']['saddlebrown'] = '#8b4513';
241
+ $data['csstidy']['replace_colors']['salmon'] = '#fa8072';
242
+ $data['csstidy']['replace_colors']['sandybrown'] = '#f4a460';
243
+ $data['csstidy']['replace_colors']['seagreen'] = '#2e8b57';
244
+ $data['csstidy']['replace_colors']['seashell'] = '#fff5ee';
245
+ $data['csstidy']['replace_colors']['sienna'] = '#a0522d';
246
+ $data['csstidy']['replace_colors']['skyblue'] = '#87ceeb';
247
+ $data['csstidy']['replace_colors']['slateblue'] = '#6a5acd';
248
+ $data['csstidy']['replace_colors']['slategray'] = '#708090';
249
+ $data['csstidy']['replace_colors']['snow'] = '#fffafa';
250
+ $data['csstidy']['replace_colors']['springgreen'] = '#00ff7f';
251
+ $data['csstidy']['replace_colors']['steelblue'] = '#4682b4';
252
+ $data['csstidy']['replace_colors']['tan'] = '#d2b48c';
253
+ $data['csstidy']['replace_colors']['thistle'] = '#d8bfd8';
254
+ $data['csstidy']['replace_colors']['tomato'] = '#ff6347';
255
+ $data['csstidy']['replace_colors']['turquoise'] = '#40e0d0';
256
+ $data['csstidy']['replace_colors']['violet'] = '#ee82ee';
257
+ $data['csstidy']['replace_colors']['violetred'] = '#d02090';
258
+ $data['csstidy']['replace_colors']['wheat'] = '#f5deb3';
259
+ $data['csstidy']['replace_colors']['whitesmoke'] = '#f5f5f5';
260
+ $data['csstidy']['replace_colors']['yellowgreen'] = '#9acd32';
261
+
262
+ /**
263
+ * A list of all shorthand properties that are devided into four properties and/or have four subvalues
264
+ *
265
+ * @global array $data['csstidy']['shorthands']
266
+ * @todo Are there new ones in CSS3?
267
+ * @see dissolve_4value_shorthands()
268
+ * @see merge_4value_shorthands()
269
+ * @version 1.0
270
+ */
271
+ $data['csstidy']['shorthands'] = array();
272
+ $data['csstidy']['shorthands']['border-color'] = array('border-top-color','border-right-color','border-bottom-color','border-left-color');
273
+ $data['csstidy']['shorthands']['border-style'] = array('border-top-style','border-right-style','border-bottom-style','border-left-style');
274
+ $data['csstidy']['shorthands']['border-width'] = array('border-top-width','border-right-width','border-bottom-width','border-left-width');
275
+ $data['csstidy']['shorthands']['margin'] = array('margin-top','margin-right','margin-bottom','margin-left');
276
+ $data['csstidy']['shorthands']['padding'] = array('padding-top','padding-right','padding-bottom','padding-left');
277
+ $data['csstidy']['shorthands']['-moz-border-radius'] = 0;
278
+
279
+ /**
280
+ * All CSS Properties. Needed for csstidy::property_is_next()
281
+ *
282
+ * @global array $data['csstidy']['all_properties']
283
+ * @version 1.1
284
+ * @see csstidy::property_is_next()
285
+ */
286
+ $data['csstidy']['all_properties']['alignment-adjust'] = 'CSS3.0';
287
+ $data['csstidy']['all_properties']['alignment-baseline'] = 'CSS3.0';
288
+ $data['csstidy']['all_properties']['animation'] = 'CSS3.0';
289
+ $data['csstidy']['all_properties']['animation-delay'] = 'CSS3.0';
290
+ $data['csstidy']['all_properties']['animation-direction'] = 'CSS3.0';
291
+ $data['csstidy']['all_properties']['animation-duration'] = 'CSS3.0';
292
+ $data['csstidy']['all_properties']['animation-iteration-count'] = 'CSS3.0';
293
+ $data['csstidy']['all_properties']['animation-name'] = 'CSS3.0';
294
+ $data['csstidy']['all_properties']['animation-play-state'] = 'CSS3.0';
295
+ $data['csstidy']['all_properties']['animation-timing-function'] = 'CSS3.0';
296
+ $data['csstidy']['all_properties']['appearance'] = 'CSS3.0';
297
+ $data['csstidy']['all_properties']['azimuth'] = 'CSS2.0,CSS2.1,CSS3.0';
298
+ $data['csstidy']['all_properties']['backface-visibility'] = 'CSS3.0';
299
+ $data['csstidy']['all_properties']['background'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
300
+ $data['csstidy']['all_properties']['background-attachment'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
301
+ $data['csstidy']['all_properties']['background-clip'] = 'CSS3.0';
302
+ $data['csstidy']['all_properties']['background-color'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
303
+ $data['csstidy']['all_properties']['background-image'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
304
+ $data['csstidy']['all_properties']['background-origin'] = 'CSS3.0';
305
+ $data['csstidy']['all_properties']['background-position'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
306
+ $data['csstidy']['all_properties']['background-repeat'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
307
+ $data['csstidy']['all_properties']['background-size'] = 'CSS3.0';
308
+ $data['csstidy']['all_properties']['baseline-shift'] = 'CSS3.0';
309
+ $data['csstidy']['all_properties']['binding'] = 'CSS3.0';
310
+ $data['csstidy']['all_properties']['bleed'] = 'CSS3.0';
311
+ $data['csstidy']['all_properties']['bookmark-label'] = 'CSS3.0';
312
+ $data['csstidy']['all_properties']['bookmark-level'] = 'CSS3.0';
313
+ $data['csstidy']['all_properties']['bookmark-state'] = 'CSS3.0';
314
+ $data['csstidy']['all_properties']['bookmark-target'] = 'CSS3.0';
315
+ $data['csstidy']['all_properties']['border'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
316
+ $data['csstidy']['all_properties']['border-bottom'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
317
+ $data['csstidy']['all_properties']['border-bottom-color'] = 'CSS2.0,CSS2.1,CSS3.0';
318
+ $data['csstidy']['all_properties']['border-bottom-left-radius'] = 'CSS3.0';
319
+ $data['csstidy']['all_properties']['border-bottom-right-radius'] = 'CSS3.0';
320
+ $data['csstidy']['all_properties']['border-bottom-style'] = 'CSS2.0,CSS2.1,CSS3.0';
321
+ $data['csstidy']['all_properties']['border-bottom-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
322
+ $data['csstidy']['all_properties']['border-collapse'] = 'CSS2.0,CSS2.1,CSS3.0';
323
+ $data['csstidy']['all_properties']['border-color'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
324
+ $data['csstidy']['all_properties']['border-image'] = 'CSS3.0';
325
+ $data['csstidy']['all_properties']['border-image-outset'] = 'CSS3.0';
326
+ $data['csstidy']['all_properties']['border-image-repeat'] = 'CSS3.0';
327
+ $data['csstidy']['all_properties']['border-image-slice'] = 'CSS3.0';
328
+ $data['csstidy']['all_properties']['border-image-source'] = 'CSS3.0';
329
+ $data['csstidy']['all_properties']['border-image-width'] = 'CSS3.0';
330
+ $data['csstidy']['all_properties']['border-left'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
331
+ $data['csstidy']['all_properties']['border-left-color'] = 'CSS2.0,CSS2.1,CSS3.0';
332
+ $data['csstidy']['all_properties']['border-left-style'] = 'CSS2.0,CSS2.1,CSS3.0';
333
+ $data['csstidy']['all_properties']['border-left-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
334
+ $data['csstidy']['all_properties']['border-radius'] = 'CSS3.0';
335
+ $data['csstidy']['all_properties']['border-right'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
336
+ $data['csstidy']['all_properties']['border-right-color'] = 'CSS2.0,CSS2.1,CSS3.0';
337
+ $data['csstidy']['all_properties']['border-right-style'] = 'CSS2.0,CSS2.1,CSS3.0';
338
+ $data['csstidy']['all_properties']['border-right-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
339
+ $data['csstidy']['all_properties']['border-spacing'] = 'CSS2.0,CSS2.1,CSS3.0';
340
+ $data['csstidy']['all_properties']['border-style'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
341
+ $data['csstidy']['all_properties']['border-top'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
342
+ $data['csstidy']['all_properties']['border-top-color'] = 'CSS2.0,CSS2.1,CSS3.0';
343
+ $data['csstidy']['all_properties']['border-top-left-radius'] = 'CSS3.0';
344
+ $data['csstidy']['all_properties']['border-top-right-radius'] = 'CSS3.0';
345
+ $data['csstidy']['all_properties']['border-top-style'] = 'CSS2.0,CSS2.1,CSS3.0';
346
+ $data['csstidy']['all_properties']['border-top-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
347
+ $data['csstidy']['all_properties']['border-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
348
+ $data['csstidy']['all_properties']['bottom'] = 'CSS2.0,CSS2.1,CSS3.0';
349
+ $data['csstidy']['all_properties']['box-decoration-break'] = 'CSS3.0';
350
+ $data['csstidy']['all_properties']['box-shadow'] = 'CSS3.0';
351
+ $data['csstidy']['all_properties']['box-sizing'] = 'CSS3.0';
352
+ $data['csstidy']['all_properties']['break-after'] = 'CSS3.0';
353
+ $data['csstidy']['all_properties']['break-before'] = 'CSS3.0';
354
+ $data['csstidy']['all_properties']['break-inside'] = 'CSS3.0';
355
+ $data['csstidy']['all_properties']['caption-side'] = 'CSS2.0,CSS2.1,CSS3.0';
356
+ $data['csstidy']['all_properties']['clear'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
357
+ $data['csstidy']['all_properties']['clip'] = 'CSS2.0,CSS2.1,CSS3.0';
358
+ $data['csstidy']['all_properties']['color'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
359
+ $data['csstidy']['all_properties']['color-profile'] = 'CSS3.0';
360
+ $data['csstidy']['all_properties']['column-count'] = 'CSS3.0';
361
+ $data['csstidy']['all_properties']['column-fill'] = 'CSS3.0';
362
+ $data['csstidy']['all_properties']['column-gap'] = 'CSS3.0';
363
+ $data['csstidy']['all_properties']['column-rule'] = 'CSS3.0';
364
+ $data['csstidy']['all_properties']['column-rule-color'] = 'CSS3.0';
365
+ $data['csstidy']['all_properties']['column-rule-style'] = 'CSS3.0';
366
+ $data['csstidy']['all_properties']['column-rule-width'] = 'CSS3.0';
367
+ $data['csstidy']['all_properties']['column-span'] = 'CSS3.0';
368
+ $data['csstidy']['all_properties']['column-width'] = 'CSS3.0';
369
+ $data['csstidy']['all_properties']['columns'] = 'CSS3.0';
370
+ $data['csstidy']['all_properties']['content'] = 'CSS2.0,CSS2.1,CSS3.0';
371
+ $data['csstidy']['all_properties']['counter-increment'] = 'CSS2.0,CSS2.1,CSS3.0';
372
+ $data['csstidy']['all_properties']['counter-reset'] = 'CSS2.0,CSS2.1,CSS3.0';
373
+ $data['csstidy']['all_properties']['crop'] = 'CSS3.0';
374
+ $data['csstidy']['all_properties']['cue'] = 'CSS2.0,CSS2.1,CSS3.0';
375
+ $data['csstidy']['all_properties']['cue-after'] = 'CSS2.0,CSS2.1,CSS3.0';
376
+ $data['csstidy']['all_properties']['cue-before'] = 'CSS2.0,CSS2.1,CSS3.0';
377
+ $data['csstidy']['all_properties']['cursor'] = 'CSS2.0,CSS2.1,CSS3.0';
378
+ $data['csstidy']['all_properties']['direction'] = 'CSS2.0,CSS2.1,CSS3.0';
379
+ $data['csstidy']['all_properties']['display'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
380
+ $data['csstidy']['all_properties']['dominant-baseline'] = 'CSS3.0';
381
+ $data['csstidy']['all_properties']['drop-initial-after-adjust'] = 'CSS3.0';
382
+ $data['csstidy']['all_properties']['drop-initial-after-align'] = 'CSS3.0';
383
+ $data['csstidy']['all_properties']['drop-initial-before-adjust'] = 'CSS3.0';
384
+ $data['csstidy']['all_properties']['drop-initial-before-align'] = 'CSS3.0';
385
+ $data['csstidy']['all_properties']['drop-initial-size'] = 'CSS3.0';
386
+ $data['csstidy']['all_properties']['drop-initial-value'] = 'CSS3.0';
387
+ $data['csstidy']['all_properties']['elevation'] = 'CSS2.0,CSS2.1,CSS3.0';
388
+ $data['csstidy']['all_properties']['empty-cells'] = 'CSS2.0,CSS2.1,CSS3.0';
389
+ $data['csstidy']['all_properties']['fit'] = 'CSS3.0';
390
+ $data['csstidy']['all_properties']['fit-position'] = 'CSS3.0';
391
+ $data['csstidy']['all_properties']['flex-align'] = 'CSS3.0';
392
+ $data['csstidy']['all_properties']['flex-flow'] = 'CSS3.0';
393
+ $data['csstidy']['all_properties']['flex-line-pack'] = 'CSS3.0';
394
+ $data['csstidy']['all_properties']['flex-order'] = 'CSS3.0';
395
+ $data['csstidy']['all_properties']['flex-pack'] = 'CSS3.0';
396
+ $data['csstidy']['all_properties']['float'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
397
+ $data['csstidy']['all_properties']['float-offset'] = 'CSS3.0';
398
+ $data['csstidy']['all_properties']['font'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
399
+ $data['csstidy']['all_properties']['font-family'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
400
+ $data['csstidy']['all_properties']['font-size'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
401
+ $data['csstidy']['all_properties']['font-size-adjust'] = 'CSS2.0,CSS3.0';
402
+ $data['csstidy']['all_properties']['font-stretch'] = 'CSS2.0,CSS3.0';
403
+ $data['csstidy']['all_properties']['font-style'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
404
+ $data['csstidy']['all_properties']['font-variant'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
405
+ $data['csstidy']['all_properties']['font-weight'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
406
+ $data['csstidy']['all_properties']['grid-columns'] = 'CSS3.0';
407
+ $data['csstidy']['all_properties']['grid-rows'] = 'CSS3.0';
408
+ $data['csstidy']['all_properties']['hanging-punctuation'] = 'CSS3.0';
409
+ $data['csstidy']['all_properties']['height'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
410
+ $data['csstidy']['all_properties']['hyphenate-after'] = 'CSS3.0';
411
+ $data['csstidy']['all_properties']['hyphenate-before'] = 'CSS3.0';
412
+ $data['csstidy']['all_properties']['hyphenate-character'] = 'CSS3.0';
413
+ $data['csstidy']['all_properties']['hyphenate-lines'] = 'CSS3.0';
414
+ $data['csstidy']['all_properties']['hyphenate-resource'] = 'CSS3.0';
415
+ $data['csstidy']['all_properties']['hyphens'] = 'CSS3.0';
416
+ $data['csstidy']['all_properties']['icon'] = 'CSS3.0';
417
+ $data['csstidy']['all_properties']['image-orientation'] = 'CSS3.0';
418
+ $data['csstidy']['all_properties']['image-rendering'] = 'CSS3.0';
419
+ $data['csstidy']['all_properties']['image-resolution'] = 'CSS3.0';
420
+ $data['csstidy']['all_properties']['inline-box-align'] = 'CSS3.0';
421
+ $data['csstidy']['all_properties']['left'] = 'CSS2.0,CSS2.1,CSS3.0';
422
+ $data['csstidy']['all_properties']['letter-spacing'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
423
+ $data['csstidy']['all_properties']['line-break'] = 'CSS3.0';
424
+ $data['csstidy']['all_properties']['line-height'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
425
+ $data['csstidy']['all_properties']['line-stacking'] = 'CSS3.0';
426
+ $data['csstidy']['all_properties']['line-stacking-ruby'] = 'CSS3.0';
427
+ $data['csstidy']['all_properties']['line-stacking-shift'] = 'CSS3.0';
428
+ $data['csstidy']['all_properties']['line-stacking-strategy'] = 'CSS3.0';
429
+ $data['csstidy']['all_properties']['list-style'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
430
+ $data['csstidy']['all_properties']['list-style-image'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
431
+ $data['csstidy']['all_properties']['list-style-position'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
432
+ $data['csstidy']['all_properties']['list-style-type'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
433
+ $data['csstidy']['all_properties']['margin'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
434
+ $data['csstidy']['all_properties']['margin-bottom'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
435
+ $data['csstidy']['all_properties']['margin-left'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
436
+ $data['csstidy']['all_properties']['margin-right'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
437
+ $data['csstidy']['all_properties']['margin-top'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
438
+ $data['csstidy']['all_properties']['marker-offset'] = 'CSS2.0,CSS3.0';
439
+ $data['csstidy']['all_properties']['marks'] = 'CSS2.0,CSS3.0';
440
+ $data['csstidy']['all_properties']['marquee-direction'] = 'CSS3.0';
441
+ $data['csstidy']['all_properties']['marquee-loop'] = 'CSS3.0';
442
+ $data['csstidy']['all_properties']['marquee-play-count'] = 'CSS3.0';
443
+ $data['csstidy']['all_properties']['marquee-speed'] = 'CSS3.0';
444
+ $data['csstidy']['all_properties']['marquee-style'] = 'CSS3.0';
445
+ $data['csstidy']['all_properties']['max-height'] = 'CSS2.0,CSS2.1,CSS3.0';
446
+ $data['csstidy']['all_properties']['max-width'] = 'CSS2.0,CSS2.1,CSS3.0';
447
+ $data['csstidy']['all_properties']['min-height'] = 'CSS2.0,CSS2.1,CSS3.0';
448
+ $data['csstidy']['all_properties']['min-width'] = 'CSS2.0,CSS2.1,CSS3.0';
449
+ $data['csstidy']['all_properties']['move-to'] = 'CSS3.0';
450
+ $data['csstidy']['all_properties']['nav-down'] = 'CSS3.0';
451
+ $data['csstidy']['all_properties']['nav-index'] = 'CSS3.0';
452
+ $data['csstidy']['all_properties']['nav-left'] = 'CSS3.0';
453
+ $data['csstidy']['all_properties']['nav-right'] = 'CSS3.0';
454
+ $data['csstidy']['all_properties']['nav-up'] = 'CSS3.0';
455
+ $data['csstidy']['all_properties']['opacity'] = 'CSS3.0';
456
+ $data['csstidy']['all_properties']['orphans'] = 'CSS2.0,CSS2.1,CSS3.0';
457
+ $data['csstidy']['all_properties']['outline'] = 'CSS2.0,CSS2.1,CSS3.0';
458
+ $data['csstidy']['all_properties']['outline-color'] = 'CSS2.0,CSS2.1,CSS3.0';
459
+ $data['csstidy']['all_properties']['outline-offset'] = 'CSS3.0';
460
+ $data['csstidy']['all_properties']['outline-style'] = 'CSS2.0,CSS2.1,CSS3.0';
461
+ $data['csstidy']['all_properties']['outline-width'] = 'CSS2.0,CSS2.1,CSS3.0';
462
+ $data['csstidy']['all_properties']['overflow'] = 'CSS2.0,CSS2.1,CSS3.0';
463
+ $data['csstidy']['all_properties']['overflow-style'] = 'CSS3.0';
464
+ $data['csstidy']['all_properties']['overflow-wrap'] = 'CSS3.0';
465
+ $data['csstidy']['all_properties']['overflow-x'] = 'CSS3.0';
466
+ $data['csstidy']['all_properties']['overflow-y'] = 'CSS3.0';
467
+ $data['csstidy']['all_properties']['padding'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
468
+ $data['csstidy']['all_properties']['padding-bottom'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
469
+ $data['csstidy']['all_properties']['padding-left'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
470
+ $data['csstidy']['all_properties']['padding-right'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
471
+ $data['csstidy']['all_properties']['padding-top'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
472
+ $data['csstidy']['all_properties']['page'] = 'CSS2.0,CSS3.0';
473
+ $data['csstidy']['all_properties']['page-break-after'] = 'CSS2.0,CSS2.1,CSS3.0';
474
+ $data['csstidy']['all_properties']['page-break-before'] = 'CSS2.0,CSS2.1,CSS3.0';
475
+ $data['csstidy']['all_properties']['page-break-inside'] = 'CSS2.0,CSS2.1,CSS3.0';
476
+ $data['csstidy']['all_properties']['page-policy'] = 'CSS3.0';
477
+ $data['csstidy']['all_properties']['pause'] = 'CSS2.0,CSS2.1,CSS3.0';
478
+ $data['csstidy']['all_properties']['pause-after'] = 'CSS2.0,CSS2.1,CSS3.0';
479
+ $data['csstidy']['all_properties']['pause-before'] = 'CSS2.0,CSS2.1,CSS3.0';
480
+ $data['csstidy']['all_properties']['perspective'] = 'CSS3.0';
481
+ $data['csstidy']['all_properties']['perspective-origin'] = 'CSS3.0';
482
+ $data['csstidy']['all_properties']['phonemes'] = 'CSS3.0';
483
+ $data['csstidy']['all_properties']['pitch'] = 'CSS2.0,CSS2.1,CSS3.0';
484
+ $data['csstidy']['all_properties']['pitch-range'] = 'CSS2.0,CSS2.1,CSS3.0';
485
+ $data['csstidy']['all_properties']['play-during'] = 'CSS2.0,CSS2.1,CSS3.0';
486
+ $data['csstidy']['all_properties']['position'] = 'CSS2.0,CSS2.1,CSS3.0';
487
+ $data['csstidy']['all_properties']['presentation-level'] = 'CSS3.0';
488
+ $data['csstidy']['all_properties']['punctuation-trim'] = 'CSS3.0';
489
+ $data['csstidy']['all_properties']['quotes'] = 'CSS2.0,CSS2.1,CSS3.0';
490
+ $data['csstidy']['all_properties']['rendering-intent'] = 'CSS3.0';
491
+ $data['csstidy']['all_properties']['resize'] = 'CSS3.0';
492
+ $data['csstidy']['all_properties']['rest'] = 'CSS3.0';
493
+ $data['csstidy']['all_properties']['rest-after'] = 'CSS3.0';
494
+ $data['csstidy']['all_properties']['rest-before'] = 'CSS3.0';
495
+ $data['csstidy']['all_properties']['richness'] = 'CSS2.0,CSS2.1,CSS3.0';
496
+ $data['csstidy']['all_properties']['right'] = 'CSS2.0,CSS2.1,CSS3.0';
497
+ $data['csstidy']['all_properties']['rotation'] = 'CSS3.0';
498
+ $data['csstidy']['all_properties']['rotation-point'] = 'CSS3.0';
499
+ $data['csstidy']['all_properties']['ruby-align'] = 'CSS3.0';
500
+ $data['csstidy']['all_properties']['ruby-overhang'] = 'CSS3.0';
501
+ $data['csstidy']['all_properties']['ruby-position'] = 'CSS3.0';
502
+ $data['csstidy']['all_properties']['ruby-span'] = 'CSS3.0';
503
+ $data['csstidy']['all_properties']['size'] = 'CSS2.0,CSS3.0';
504
+ $data['csstidy']['all_properties']['speak'] = 'CSS2.0,CSS2.1,CSS3.0';
505
+ $data['csstidy']['all_properties']['speak-header'] = 'CSS2.0,CSS2.1,CSS3.0';
506
+ $data['csstidy']['all_properties']['speak-numeral'] = 'CSS2.0,CSS2.1,CSS3.0';
507
+ $data['csstidy']['all_properties']['speak-punctuation'] = 'CSS2.0,CSS2.1,CSS3.0';
508
+ $data['csstidy']['all_properties']['speech-rate'] = 'CSS2.0,CSS2.1,CSS3.0';
509
+ $data['csstidy']['all_properties']['src'] = 'CSS3.0';
510
+ $data['csstidy']['all_properties']['stress'] = 'CSS2.0,CSS2.1,CSS3.0';
511
+ $data['csstidy']['all_properties']['string-set'] = 'CSS3.0';
512
+ $data['csstidy']['all_properties']['tab-size'] = 'CSS3.0';
513
+ $data['csstidy']['all_properties']['table-layout'] = 'CSS2.0,CSS2.1,CSS3.0';
514
+ $data['csstidy']['all_properties']['target'] = 'CSS3.0';
515
+ $data['csstidy']['all_properties']['target-name'] = 'CSS3.0';
516
+ $data['csstidy']['all_properties']['target-new'] = 'CSS3.0';
517
+ $data['csstidy']['all_properties']['target-position'] = 'CSS3.0';
518
+ $data['csstidy']['all_properties']['text-align'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
519
+ $data['csstidy']['all_properties']['text-align-last'] = 'CSS3.0';
520
+ $data['csstidy']['all_properties']['text-decoration'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
521
+ $data['csstidy']['all_properties']['text-decoration-color'] = 'CSS3.0';
522
+ $data['csstidy']['all_properties']['text-decoration-line'] = 'CSS3.0';
523
+ $data['csstidy']['all_properties']['text-decoration-skip'] = 'CSS3.0';
524
+ $data['csstidy']['all_properties']['text-decoration-style'] = 'CSS3.0';
525
+ $data['csstidy']['all_properties']['text-emphasis'] = 'CSS3.0';
526
+ $data['csstidy']['all_properties']['text-emphasis-color'] = 'CSS3.0';
527
+ $data['csstidy']['all_properties']['text-emphasis-position'] = 'CSS3.0';
528
+ $data['csstidy']['all_properties']['text-emphasis-style'] = 'CSS3.0';
529
+ $data['csstidy']['all_properties']['text-height'] = 'CSS3.0';
530
+ $data['csstidy']['all_properties']['text-indent'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
531
+ $data['csstidy']['all_properties']['text-justify'] = 'CSS3.0';
532
+ $data['csstidy']['all_properties']['text-outline'] = 'CSS3.0';
533
+ $data['csstidy']['all_properties']['text-shadow'] = 'CSS2.0,CSS3.0';
534
+ $data['csstidy']['all_properties']['text-space-collapse'] = 'CSS3.0';
535
+ $data['csstidy']['all_properties']['text-transform'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
536
+ $data['csstidy']['all_properties']['text-underline-position'] = 'CSS3.0';
537
+ $data['csstidy']['all_properties']['text-wrap'] = 'CSS3.0';
538
+ $data['csstidy']['all_properties']['top'] = 'CSS2.0,CSS2.1,CSS3.0';
539
+ $data['csstidy']['all_properties']['transform'] = 'CSS3.0';
540
+ $data['csstidy']['all_properties']['transform-origin'] = 'CSS3.0';
541
+ $data['csstidy']['all_properties']['transform-style'] = 'CSS3.0';
542
+ $data['csstidy']['all_properties']['transition'] = 'CSS3.0';
543
+ $data['csstidy']['all_properties']['transition-delay'] = 'CSS3.0';
544
+ $data['csstidy']['all_properties']['transition-duration'] = 'CSS3.0';
545
+ $data['csstidy']['all_properties']['transition-property'] = 'CSS3.0';
546
+ $data['csstidy']['all_properties']['transition-timing-function'] = 'CSS3.0';
547
+ $data['csstidy']['all_properties']['unicode-bidi'] = 'CSS2.0,CSS2.1,CSS3.0';
548
+ $data['csstidy']['all_properties']['vertical-align'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
549
+ $data['csstidy']['all_properties']['visibility'] = 'CSS2.0,CSS2.1,CSS3.0';
550
+ $data['csstidy']['all_properties']['voice-balance'] = 'CSS3.0';
551
+ $data['csstidy']['all_properties']['voice-duration'] = 'CSS3.0';
552
+ $data['csstidy']['all_properties']['voice-family'] = 'CSS2.0,CSS2.1,CSS3.0';
553
+ $data['csstidy']['all_properties']['voice-pitch'] = 'CSS3.0';
554
+ $data['csstidy']['all_properties']['voice-pitch-range'] = 'CSS3.0';
555
+ $data['csstidy']['all_properties']['voice-rate'] = 'CSS3.0';
556
+ $data['csstidy']['all_properties']['voice-stress'] = 'CSS3.0';
557
+ $data['csstidy']['all_properties']['voice-volume'] = 'CSS3.0';
558
+ $data['csstidy']['all_properties']['volume'] = 'CSS2.0,CSS2.1,CSS3.0';
559
+ $data['csstidy']['all_properties']['white-space'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
560
+ $data['csstidy']['all_properties']['widows'] = 'CSS2.0,CSS2.1,CSS3.0';
561
+ $data['csstidy']['all_properties']['width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
562
+ $data['csstidy']['all_properties']['word-break'] = 'CSS3.0';
563
+ $data['csstidy']['all_properties']['word-spacing'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
564
+ $data['csstidy']['all_properties']['word-wrap'] = 'CSS3.0';
565
+ $data['csstidy']['all_properties']['z-index'] = 'CSS2.0,CSS2.1,CSS3.0';
566
+
567
+ /**
568
+ * An array containing all properties that can accept a quoted string as a value.
569
+ *
570
+ * @global array $data['csstidy']['quoted_string_properties']
571
+ */
572
+ $data['csstidy']['quoted_string_properties'] = array('content', 'font-family', 'quotes');
573
+
574
+ /**
575
+ * An array containing all properties that can be defined multiple times without being overwritten.
576
+ *
577
+ * @global array $data['csstidy']['quoted_string_properties']
578
+ */
579
+ $data['csstidy']['multiple_properties'] = array('background', 'background-image');
580
+
581
+ /**
582
+ * An array containing all predefined templates.
583
+ *
584
+ * @global array $data['csstidy']['predefined_templates']
585
+ * @version 1.0
586
+ * @see csstidy::load_template()
587
+ */
588
+ $data['csstidy']['predefined_templates']['default'][] = '<span class="at">'; //string before @rule
589
+ $data['csstidy']['predefined_templates']['default'][] = '</span> <span class="format">{</span>'."\n"; //bracket after @-rule
590
+ $data['csstidy']['predefined_templates']['default'][] = '<span class="selector">'; //string before selector
591
+ $data['csstidy']['predefined_templates']['default'][] = '</span> <span class="format">{</span>'."\n"; //bracket after selector
592
+ $data['csstidy']['predefined_templates']['default'][] = '<span class="property">'; //string before property
593
+ $data['csstidy']['predefined_templates']['default'][] = '</span><span class="value">'; //string after property+before value
594
+ $data['csstidy']['predefined_templates']['default'][] = '</span><span class="format">;</span>'."\n"; //string after value
595
+ $data['csstidy']['predefined_templates']['default'][] = '<span class="format">}</span>'; //closing bracket - selector
596
+ $data['csstidy']['predefined_templates']['default'][] = "\n\n"; //space between blocks {...}
597
+ $data['csstidy']['predefined_templates']['default'][] = "\n".'<span class="format">}</span>'. "\n\n"; //closing bracket @-rule
598
+ $data['csstidy']['predefined_templates']['default'][] = ''; //indent in @-rule
599
+ $data['csstidy']['predefined_templates']['default'][] = '<span class="comment">'; // before comment
600
+ $data['csstidy']['predefined_templates']['default'][] = '</span>'."\n"; // after comment
601
+ $data['csstidy']['predefined_templates']['default'][] = "\n"; // after each line @-rule
602
+
603
+ $data['csstidy']['predefined_templates']['high_compression'][] = '<span class="at">';
604
+ $data['csstidy']['predefined_templates']['high_compression'][] = '</span> <span class="format">{</span>'."\n";
605
+ $data['csstidy']['predefined_templates']['high_compression'][] = '<span class="selector">';
606
+ $data['csstidy']['predefined_templates']['high_compression'][] = '</span><span class="format">{</span>';
607
+ $data['csstidy']['predefined_templates']['high_compression'][] = '<span class="property">';
608
+ $data['csstidy']['predefined_templates']['high_compression'][] = '</span><span class="value">';
609
+ $data['csstidy']['predefined_templates']['high_compression'][] = '</span><span class="format">;</span>';
610
+ $data['csstidy']['predefined_templates']['high_compression'][] = '<span class="format">}</span>';
611
+ $data['csstidy']['predefined_templates']['high_compression'][] = "\n";
612
+ $data['csstidy']['predefined_templates']['high_compression'][] = "\n". '<span class="format">}'."\n".'</span>';
613
+ $data['csstidy']['predefined_templates']['high_compression'][] = '';
614
+ $data['csstidy']['predefined_templates']['high_compression'][] = '<span class="comment">'; // before comment
615
+ $data['csstidy']['predefined_templates']['high_compression'][] = '</span>'; // after comment
616
+ $data['csstidy']['predefined_templates']['high_compression'][] = "\n";
617
+
618
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '<span class="at">';
619
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="format">{</span>';
620
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '<span class="selector">';
621
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="format">{</span>';
622
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '<span class="property">';
623
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="value">';
624
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="format">;</span>';
625
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '<span class="format">}</span>';
626
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '';
627
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '<span class="format">}</span>';
628
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '';
629
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '<span class="comment">'; // before comment
630
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '</span>'; // after comment
631
+ $data['csstidy']['predefined_templates']['highest_compression'][] = '';
632
+
633
+ $data['csstidy']['predefined_templates']['low_compression'][] = '<span class="at">';
634
+ $data['csstidy']['predefined_templates']['low_compression'][] = '</span> <span class="format">{</span>'."\n";
635
+ $data['csstidy']['predefined_templates']['low_compression'][] = '<span class="selector">';
636
+ $data['csstidy']['predefined_templates']['low_compression'][] = '</span>'."\n".'<span class="format">{</span>'."\n";
637
+ $data['csstidy']['predefined_templates']['low_compression'][] = ' <span class="property">';
638
+ $data['csstidy']['predefined_templates']['low_compression'][] = '</span><span class="value">';
639
+ $data['csstidy']['predefined_templates']['low_compression'][] = '</span><span class="format">;</span>'."\n";
640
+ $data['csstidy']['predefined_templates']['low_compression'][] = '<span class="format">}</span>';
641
+ $data['csstidy']['predefined_templates']['low_compression'][] = "\n\n";
642
+ $data['csstidy']['predefined_templates']['low_compression'][] = "\n".'<span class="format">}</span>'."\n\n";
643
+ $data['csstidy']['predefined_templates']['low_compression'][] = ' ';
644
+ $data['csstidy']['predefined_templates']['low_compression'][] = '<span class="comment">'; // before comment
645
+ $data['csstidy']['predefined_templates']['low_compression'][] = '</span>'."\n"; // after comment
646
+ $data['csstidy']['predefined_templates']['low_compression'][] = "\n";
libs/CSSTidy/lang.inc.php ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Localization of CSS Optimiser Interface of CSSTidy
5
+ *
6
+ * Copyright 2005, 2006, 2007 Florian Schmitz
7
+ *
8
+ * This file is part of CSSTidy.
9
+ *
10
+ * CSSTidy is free software; you can redistribute it and/or modify
11
+ * it under the terms of the GNU Lesser General Public License as published by
12
+ * the Free Software Foundation; either version 2.1 of the License, or
13
+ * (at your option) any later version.
14
+ *
15
+ * CSSTidy is distributed in the hope that it will be useful,
16
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ * GNU Lesser General Public License for more details.
19
+ *
20
+ * You should have received a copy of the GNU Lesser General Public License
21
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
22
+ *
23
+ * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
24
+ * @package csstidy
25
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
26
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
27
+ */
28
+
29
+
30
+ if (isset($_GET['lang'])) {
31
+ $l = $_GET['lang'];
32
+ } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
33
+ $l = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
34
+ $l = strtolower(substr($l, 0, 2));
35
+ } else {
36
+ $l = '';
37
+ }
38
+
39
+ $l = (in_array($l, array('de', 'fr', 'zh'))) ? $l : 'en';
40
+
41
+ // note 5 in all but French, and 40 in all are orphaned
42
+
43
+ $lang = array();
44
+ $lang['en'][0] = 'CSS Formatter and Optimiser/Optimizer (based on CSSTidy ';
45
+ $lang['en'][1] = 'CSS Formatter and Optimiser';
46
+ $lang['en'][2] = '(based on';
47
+ $lang['en'][3] = '(plaintext)';
48
+ $lang['en'][4] = 'Important Note:';
49
+ $lang['en'][6] = 'Your code should be well-formed. This is <strong>not a validator</strong> which points out errors in your CSS code. To make sure that your code is valid, use the <a href="http://jigsaw.w3.org/css-validator/">W3C Validator</a>.';
50
+ $lang['en'][7] = 'all comments are removed';
51
+ $lang['en'][8] = 'CSS Input:';
52
+ $lang['en'][9] = 'CSS-Code:';
53
+ $lang['en'][10] = 'CSS from URL:';
54
+ $lang['en'][11] = 'Code Layout:';
55
+ $lang['en'][12] = 'Compression&#160;(code&#160;layout):';
56
+ $lang['en'][13] = 'Highest (no readability, smallest size)';
57
+ $lang['en'][14] = 'High (moderate readability, smaller size)';
58
+ $lang['en'][15] = 'Standard (balance between readability and size)';
59
+ $lang['en'][16] = 'Low (higher readability)';
60
+ $lang['en'][17] = 'Custom (enter below)';
61
+ $lang['en'][18] = 'Custom <a href="http://csstidy.sourceforge.net/templates.php">template</a>';
62
+ $lang['en'][19] = 'Options';
63
+ $lang['en'][20] = 'Sort Selectors (caution)';
64
+ $lang['en'][21] = 'Sort Properties';
65
+ $lang['en'][22] = 'Regroup selectors';
66
+ $lang['en'][23] = 'Optimise shorthands';
67
+ $lang['en'][24] = 'Compress colors';
68
+ $lang['en'][25] = 'Lowercase selectors';
69
+ $lang['en'][26] = 'Case for properties:';
70
+ $lang['en'][27] = 'Lowercase';
71
+ $lang['en'][28] = 'No or invalid CSS input or wrong URL!';
72
+ $lang['en'][29] = 'Uppercase';
73
+ $lang['en'][30] = 'lowercase elementnames needed for XHTML';
74
+ $lang['en'][31] = 'Remove unnecessary backslashes';
75
+ $lang['en'][32] = 'convert !important-hack';
76
+ $lang['en'][33] = 'Output as file';
77
+ $lang['en'][34] = 'Bigger compression because of smaller newlines (copy &#38; paste doesn\'t work)';
78
+ $lang['en'][35] = 'Process CSS';
79
+ $lang['en'][36] = 'Compression Ratio';
80
+ $lang['en'][37] = 'Input';
81
+ $lang['en'][38] = 'Output';
82
+ $lang['en'][39] = 'Language';
83
+ $lang['en'][41] = 'Attention: This may change the behaviour of your CSS Code!';
84
+ $lang['en'][42] = 'Remove last ;';
85
+ $lang['en'][43] = 'Discard invalid properties';
86
+ $lang['en'][44] = 'Only safe optimisations';
87
+ $lang['en'][45] = 'Compress font-weight';
88
+ $lang['en'][46] = 'Save comments';
89
+ $lang['en'][47] = 'Do not change anything';
90
+ $lang['en'][48] = 'Only seperate selectors (split at ,)';
91
+ $lang['en'][49] = 'Merge selectors with the same properties (fast)';
92
+ $lang['en'][50] = 'Merge selectors intelligently (slow)';
93
+ $lang['en'][51] = 'Preserve CSS';
94
+ $lang['en'][52] = 'Save comments, hacks, etc. Most optimisations can *not* be applied if this is enabled.';
95
+ $lang['en'][53] = 'None';
96
+ $lang['en'][54] = 'Don\'t optimise';
97
+ $lang['en'][55] = 'Safe optimisations';
98
+ $lang['en'][56] = 'All optimisations';
99
+ $lang['en'][57] = 'Add timestamp';
100
+ $lang['en'][58] = 'Copy to clipboard';
101
+ $lang['en'][59] = 'Back to top';
102
+ $lang['en'][60] = 'Your browser doesn\'t support copy to clipboard.';
103
+ $lang['en'][61] = 'For bugs and suggestions feel free to';
104
+ $lang['en'][62] = 'contact me';
105
+ $lang['en'][63] = 'Output CSS code as complete HTML document';
106
+ $lang['en'][64] = 'Code';
107
+ $lang['en'][65] = 'CSS to style CSS output';
108
+ $lang['en'][66] = 'You need to go to about:config in your URL bar, select \'signed.applets.codebase_principal_support\' in the filter field, and set its value to true in order to use this feature; however, be aware that doing so increases security risks.';
109
+
110
+
111
+ $lang['de'][0] = 'CSS Formatierer und Optimierer (basierend auf CSSTidy ';
112
+ $lang['de'][1] = 'CSS Formatierer und Optimierer';
113
+ $lang['de'][2] = '(basierend auf';
114
+ $lang['de'][3] = '(Textversion)';
115
+ $lang['de'][4] = 'Wichtiger Hinweis:';
116
+ $lang['de'][6] = 'Der CSS Code sollte wohlgeformt sein. Der CSS Code wird <strong>nicht auf Gültigkeit überprüft</strong>. Um sicherzugehen dass dein Code valide ist, benutze den <a href="http://jigsaw.w3.org/css-validator/">W3C Validierungsservice</a>.';
117
+ $lang['de'][7] = 'alle Kommentare werden entfernt';
118
+ $lang['de'][8] = 'CSS Eingabe:';
119
+ $lang['de'][9] = 'CSS-Code:';
120
+ $lang['de'][10] = 'CSS von URL:';
121
+ $lang['de'][11] = 'Code Layout:';
122
+ $lang['de'][12] = 'Komprimierung&#160;(Code&#160;Layout):';
123
+ $lang['de'][13] = 'Höchste (keine Lesbarkeit, niedrigste Größe)';
124
+ $lang['de'][14] = 'Hoch (mittelmäßige Lesbarkeit, geringe Größe)';
125
+ $lang['de'][15] = 'Standard (Kompromiss zwischen Lesbarkeit und Größe)';
126
+ $lang['de'][16] = 'Niedrig (höhere Lesbarkeit)';
127
+ $lang['de'][17] = 'Benutzerdefiniert (unten eingeben)';
128
+ $lang['de'][18] = 'Benutzerdefinierte <a href="http://csstidy.sourceforge.net/templates.php">Vorlage</a>';
129
+ $lang['de'][19] = 'Optionen';
130
+ $lang['de'][20] = 'Selektoren sortieren (Vorsicht)';
131
+ $lang['de'][21] = 'Eigenschaften sortieren';
132
+ $lang['de'][22] = 'Selektoren umgruppieren';
133
+ $lang['de'][23] = 'Shorthands optimieren';
134
+ $lang['de'][24] = 'Farben komprimieren';
135
+ $lang['de'][25] = 'Selektoren in Kleinbuchstaben';
136
+ $lang['de'][26] = 'Groß-/Kleinschreibung für Eigenschaften';
137
+ $lang['de'][27] = 'Kleinbuchstaben';
138
+ $lang['de'][28] = 'Keine oder ungültige CSS Eingabe oder falsche URL!';
139
+ $lang['de'][29] = 'Großbuchstaben';
140
+ $lang['de'][30] = 'kleingeschriebene Elementnamen benötigt für XHTML';
141
+ $lang['de'][31] = 'Unnötige Backslashes entfernen';
142
+ $lang['de'][32] = '!important-Hack konvertieren';
143
+ $lang['de'][33] = 'Als Datei ausgeben';
144
+ $lang['de'][34] = 'Größere Komprimierung augrund von kleineren Neuezeile-Zeichen';
145
+ $lang['de'][35] = 'CSS verarbeiten';
146
+ $lang['de'][36] = 'Komprimierungsrate';
147
+ $lang['de'][37] = 'Eingabe';
148
+ $lang['de'][38] = 'Ausgabe';
149
+ $lang['de'][39] = 'Sprache';
150
+ $lang['de'][41] = 'Achtung: Dies könnte das Verhalten ihres CSS-Codes verändern!';
151
+ $lang['de'][42] = 'Letztes ; entfernen';
152
+ $lang['de'][43] = 'Ungültige Eigenschaften entfernen';
153
+ $lang['de'][44] = 'Nur sichere Optimierungen';
154
+ $lang['de'][45] = 'font-weight komprimieren';
155
+ $lang['de'][46] = 'Kommentare beibehalten';
156
+ $lang['de'][47] = 'Nichts ändern';
157
+ $lang['de'][48] = 'Selektoren nur trennen (am Komma)';
158
+ $lang['de'][49] = 'Selektoren mit gleichen Eigenschaften zusammenfassen (schnell)';
159
+ $lang['de'][50] = 'Selektoren intelligent zusammenfassen (langsam!)';
160
+ $lang['de'][51] = 'CSS erhalten';
161
+ $lang['de'][52] = 'Kommentare, Hacks, etc. speichern. Viele Optimierungen sind dann aber nicht mehr möglich.';
162
+ $lang['de'][53] = 'Keine';
163
+ $lang['de'][54] = 'Nicht optimieren';
164
+ $lang['de'][55] = 'Sichere Optimierungen';
165
+ $lang['de'][56] = 'Alle Optimierungen';
166
+ $lang['de'][57] = 'Zeitstempel hinzufügen';
167
+ $lang['de'][58] = 'Copy to clipboard';
168
+ $lang['de'][59] = 'Back to top';
169
+ $lang['de'][60] = 'Your browser doesn\'t support copy to clipboard.';
170
+ $lang['de'][61] = 'For bugs and suggestions feel free to';
171
+ $lang['de'][62] = 'contact me';
172
+ $lang['de'][63] = 'Output CSS code as complete HTML document';
173
+ $lang['de'][64] = 'Code';
174
+ $lang['de'][65] = 'CSS to style CSS output';
175
+ $lang['de'][66] = 'You need to go to about:config in your URL bar, select \'signed.applets.codebase_principal_support\' in the filter field, and set its value to true in order to use this feature; however, be aware that doing so increases security risks.';
176
+
177
+
178
+ $lang['fr'][0] = 'CSS Formateur et Optimiseur (basé sur CSSTidy ';
179
+ $lang['fr'][1] = 'CSS Formateur et Optimiseur';
180
+ $lang['fr'][2] = '(basé sur ';
181
+ $lang['fr'][3] = '(Version texte)';
182
+ $lang['fr'][4] = 'Note Importante&#160;:';
183
+ $lang['fr'][6] = 'Votre code doit être valide. Ce n’est <strong>pas un validateur</strong> qui signale les erreurs dans votre code CSS. Pour être sûr que votre code est correct, utilisez le validateur&#160;: <a href="http://jigsaw.w3.org/css-validator/">W3C Validator</a>.';
184
+ $lang['fr'][7] = 'tous les commentaires sont enlevés';
185
+ $lang['fr'][8] = 'Champ CSS&#160;:';
186
+ $lang['fr'][9] = 'Code CSS&#160;:';
187
+ $lang['fr'][10] = 'CSS en provenance d’une URL&#160;:<br />';
188
+ $lang['fr'][11] = 'Mise en page du code&#160;:';
189
+ $lang['fr'][12] = 'Compression (mise en page du code)&#160;:';
190
+ $lang['fr'][13] = 'La plus élevée (aucune lisibilité, taille minimale)';
191
+ $lang['fr'][14] = 'Élevée (lisibilité modérée, petite taille)';
192
+ $lang['fr'][15] = 'Normale (équilibre entre lisibilité et taille)';
193
+ $lang['fr'][16] = 'Faible (lisibilité élevée)';
194
+ $lang['fr'][17] = 'Sur mesure (entrer ci-dessous)';
195
+ $lang['fr'][18] = '<a href="http://csstidy.sourceforge.net/templates.php">Gabarit</a> sur mesure';
196
+ $lang['fr'][19] = 'Options';
197
+ $lang['fr'][20] = 'Trier les sélecteurs (attention)';
198
+ $lang['fr'][21] = 'Trier les propriétés';
199
+ $lang['fr'][22] = 'Regrouper les sélecteurs';
200
+ $lang['fr'][23] = 'Propriétés raccourcies';
201
+ $lang['fr'][24] = 'Compresser les couleurs';
202
+ $lang['fr'][25] = 'Sélecteurs en minuscules';
203
+ $lang['fr'][26] = 'Case pour les propriétés&#160;:';
204
+ $lang['fr'][27] = 'Minuscule';
205
+ $lang['fr'][28] = 'CSS non valide ou URL incorrecte&#160;!';
206
+ $lang['fr'][29] = 'Majuscule';
207
+ $lang['fr'][30] = 'les noms des éléments en minuscules (indispensables pour XHTML)';
208
+ $lang['fr'][31] = 'enlever les antislashs inutiles';
209
+ $lang['fr'][32] = 'convertir !important-hack';
210
+ $lang['fr'][33] = 'Sauver en tant que fichier';
211
+ $lang['fr'][34] = 'Meilleure compression grâce aux caractères de saut de ligne plus petits (copier &#38; coller ne marche pas)';
212
+ $lang['fr'][35] = 'Compresser la CSS';
213
+ $lang['fr'][36] = 'Facteur de Compression';
214
+ $lang['fr'][37] = 'Entrée';
215
+ $lang['fr'][38] = 'Sortie';
216
+ $lang['fr'][39] = 'Langue';
217
+ $lang['fr'][41] = 'Attention&#160;: ceci peut changer le comportement de votre code CSS&#160;!';
218
+ $lang['fr'][42] = 'Enlever le dernier ;';
219
+ $lang['fr'][43] = 'Supprimer les propriétés non valide';
220
+ $lang['fr'][44] = 'Seulement les optimisations sûres';
221
+ $lang['fr'][45] = 'Compresser font-weight';
222
+ $lang['fr'][46] = 'Sauvegarder les commentaires ';
223
+ $lang['fr'][47] = 'Ne rien changer';
224
+ $lang['fr'][48] = 'Sépare les sélecteurs (sépare au niveau de ,)';
225
+ $lang['fr'][49] = 'Fusionne les sélecteurs avec les mêmes propriétés (rapide)';
226
+ $lang['fr'][50] = 'Fusionne les sélecteurs intelligemment (lent)';
227
+ $lang['fr'][51] = 'Préserver la CSS';
228
+ $lang['fr'][52] = 'Sauvegarder les commentaires, hacks, etc. La plupart des optimisations ne peuvent *pas* être appliquées si cela est activé.';
229
+ $lang['fr'][53] = 'Aucun';
230
+ $lang['fr'][54] = 'Ne pas optimiser';
231
+ $lang['fr'][55] = 'Optimisations sûres';
232
+ $lang['fr'][56] = 'Toutes les optimisations';
233
+ $lang['fr'][57] = 'Ajouter un timestamp';
234
+ $lang['fr'][58] = 'Copier dans le presse-papiers';
235
+ $lang['fr'][59] = 'Retour en haut';
236
+ $lang['fr'][60] = 'Votre navigateur ne supporte pas la copie vers le presse-papiers.';
237
+ $lang['fr'][61] = 'Pour signaler des bugs ou pour des suggestions,';
238
+ $lang['fr'][62] = 'contactez-moi';
239
+ $lang['fr'][63] = 'Sauver le code CSS comme document complet HTML';
240
+ $lang['fr'][64] = 'Code';
241
+ $lang['fr'][65] = 'CSS pour colorier la sortie CSS';
242
+ $lang['fr'][66] = 'Vous devez aller dans about:config dans votre barre d’adresse, sélectionner \'signed.applets.codebase_principal_support\' dans le champ Filtre et attribuez-lui la valeur \'true\' pour utiliser cette fonctionnalité; toutefois, soyez conscient que cela augmente les risques de sécurité.';
243
+
244
+
245
+ $lang['zh'][0] = 'CSS整形與最佳化工具(使用 CSSTidy ';
246
+ $lang['zh'][1] = 'CSS整形與最佳化工具';
247
+ $lang['zh'][2] = '(使用';
248
+ $lang['zh'][3] = '(純文字)';
249
+ $lang['zh'][4] = '重要事項:';
250
+ $lang['zh'][6] = '你的原始碼必須是良構的(well-formed). 這個工具<strong>沒有內建驗證器(validator)</strong>. 驗證器能夠指出你CSS原始碼裡的錯誤. 請使用 <a href="http://jigsaw.w3.org/css-validator/">W3C 驗證器</a>, 確保你的原始碼合乎規範.';
251
+ $lang['zh'][7] = '所有註解都移除了';
252
+ $lang['zh'][8] = 'CSS 輸入:';
253
+ $lang['zh'][9] = 'CSS 原始碼:';
254
+ $lang['zh'][10] = 'CSS 檔案網址(URL):';
255
+ $lang['zh'][11] = '原始碼規劃:';
256
+ $lang['zh'][12] = '壓縮程度(原始碼規劃):';
257
+ $lang['zh'][13] = '最高 (沒有辦法讀, 檔案最小)';
258
+ $lang['zh'][14] = '高 (適度的可讀性, 檔案小)';
259
+ $lang['zh'][15] = '標準 (兼顧可讀性與檔案大小)';
260
+ $lang['zh'][16] = '低 (注重可讀性)';
261
+ $lang['zh'][17] = '自訂 (在下方設定)';
262
+ $lang['zh'][18] = '自訂<a href="http://csstidy.sourceforge.net/templates.php">樣板</a>';
263
+ $lang['zh'][19] = '選項';
264
+ $lang['zh'][20] = '整理選擇符(請謹慎使用)';
265
+ $lang['zh'][21] = '整理屬性';
266
+ $lang['zh'][22] = '重組選擇符';
267
+ $lang['zh'][23] = '速記法(shorthand)最佳化';
268
+ $lang['zh'][24] = '壓縮色彩語法';
269
+ $lang['zh'][25] = '改用小寫選擇符';
270
+ $lang['zh'][26] = '屬性的字形:';
271
+ $lang['zh'][27] = '小寫';
272
+ $lang['zh'][28] = '沒有輸入CSS, 語法不符合規定, 或是網址錯誤!';
273
+ $lang['zh'][29] = '大寫';
274
+ $lang['zh'][30] = 'XHTML必須使用小寫的元素名稱';
275
+ $lang['zh'][31] = '移除不必要的反斜線';
276
+ $lang['zh'][32] = '轉換 !important-hack';
277
+ $lang['zh'][33] = '輸出成檔案形式';
278
+ $lang['zh'][34] = '由於比較少換行字元, 會有更大的壓縮比率(複製&#38;貼上沒有用)';
279
+ $lang['zh'][35] = '執行';
280
+ $lang['zh'][36] = '壓縮比率';
281
+ $lang['zh'][37] = '輸入';
282
+ $lang['zh'][38] = '輸出';
283
+ $lang['zh'][39] = '語言';
284
+ $lang['zh'][41] = '注意: 這或許會變更你CSS原始碼的行為!';
285
+ $lang['zh'][42] = '除去最後一個分號';
286
+ $lang['zh'][43] = '拋棄不符合規定的屬性';
287
+ $lang['zh'][44] = '只安全地最佳化';
288
+ $lang['zh'][45] = '壓縮 font-weight';
289
+ $lang['zh'][46] = '保留註解';
290
+ $lang['zh'][47] = '什麼都不要改';
291
+ $lang['zh'][48] = '只分開原本用逗號分隔的選擇符';
292
+ $lang['zh'][49] = '合併有相同屬性的選擇符(快速)';
293
+ $lang['zh'][50] = '聰明地合併選擇符(慢速)';
294
+ $lang['zh'][51] = '保護CSS';
295
+ $lang['zh'][52] = '保留註解與 hack 等等. 如果啟用這個選項, 大多數的最佳化程序都不會執行.';
296
+ $lang['zh'][53] = '不改變';
297
+ $lang['zh'][54] = '不做最佳化';
298
+ $lang['zh'][55] = '安全地最佳化';
299
+ $lang['zh'][56] = '全部最佳化';
300
+ $lang['zh'][57] = '加上時間戳記';
301
+ $lang['zh'][58] = '复制到剪贴板';
302
+ $lang['zh'][59] = '回到页面上方';
303
+ $lang['zh'][60] = '你的浏览器不支持复制到剪贴板。';
304
+ $lang['zh'][61] = '如果程序有错误或你有建议,欢迎';
305
+ $lang['zh'][62] = '和我联系';
306
+ $lang['zh'][63] = 'Output CSS code as complete HTML document';
307
+ $lang['zh'][64] = '代码';
308
+ $lang['zh'][65] = 'CSS to style CSS output';
309
+ $lang['zh'][66] = 'You need to go to about:config in your URL bar, select \'signed.applets.codebase_principal_support\' in the filter field, and set its value to true in order to use this feature; however, be aware that doing so increases security risks.';
libs/JSMin/jsmin.php ADDED
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * jsmin.php - PHP implementation of Douglas Crockford's JSMin.
4
+ *
5
+ * This is pretty much a direct port of jsmin.c to PHP with just a few
6
+ * PHP-specific performance tweaks. Also, whereas jsmin.c reads from stdin and
7
+ * outputs to stdout, this library accepts a string as input and returns another
8
+ * string as output.
9
+ *
10
+ * PHP 5 or higher is required.
11
+ *
12
+ * Permission is hereby granted to use this version of the library under the
13
+ * same terms as jsmin.c, which has the following license:
14
+ *
15
+ * --
16
+ * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
17
+ *
18
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
19
+ * this software and associated documentation files (the "Software"), to deal in
20
+ * the Software without restriction, including without limitation the rights to
21
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
22
+ * of the Software, and to permit persons to whom the Software is furnished to do
23
+ * so, subject to the following conditions:
24
+ *
25
+ * The above copyright notice and this permission notice shall be included in all
26
+ * copies or substantial portions of the Software.
27
+ *
28
+ * The Software shall be used for Good, not Evil.
29
+ *
30
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36
+ * SOFTWARE.
37
+ * --
38
+ *
39
+ * @package JSMin
40
+ * @author Ryan Grove <ryan@wonko.com>
41
+ * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
42
+ * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
43
+ * @copyright 2012 Adam Goforth <aag@adamgoforth.com> (Updates)
44
+ * @license http://opensource.org/licenses/mit-license.php MIT License
45
+ * @version 1.1.2 (2012-05-01)
46
+ * @link https://github.com/rgrove/jsmin-php
47
+ */
48
+
49
+ class JSMin {
50
+ const ORD_LF = 10;
51
+ const ORD_SPACE = 32;
52
+ const ACTION_KEEP_A = 1;
53
+ const ACTION_DELETE_A = 2;
54
+ const ACTION_DELETE_A_B = 3;
55
+
56
+ protected $a = '';
57
+ protected $b = '';
58
+ protected $input = '';
59
+ protected $inputIndex = 0;
60
+ protected $inputLength = 0;
61
+ protected $lookAhead = null;
62
+ protected $output = '';
63
+
64
+ // -- Public Static Methods --------------------------------------------------
65
+
66
+ /**
67
+ * Minify Javascript
68
+ *
69
+ * @uses __construct()
70
+ * @uses min()
71
+ * @param string $js Javascript to be minified
72
+ * @return string
73
+ */
74
+ public static function minify($js) {
75
+ $jsmin = new JSMin($js);
76
+ return $jsmin->min();
77
+ }
78
+
79
+ // -- Public Instance Methods ------------------------------------------------
80
+
81
+ /**
82
+ * Constructor
83
+ *
84
+ * @param string $input Javascript to be minified
85
+ */
86
+ public function __construct($input) {
87
+ $this->input = str_replace("\r\n", "\n", $input);
88
+ $this->inputLength = strlen($this->input);
89
+ }
90
+
91
+ // -- Protected Instance Methods ---------------------------------------------
92
+
93
+ /**
94
+ * Action -- do something! What to do is determined by the $command argument.
95
+ *
96
+ * action treats a string as a single character. Wow!
97
+ * action recognizes a regular expression if it is preceded by ( or , or =.
98
+ *
99
+ * @uses next()
100
+ * @uses get()
101
+ * @throws JSMinException If parser errors are found:
102
+ * - Unterminated string literal
103
+ * - Unterminated regular expression set in regex literal
104
+ * - Unterminated regular expression literal
105
+ * @param int $command One of class constants:
106
+ * ACTION_KEEP_A Output A. Copy B to A. Get the next B.
107
+ * ACTION_DELETE_A Copy B to A. Get the next B. (Delete A).
108
+ * ACTION_DELETE_A_B Get the next B. (Delete B).
109
+ */
110
+ protected function action($command) {
111
+ switch($command) {
112
+ case self::ACTION_KEEP_A:
113
+ $this->output .= $this->a;
114
+
115
+ case self::ACTION_DELETE_A:
116
+ $this->a = $this->b;
117
+
118
+ if ($this->a === "'" || $this->a === '"') {
119
+ for (;;) {
120
+ $this->output .= $this->a;
121
+ $this->a = $this->get();
122
+
123
+ if ($this->a === $this->b) {
124
+ break;
125
+ }
126
+
127
+ if (ord($this->a) <= self::ORD_LF) {
128
+ throw new JSMinException('Unterminated string literal.');
129
+ }
130
+
131
+ if ($this->a === '\\') {
132
+ $this->output .= $this->a;
133
+ $this->a = $this->get();
134
+ }
135
+ }
136
+ }
137
+
138
+ case self::ACTION_DELETE_A_B:
139
+ $this->b = $this->next();
140
+
141
+ if ($this->b === '/' && (
142
+ $this->a === '(' || $this->a === ',' || $this->a === '=' ||
143
+ $this->a === ':' || $this->a === '[' || $this->a === '!' ||
144
+ $this->a === '&' || $this->a === '|' || $this->a === '?' ||
145
+ $this->a === '{' || $this->a === '}' || $this->a === ';' ||
146
+ $this->a === "\n" )) {
147
+
148
+ $this->output .= $this->a . $this->b;
149
+
150
+ for (;;) {
151
+ $this->a = $this->get();
152
+
153
+ if ($this->a === '[') {
154
+ /*
155
+ inside a regex [...] set, which MAY contain a '/' itself. Example: mootools Form.Validator near line 460:
156
+ return Form.Validator.getValidator('IsEmpty').test(element) || (/^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]\.?){0,63}[a-z0-9!#$%&'*+/=?^_`{|}~-]@(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\])$/i).test(element.get('value'));
157
+ */
158
+ for (;;) {
159
+ $this->output .= $this->a;
160
+ $this->a = $this->get();
161
+
162
+ if ($this->a === ']') {
163
+ break;
164
+ } elseif ($this->a === '\\') {
165
+ $this->output .= $this->a;
166
+ $this->a = $this->get();
167
+ } elseif (ord($this->a) <= self::ORD_LF) {
168
+ throw new JSMinException('Unterminated regular expression set in regex literal.');
169
+ }
170
+ }
171
+ } elseif ($this->a === '/') {
172
+ break;
173
+ } elseif ($this->a === '\\') {
174
+ $this->output .= $this->a;
175
+ $this->a = $this->get();
176
+ } elseif (ord($this->a) <= self::ORD_LF) {
177
+ throw new JSMinException('Unterminated regular expression literal.');
178
+ }
179
+
180
+ $this->output .= $this->a;
181
+ }
182
+
183
+ $this->b = $this->next();
184
+ }
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Get next char. Convert ctrl char to space.
190
+ *
191
+ * @return string|null
192
+ */
193
+ protected function get() {
194
+ $c = $this->lookAhead;
195
+ $this->lookAhead = null;
196
+
197
+ if ($c === null) {
198
+ if ($this->inputIndex < $this->inputLength) {
199
+ $c = substr($this->input, $this->inputIndex, 1);
200
+ $this->inputIndex += 1;
201
+ } else {
202
+ $c = null;
203
+ }
204
+ }
205
+
206
+ if ($c === "\r") {
207
+ return "\n";
208
+ }
209
+
210
+ if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
211
+ return $c;
212
+ }
213
+
214
+ return ' ';
215
+ }
216
+
217
+ /**
218
+ * Is $c a letter, digit, underscore, dollar sign, or non-ASCII character.
219
+ *
220
+ * @return bool
221
+ */
222
+ protected function isAlphaNum($c) {
223
+ return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
224
+ }
225
+
226
+ /**
227
+ * Perform minification, return result
228
+ *
229
+ * @uses action()
230
+ * @uses isAlphaNum()
231
+ * @uses get()
232
+ * @uses peek()
233
+ * @return string
234
+ */
235
+ protected function min() {
236
+ if (0 == strncmp($this->peek(), "\xef", 1)) {
237
+ $this->get();
238
+ $this->get();
239
+ $this->get();
240
+ }
241
+
242
+ $this->a = "\n";
243
+ $this->action(self::ACTION_DELETE_A_B);
244
+
245
+ while ($this->a !== null) {
246
+ switch ($this->a) {
247
+ case ' ':
248
+ if ($this->isAlphaNum($this->b)) {
249
+ $this->action(self::ACTION_KEEP_A);
250
+ } else {
251
+ $this->action(self::ACTION_DELETE_A);
252
+ }
253
+ break;
254
+
255
+ case "\n":
256
+ switch ($this->b) {
257
+ case '{':
258
+ case '[':
259
+ case '(':
260
+ case '+':
261
+ case '-':
262
+ case '!':
263
+ case '~':
264
+ $this->action(self::ACTION_KEEP_A);
265
+ break;
266
+
267
+ case ' ':
268
+ $this->action(self::ACTION_DELETE_A_B);
269
+ break;
270
+
271
+ default:
272
+ if ($this->isAlphaNum($this->b)) {
273
+ $this->action(self::ACTION_KEEP_A);
274
+ }
275
+ else {
276
+ $this->action(self::ACTION_DELETE_A);
277
+ }
278
+ }
279
+ break;
280
+
281
+ default:
282
+ switch ($this->b) {
283
+ case ' ':
284
+ if ($this->isAlphaNum($this->a)) {
285
+ $this->action(self::ACTION_KEEP_A);
286
+ break;
287
+ }
288
+
289
+ $this->action(self::ACTION_DELETE_A_B);
290
+ break;
291
+
292
+ case "\n":
293
+ switch ($this->a) {
294
+ case '}':
295
+ case ']':
296
+ case ')':
297
+ case '+':
298
+ case '-':
299
+ case '"':
300
+ case "'":
301
+ $this->action(self::ACTION_KEEP_A);
302
+ break;
303
+
304
+ default:
305
+ if ($this->isAlphaNum($this->a)) {
306
+ $this->action(self::ACTION_KEEP_A);
307
+ }
308
+ else {
309
+ $this->action(self::ACTION_DELETE_A_B);
310
+ }
311
+ }
312
+ break;
313
+
314
+ default:
315
+ $this->action(self::ACTION_KEEP_A);
316
+ break;
317
+ }
318
+ }
319
+ }
320
+
321
+ return $this->output;
322
+ }
323
+
324
+ /**
325
+ * Get the next character, skipping over comments. peek() is used to see
326
+ * if a '/' is followed by a '/' or '*'.
327
+ *
328
+ * @uses get()
329
+ * @uses peek()
330
+ * @throws JSMinException On unterminated comment.
331
+ * @return string
332
+ */
333
+ protected function next() {
334
+ $c = $this->get();
335
+
336
+ if ($c === '/') {
337
+ switch($this->peek()) {
338
+ case '/':
339
+ for (;;) {
340
+ $c = $this->get();
341
+
342
+ if (ord($c) <= self::ORD_LF) {
343
+ return $c;
344
+ }
345
+ }
346
+
347
+ case '*':
348
+ $this->get();
349
+
350
+ for (;;) {
351
+ switch($this->get()) {
352
+ case '*':
353
+ if ($this->peek() === '/') {
354
+ $this->get();
355
+ return ' ';
356
+ }
357
+ break;
358
+
359
+ case null:
360
+ throw new JSMinException('Unterminated comment.');
361
+ }
362
+ }
363
+
364
+ default:
365
+ return $c;
366
+ }
367
+ }
368
+
369
+ return $c;
370
+ }
371
+
372
+ /**
373
+ * Get next char. If is ctrl character, translate to a space or newline.
374
+ *
375
+ * @uses get()
376
+ * @return string|null
377
+ */
378
+ protected function peek() {
379
+ $this->lookAhead = $this->get();
380
+ return $this->lookAhead;
381
+ }
382
+ }
383
+
384
+ // -- Exceptions ---------------------------------------------------------------
385
+ class JSMinException extends Exception {}
386
+ ?>
libs/matthiasmullie/index.html DELETED
File without changes
libs/matthiasmullie/minify/bin/index.html DELETED
File without changes
libs/matthiasmullie/minify/bin/minifycss DELETED
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env php
2
- <?php
3
- use MatthiasMullie\Minify;
4
-
5
- // command line utility to minify CSS
6
- if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
- // if composer install
8
- require_once __DIR__ . '/../../../autoload.php';
9
- } else {
10
- require_once __DIR__ . '/../src/Minify.php';
11
- require_once __DIR__ . '/../src/CSS.php';
12
- require_once __DIR__ . '/../src/Exception.php';
13
- }
14
-
15
- error_reporting(E_ALL);
16
- // check PHP setup for cli arguments
17
- if (!isset($_SERVER['argv']) && !isset($argv)) {
18
- fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
- exit(1);
20
- } elseif (!isset($argv)) {
21
- $argv = $_SERVER['argv'];
22
- }
23
- // check if path to file given
24
- if (!isset($argv[1])) {
25
- fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
- exit(1);
27
- }
28
- // check if script run in cli environment
29
- if ('cli' !== php_sapi_name()) {
30
- fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
- exit(1);
32
- }
33
- // check if source file exists
34
- if (!file_exists($argv[1])) {
35
- fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
- exit(1);
37
- }
38
-
39
- try {
40
- $minifier = new Minify\CSS($argv[1]);
41
- echo $minifier->minify();
42
- } catch (Exception $e) {
43
- fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
- exit(1);
45
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/bin/minifyjs DELETED
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env php
2
- <?php
3
- use MatthiasMullie\Minify;
4
-
5
- // command line utility to minify JS
6
- if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
- // if composer install
8
- require_once __DIR__ . '/../../../autoload.php';
9
- } else {
10
- require_once __DIR__ . '/../src/Minify.php';
11
- require_once __DIR__ . '/../src/JS.php';
12
- require_once __DIR__ . '/../src/Exception.php';
13
- }
14
-
15
- error_reporting(E_ALL);
16
- // check PHP setup for cli arguments
17
- if (!isset($_SERVER['argv']) && !isset($argv)) {
18
- fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
- exit(1);
20
- } elseif (!isset($argv)) {
21
- $argv = $_SERVER['argv'];
22
- }
23
- // check if path to file given
24
- if (!isset($argv[1])) {
25
- fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
- exit(1);
27
- }
28
- // check if script run in cli environment
29
- if ('cli' !== php_sapi_name()) {
30
- fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
- exit(1);
32
- }
33
- // check if source file exists
34
- if (!file_exists($argv[1])) {
35
- fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
- exit(1);
37
- }
38
-
39
- try {
40
- $minifier = new Minify\JS($argv[1]);
41
- echo $minifier->minify();
42
- } catch (Exception $e) {
43
- fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
- exit(1);
45
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/data/index.html DELETED
File without changes
libs/matthiasmullie/minify/data/js/index.html DELETED
File without changes
libs/matthiasmullie/minify/data/js/keywords_after.txt DELETED
@@ -1,7 +0,0 @@
1
- in
2
- public
3
- extends
4
- private
5
- protected
6
- implements
7
- instanceof
 
 
 
 
 
 
 
libs/matthiasmullie/minify/data/js/keywords_before.txt DELETED
@@ -1,26 +0,0 @@
1
- do
2
- in
3
- let
4
- new
5
- var
6
- case
7
- else
8
- enum
9
- void
10
- with
11
- class
12
- const
13
- yield
14
- delete
15
- export
16
- import
17
- public
18
- static
19
- typeof
20
- extends
21
- package
22
- private
23
- function
24
- protected
25
- implements
26
- instanceof
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/data/js/keywords_reserved.txt DELETED
@@ -1,63 +0,0 @@
1
- do
2
- if
3
- in
4
- for
5
- let
6
- new
7
- try
8
- var
9
- case
10
- else
11
- enum
12
- eval
13
- null
14
- this
15
- true
16
- void
17
- with
18
- break
19
- catch
20
- class
21
- const
22
- false
23
- super
24
- throw
25
- while
26
- yield
27
- delete
28
- export
29
- import
30
- public
31
- return
32
- static
33
- switch
34
- typeof
35
- default
36
- extends
37
- finally
38
- package
39
- private
40
- continue
41
- debugger
42
- function
43
- arguments
44
- interface
45
- protected
46
- implements
47
- instanceof
48
- abstract
49
- boolean
50
- byte
51
- char
52
- double
53
- final
54
- float
55
- goto
56
- int
57
- long
58
- native
59
- short
60
- synchronized
61
- throws
62
- transient
63
- volatile
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/data/js/operators.txt DELETED
@@ -1,46 +0,0 @@
1
- +
2
- -
3
- *
4
- /
5
- %
6
- =
7
- +=
8
- -=
9
- *=
10
- /=
11
- %=
12
- <<=
13
- >>=
14
- >>>=
15
- &=
16
- ^=
17
- |=
18
- &
19
- |
20
- ^
21
- ~
22
- <<
23
- >>
24
- >>>
25
- ==
26
- ===
27
- !=
28
- !==
29
- >
30
- <
31
- >=
32
- <=
33
- &&
34
- ||
35
- !
36
- .
37
- [
38
- ]
39
- ?
40
- :
41
- ,
42
- ;
43
- (
44
- )
45
- {
46
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/data/js/operators_after.txt DELETED
@@ -1,43 +0,0 @@
1
- +
2
- -
3
- *
4
- /
5
- %
6
- =
7
- +=
8
- -=
9
- *=
10
- /=
11
- %=
12
- <<=
13
- >>=
14
- >>>=
15
- &=
16
- ^=
17
- |=
18
- &
19
- |
20
- ^
21
- <<
22
- >>
23
- >>>
24
- ==
25
- ===
26
- !=
27
- !==
28
- >
29
- <
30
- >=
31
- <=
32
- &&
33
- ||
34
- .
35
- [
36
- ]
37
- ?
38
- :
39
- ,
40
- ;
41
- (
42
- )
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/data/js/operators_before.txt DELETED
@@ -1,43 +0,0 @@
1
- +
2
- -
3
- *
4
- /
5
- %
6
- =
7
- +=
8
- -=
9
- *=
10
- /=
11
- %=
12
- <<=
13
- >>=
14
- >>>=
15
- &=
16
- ^=
17
- |=
18
- &
19
- |
20
- ^
21
- ~
22
- <<
23
- >>
24
- >>>
25
- ==
26
- ===
27
- !=
28
- !==
29
- >
30
- <
31
- >=
32
- <=
33
- &&
34
- ||
35
- !
36
- .
37
- [
38
- ?
39
- :
40
- ,
41
- ;
42
- (
43
- {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/index.html DELETED
File without changes
libs/matthiasmullie/minify/src/CSS.php DELETED
@@ -1,735 +0,0 @@
1
- <?php
2
- /**
3
- * CSS Minifier
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
-
12
- namespace MatthiasMullie\Minify;
13
-
14
- use MatthiasMullie\Minify\Exceptions\FileImportException;
15
- use MatthiasMullie\PathConverter\ConverterInterface;
16
- use 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
27
- * @license MIT License
28
- */
29
- class CSS extends Minify
30
- {
31
- /**
32
- * @var int maximum inport size in kB
33
- */
34
- protected $maxImportSize = 5;
35
-
36
- /**
37
- * @var string[] valid import extensions
38
- */
39
- protected $importExtensions = array(
40
- 'gif' => 'data:image/gif',
41
- 'png' => 'data:image/png',
42
- 'jpe' => 'data:image/jpeg',
43
- 'jpg' => 'data:image/jpeg',
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',
50
- );
51
-
52
- /**
53
- * Set the maximum size if files to be imported.
54
- *
55
- * Files larger than this size (in kB) will not be imported into the CSS.
56
- * Importing files into the CSS as data-uri will save you some connections,
57
- * but we should only import relatively small decorative images so that our
58
- * CSS file doesn't get too bulky.
59
- *
60
- * @param int $size Size in kB
61
- */
62
- public function setMaxImportSize($size)
63
- {
64
- $this->maxImportSize = $size;
65
- }
66
-
67
- /**
68
- * Set the type of extensions to be imported into the CSS (to save network
69
- * connections).
70
- * Keys of the array should be the file extensions & respective values
71
- * should be the data type.
72
- *
73
- * @param string[] $extensions Array of file extensions
74
- */
75
- public function setImportExtensions(array $extensions)
76
- {
77
- $this->importExtensions = $extensions;
78
- }
79
-
80
- /**
81
- * Move any import statements to the top.
82
- *
83
- * @param string $content Nearly finished CSS content
84
- *
85
- * @return string
86
- */
87
- protected function moveImportsToTop($content)
88
- {
89
- if (preg_match_all('/(;?)(@import (?<url>url\()?(?P<quotes>["\']?).+?(?P=quotes)(?(url)\)))/', $content, $matches)) {
90
- // remove from content
91
- foreach ($matches[0] as $import) {
92
- $content = str_replace($import, '', $content);
93
- }
94
-
95
- // add to top
96
- $content = implode(';', $matches[2]).';'.trim($content, ';');
97
- }
98
-
99
- return $content;
100
- }
101
-
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
110
- * @param string[] $parents Parent paths, for circular reference checks
111
- *
112
- * @return string
113
- *
114
- * @throws FileImportException
115
- */
116
- protected function combineImports($source, $content, $parents)
117
- {
118
- $importRegexes = array(
119
- // @import url(xxx)
120
- '/
121
- # import statement
122
- @import
123
-
124
- # whitespace
125
- \s+
126
-
127
- # open url()
128
- url\(
129
-
130
- # (optional) open path enclosure
131
- (?P<quotes>["\']?)
132
-
133
- # fetch path
134
- (?P<path>.+?)
135
-
136
- # (optional) close path enclosure
137
- (?P=quotes)
138
-
139
- # close url()
140
- \)
141
-
142
- # (optional) trailing whitespace
143
- \s*
144
-
145
- # (optional) media statement(s)
146
- (?P<media>[^;]*)
147
-
148
- # (optional) trailing whitespace
149
- \s*
150
-
151
- # (optional) closing semi-colon
152
- ;?
153
-
154
- /ix',
155
-
156
- // @import 'xxx'
157
- '/
158
-
159
- # import statement
160
- @import
161
-
162
- # whitespace
163
- \s+
164
-
165
- # open path enclosure
166
- (?P<quotes>["\'])
167
-
168
- # fetch path
169
- (?P<path>.+?)
170
-
171
- # close path enclosure
172
- (?P=quotes)
173
-
174
- # (optional) trailing whitespace
175
- \s*
176
-
177
- # (optional) media statement(s)
178
- (?P<media>[^;]*)
179
-
180
- # (optional) trailing whitespace
181
- \s*
182
-
183
- # (optional) closing semi-colon
184
- ;?
185
-
186
- /ix',
187
- );
188
-
189
- // find all relative imports in css
190
- $matches = array();
191
- foreach ($importRegexes as $importRegex) {
192
- if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) {
193
- $matches = array_merge($matches, $regexMatches);
194
- }
195
- }
196
-
197
- $search = array();
198
- $replace = array();
199
-
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
207
- if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
208
- continue;
209
- }
210
-
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
- $importContent = $minifier->execute($source, $parents);
221
-
222
- // check if this is only valid for certain media
223
- if (!empty($match['media'])) {
224
- $importContent = '@media '.$match['media'].'{'.$importContent.'}';
225
- }
226
-
227
- // add to replacement array
228
- $search[] = $match[0];
229
- $replace[] = $importContent;
230
- }
231
-
232
- // replace the import statements
233
- return str_replace($search, $replace, $content);
234
- }
235
-
236
- /**
237
- * Import files into the CSS, base64-ized.
238
- *
239
- * @url(image.jpg) images will be loaded and their content merged into the
240
- * original file, to save HTTP requests.
241
- *
242
- * @param string $source The file to import files for
243
- * @param string $content The CSS content to import files for
244
- *
245
- * @return string
246
- */
247
- protected function importFiles($source, $content)
248
- {
249
- $regex = '/url\((["\']?)(.+?)\\1\)/i';
250
- if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
251
- $search = array();
252
- $replace = array();
253
-
254
- // loop the matches
255
- foreach ($matches as $match) {
256
- $extension = substr(strrchr($match[2], '.'), 1);
257
- if ($extension && !array_key_exists($extension, $this->importExtensions)) {
258
- continue;
259
- }
260
-
261
- // get the path for the file that will be imported
262
- $path = $match[2];
263
- $path = dirname($source).'/'.$path;
264
-
265
- // only replace the import with the content if we're able to get
266
- // the content of the file, and it's relatively small
267
- if ($this->canImportFile($path) && $this->canImportBySize($path)) {
268
- // grab content && base64-ize
269
- $importContent = $this->load($path);
270
- $importContent = base64_encode($importContent);
271
-
272
- // build replacement
273
- $search[] = $match[0];
274
- $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
275
- }
276
- }
277
-
278
- // replace the import statements
279
- $content = str_replace($search, $replace, $content);
280
- }
281
-
282
- return $content;
283
- }
284
-
285
- /**
286
- * Minify the data.
287
- * Perform CSS optimizations.
288
- *
289
- * @param string[optional] $path Path to write the data to
290
- * @param string[] $parents Parent paths, for circular reference checks
291
- *
292
- * @return string The minified data
293
- */
294
- public function execute($path = null, $parents = array())
295
- {
296
- $content = '';
297
-
298
- // loop CSS data (raw data and files)
299
- foreach ($this->data as $source => $css) {
300
- /*
301
- * Let's first take out strings & comments, since we can't just
302
- * remove whitespace anywhere. If whitespace occurs inside a string,
303
- * we should leave it alone. E.g.:
304
- * p { content: "a test" }
305
- */
306
- $this->extractStrings();
307
- $this->stripComments();
308
- $css = $this->replace($css);
309
-
310
- $css = $this->stripWhitespace($css);
311
- $css = $this->shortenHex($css);
312
- $css = $this->shortenZeroes($css);
313
- $css = $this->shortenFontWeights($css);
314
- $css = $this->stripEmptyTags($css);
315
-
316
- // restore the string we've extracted earlier
317
- $css = $this->restoreExtractedData($css);
318
-
319
- $source = is_int($source) ? '' : $source;
320
- $parents = $source ? array_merge($parents, array($source)) : $parents;
321
- $css = $this->combineImports($source, $css, $parents);
322
- $css = $this->importFiles($source, $css);
323
-
324
- /*
325
- * If we'll save to a new path, we'll have to fix the relative paths
326
- * to be relative no longer to the source file, but to the new path.
327
- * If we don't write to a file, fall back to same path so no
328
- * conversion happens (because we still want it to go through most
329
- * of the move code, which also addresses url() & @import syntax...)
330
- */
331
- $converter = $this->getPathConverter($source, $path ?: $source);
332
- $css = $this->move($converter, $css);
333
-
334
- // combine css
335
- $content .= $css;
336
- }
337
-
338
- $content = $this->moveImportsToTop($content);
339
-
340
- return $content;
341
- }
342
-
343
- /**
344
- * Moving a css file should update all relative urls.
345
- * Relative references (e.g. ../images/image.gif) in a certain css file,
346
- * will have to be updated when a file is being saved at another location
347
- * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
348
- *
349
- * @param ConverterInterface $converter Relative path converter
350
- * @param string $content The CSS content to update relative urls for
351
- *
352
- * @return string
353
- */
354
- protected function move(ConverterInterface $converter, $content)
355
- {
356
- /*
357
- * Relative path references will usually be enclosed by url(). @import
358
- * is an exception, where url() is not necessary around the path (but is
359
- * allowed).
360
- * This *could* be 1 regular expression, where both regular expressions
361
- * in this array are on different sides of a |. But we're using named
362
- * patterns in both regexes, the same name on both regexes. This is only
363
- * possible with a (?J) modifier, but that only works after a fairly
364
- * recent PCRE version. That's why I'm doing 2 separate regular
365
- * expressions & combining the matches after executing of both.
366
- */
367
- $relativeRegexes = array(
368
- // url(xxx)
369
- '/
370
- # open url()
371
- url\(
372
-
373
- \s*
374
-
375
- # open path enclosure
376
- (?P<quotes>["\'])?
377
-
378
- # fetch path
379
- (?P<path>.+?)
380
-
381
- # close path enclosure
382
- (?(quotes)(?P=quotes))
383
-
384
- \s*
385
-
386
- # close url()
387
- \)
388
-
389
- /ix',
390
-
391
- // @import "xxx"
392
- '/
393
- # import statement
394
- @import
395
-
396
- # whitespace
397
- \s+
398
-
399
- # we don\'t have to check for @import url(), because the
400
- # condition above will already catch these
401
-
402
- # open path enclosure
403
- (?P<quotes>["\'])
404
-
405
- # fetch path
406
- (?P<path>.+?)
407
-
408
- # close path enclosure
409
- (?P=quotes)
410
-
411
- /ix',
412
- );
413
-
414
- // find all relative urls in css
415
- $matches = array();
416
- foreach ($relativeRegexes as $relativeRegex) {
417
- if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
418
- $matches = array_merge($matches, $regexMatches);
419
- }
420
- }
421
-
422
- $search = array();
423
- $replace = array();
424
-
425
- // loop all urls
426
- foreach ($matches as $match) {
427
- // determine if it's a url() or an @import match
428
- $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
429
-
430
- $url = $match['path'];
431
- if ($this->canImportByPath($url)) {
432
- // attempting to interpret GET-params makes no sense, so let's discard them for awhile
433
- $params = strrchr($url, '?');
434
- $url = $params ? substr($url, 0, -strlen($params)) : $url;
435
-
436
- // fix relative url
437
- $url = $converter->convert($url);
438
-
439
- // now that the path has been converted, re-apply GET-params
440
- $url .= $params;
441
- }
442
-
443
- /*
444
- * Urls with control characters above 0x7e should be quoted.
445
- * According to Mozilla's parser, whitespace is only allowed at the
446
- * end of unquoted urls.
447
- * Urls with `)` (as could happen with data: uris) should also be
448
- * quoted to avoid being confused for the url() closing parentheses.
449
- * And urls with a # have also been reported to cause issues.
450
- * Urls with quotes inside should also remain escaped.
451
- *
452
- * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
453
- * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
454
- * @see https://github.com/matthiasmullie/minify/issues/193
455
- */
456
- $url = trim($url);
457
- if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
458
- $url = $match['quotes'] . $url . $match['quotes'];
459
- }
460
-
461
- // build replacement
462
- $search[] = $match[0];
463
- if ($type === 'url') {
464
- $replace[] = 'url('.$url.')';
465
- } elseif ($type === 'import') {
466
- $replace[] = '@import "'.$url.'"';
467
- }
468
- }
469
-
470
- // replace urls
471
- return str_replace($search, $replace, $content);
472
- }
473
-
474
- /**
475
- * Shorthand hex color codes.
476
- * #FF0000 -> #F00.
477
- *
478
- * @param string $content The CSS content to shorten the hex color codes for
479
- *
480
- * @return string
481
- */
482
- protected function shortenHex($content)
483
- {
484
- $content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?=[; }])/i', '#$1$2$3', $content);
485
-
486
- // we can shorten some even more by replacing them with their color name
487
- $colors = array(
488
- '#F0FFFF' => 'azure',
489
- '#F5F5DC' => 'beige',
490
- '#A52A2A' => 'brown',
491
- '#FF7F50' => 'coral',
492
- '#FFD700' => 'gold',
493
- '#808080' => 'gray',
494
- '#008000' => 'green',
495
- '#4B0082' => 'indigo',
496
- '#FFFFF0' => 'ivory',
497
- '#F0E68C' => 'khaki',
498
- '#FAF0E6' => 'linen',
499
- '#800000' => 'maroon',
500
- '#000080' => 'navy',
501
- '#808000' => 'olive',
502
- '#CD853F' => 'peru',
503
- '#FFC0CB' => 'pink',
504
- '#DDA0DD' => 'plum',
505
- '#800080' => 'purple',
506
- '#F00' => 'red',
507
- '#FA8072' => 'salmon',
508
- '#A0522D' => 'sienna',
509
- '#C0C0C0' => 'silver',
510
- '#FFFAFA' => 'snow',
511
- '#D2B48C' => 'tan',
512
- '#FF6347' => 'tomato',
513
- '#EE82EE' => 'violet',
514
- '#F5DEB3' => 'wheat',
515
- );
516
-
517
- return preg_replace_callback(
518
- '/(?<=[: ])('.implode(array_keys($colors), '|').')(?=[; }])/i',
519
- function ($match) use ($colors) {
520
- return $colors[strtoupper($match[0])];
521
- },
522
- $content
523
- );
524
- }
525
-
526
- /**
527
- * Shorten CSS font weights.
528
- *
529
- * @param string $content The CSS content to shorten the font weights for
530
- *
531
- * @return string
532
- */
533
- protected function shortenFontWeights($content)
534
- {
535
- $weights = array(
536
- 'normal' => 400,
537
- 'bold' => 700,
538
- );
539
-
540
- $callback = function ($match) use ($weights) {
541
- return $match[1].$weights[$match[2]];
542
- };
543
-
544
- return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
545
- }
546
-
547
- /**
548
- * Shorthand 0 values to plain 0, instead of e.g. -0em.
549
- *
550
- * @param string $content The CSS content to shorten the zero values for
551
- *
552
- * @return string
553
- */
554
- protected function shortenZeroes($content)
555
- {
556
- // we don't want to strip units in `calc()` expressions:
557
- // `5px - 0px` is valid, but `5px - 0` is not
558
- // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
559
- // `10 * 0` is invalid
560
- // best to just leave `calc()`s alone, even if they could be optimized
561
- // (which is a whole other undertaking, where units & order of
562
- // operations all need to be considered...)
563
- $calcs = $this->findCalcs($content);
564
- $content = str_replace($calcs, array_keys($calcs), $content);
565
-
566
- // reusable bits of code throughout these regexes:
567
- // before & after are used to make sure we don't match lose unintended
568
- // 0-like values (e.g. in #000, or in http://url/1.0)
569
- // units can be stripped from 0 values, or used to recognize non 0
570
- // values (where wa may be able to strip a .0 suffix)
571
- $before = '(?<=[:(, ])';
572
- $after = '(?=[ ,);}])';
573
- $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
574
-
575
- // strip units after zeroes (0px -> 0)
576
- // NOTE: it should be safe to remove all units for a 0 value, but in
577
- // practice, Webkit (especially Safari) seems to stumble over at least
578
- // 0%, potentially other units as well. Only stripping 'px' for now.
579
- // @see https://github.com/matthiasmullie/minify/issues/60
580
- $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
581
-
582
- // strip 0-digits (.0 -> 0)
583
- $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
584
- // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
585
- $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
586
- // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
587
- $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
588
- // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
589
- $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
590
-
591
- // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
592
- $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
593
-
594
- // IE doesn't seem to understand a unitless flex-basis value, so let's
595
- // add it in again (make it `%`, which is only 1 char: 0%, 0px, 0
596
- // anything, it's all just the same)
597
- $content = preg_replace('/flex:([^ ]+ [^ ]+ )0([;\}])/', 'flex:${1}0%${2}', $content);
598
- $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
599
-
600
- // restore `calc()` expressions
601
- $content = str_replace(array_keys($calcs), $calcs, $content);
602
-
603
- return $content;
604
- }
605
-
606
- /**
607
- * Strip empty tags from source code.
608
- *
609
- * @param string $content
610
- *
611
- * @return string
612
- */
613
- protected function stripEmptyTags($content)
614
- {
615
- $content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content);
616
- $content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content);
617
-
618
- return $content;
619
- }
620
-
621
- /**
622
- * Strip comments from source code.
623
- */
624
- protected function stripComments()
625
- {
626
- $this->registerPattern('/\/\*.*?\*\//s', '');
627
- }
628
-
629
- /**
630
- * Strip whitespace.
631
- *
632
- * @param string $content The CSS content to strip the whitespace for
633
- *
634
- * @return string
635
- */
636
- protected function stripWhitespace($content)
637
- {
638
- // remove leading & trailing whitespace
639
- $content = preg_replace('/^\s*/m', '', $content);
640
- $content = preg_replace('/\s*$/m', '', $content);
641
-
642
- // replace newlines with a single space
643
- $content = preg_replace('/\s+/', ' ', $content);
644
-
645
- // remove whitespace around meta characters
646
- // inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
647
- $content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
648
- $content = preg_replace('/([\[(:])\s+/', '$1', $content);
649
- $content = preg_replace('/\s+([\]\)])/', '$1', $content);
650
- $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
651
-
652
- // whitespace around + and - can only be stripped inside some pseudo-
653
- // classes, like `:nth-child(3+2n)`
654
- // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
655
- // selectors like `div.weird- p`
656
- $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
657
- $content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
658
-
659
- // remove semicolon/whitespace followed by closing bracket
660
- $content = str_replace(';}', '}', $content);
661
-
662
- return trim($content);
663
- }
664
-
665
- /**
666
- * Find all `calc()` occurrences.
667
- *
668
- * @param string $content The CSS content to find `calc()`s in.
669
- *
670
- * @return string[]
671
- */
672
- protected function findCalcs($content)
673
- {
674
- $results = array();
675
- preg_match_all('/calc(\(.+?)(?=$|;|calc\()/', $content, $matches, PREG_SET_ORDER);
676
-
677
- foreach ($matches as $match) {
678
- $length = strlen($match[1]);
679
- $expr = '';
680
- $opened = 0;
681
-
682
- for ($i = 0; $i < $length; $i++) {
683
- $char = $match[1][$i];
684
- $expr .= $char;
685
- if ($char === '(') {
686
- $opened++;
687
- } elseif ($char === ')' && --$opened === 0) {
688
- break;
689
- }
690
- }
691
-
692
- $results['calc('.count($results).')'] = 'calc'.$expr;
693
- }
694
-
695
- return $results;
696
- }
697
-
698
- /**
699
- * Check if file is small enough to be imported.
700
- *
701
- * @param string $path The path to the file
702
- *
703
- * @return bool
704
- */
705
- protected function canImportBySize($path)
706
- {
707
- return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
708
- }
709
-
710
- /**
711
- * Check if file a file can be imported, going by the path.
712
- *
713
- * @param string $path
714
- *
715
- * @return bool
716
- */
717
- protected function canImportByPath($path)
718
- {
719
- return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
720
- }
721
-
722
- /**
723
- * Return a converter to update relative paths to be relative to the new
724
- * destination.
725
- *
726
- * @param string $source
727
- * @param string $target
728
- *
729
- * @return ConverterInterface
730
- */
731
- protected function getPathConverter($source, $target)
732
- {
733
- return new Converter($source, $target);
734
- }
735
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/src/Exception.php DELETED
@@ -1,20 +0,0 @@
1
- <?php
2
- /**
3
- * Base Exception
4
- *
5
- * @deprecated Use Exceptions\BasicException instead
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- */
9
- namespace 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
19
- {
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/src/Exceptions/BasicException.php DELETED
@@ -1,23 +0,0 @@
1
- <?php
2
- /**
3
- * Basic exception
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace MatthiasMullie\Minify\Exceptions;
12
-
13
- use 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
22
- {
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/src/Exceptions/FileImportException.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
- /**
3
- * File Import Exception
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace 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
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/src/Exceptions/IOException.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
- /**
3
- * IO Exception
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace 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
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/src/Exceptions/index.html DELETED
File without changes
libs/matthiasmullie/minify/src/JS.php DELETED
@@ -1,577 +0,0 @@
1
- <?php
2
- /**
3
- * JavaScript minifier
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace 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
22
- * @license MIT License
23
- */
24
- class JS extends Minify
25
- {
26
- /**
27
- * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
28
- *
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';
35
-
36
- /**
37
- * Full list of JavaScript reserved words.
38
- * Will be loaded from /data/js/keywords_reserved.txt.
39
- *
40
- * @see https://mathiasbynens.be/notes/reserved-keywords
41
- *
42
- * @var string[]
43
- */
44
- protected $keywordsReserved = array();
45
-
46
- /**
47
- * List of JavaScript reserved words that accept a <variable, value, ...>
48
- * after them. Some end of lines are not the end of a statement, like with
49
- * these keywords.
50
- *
51
- * E.g.: we shouldn't insert a ; after this else
52
- * else
53
- * console.log('this is quite fine')
54
- *
55
- * Will be loaded from /data/js/keywords_before.txt
56
- *
57
- * @var string[]
58
- */
59
- protected $keywordsBefore = array();
60
-
61
- /**
62
- * List of JavaScript reserved words that accept a <variable, value, ...>
63
- * before them. Some end of lines are not the end of a statement, like when
64
- * continued by one of these keywords on the newline.
65
- *
66
- * E.g.: we shouldn't insert a ; before this instanceof
67
- * variable
68
- * instanceof String
69
- *
70
- * Will be loaded from /data/js/keywords_after.txt
71
- *
72
- * @var string[]
73
- */
74
- protected $keywordsAfter = array();
75
-
76
- /**
77
- * List of all JavaScript operators.
78
- *
79
- * Will be loaded from /data/js/operators.txt
80
- *
81
- * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
82
- *
83
- * @var string[]
84
- */
85
- protected $operators = array();
86
-
87
- /**
88
- * List of JavaScript operators that accept a <variable, value, ...> after
89
- * them. Some end of lines are not the end of a statement, like with these
90
- * operators.
91
- *
92
- * Note: Most operators are fine, we've only removed ++ and --.
93
- * ++ & -- have to be joined with the value they're in-/decrementing.
94
- *
95
- * Will be loaded from /data/js/operators_before.txt
96
- *
97
- * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
98
- *
99
- * @var string[]
100
- */
101
- protected $operatorsBefore = array();
102
-
103
- /**
104
- * List of JavaScript operators that accept a <variable, value, ...> before
105
- * them. Some end of lines are not the end of a statement, like when
106
- * continued by one of these operators on the newline.
107
- *
108
- * Note: Most operators are fine, we've only removed ), ], ++, --, ! and ~.
109
- * There can't be a newline separating ! or ~ and whatever it is negating.
110
- * ++ & -- have to be joined with the value they're in-/decrementing.
111
- * ) & ] are "special" in that they have lots or usecases. () for example
112
- * is used for function calls, for grouping, in if () and for (), ...
113
- *
114
- * Will be loaded from /data/js/operators_after.txt
115
- *
116
- * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
117
- *
118
- * @var string[]
119
- */
120
- protected $operatorsAfter = array();
121
-
122
- /**
123
- * {@inheritdoc}
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
- /**
140
- * Minify the data.
141
- * Perform JS optimizations.
142
- *
143
- * @param string[optional] $path Path to write the data to
144
- *
145
- * @return string The minified data
146
- */
147
- public function execute($path = null)
148
- {
149
- $content = '';
150
-
151
- /*
152
- * Let's first take out strings, comments and regular expressions.
153
- * All of these can contain JS code-like characters, and we should make
154
- * sure any further magic ignores anything inside of these.
155
- *
156
- * Consider this example, where we should not strip any whitespace:
157
- * var str = "a test";
158
- *
159
- * Comments will be removed altogether, strings and regular expressions
160
- * will be replaced by placeholder text, which we'll restore later.
161
- */
162
- $this->extractStrings('\'"`');
163
- $this->stripComments();
164
- $this->extractRegex();
165
-
166
- // loop files
167
- foreach ($this->data as $source => $js) {
168
- // take out strings, comments & regex (for which we've registered
169
- // the regexes just a few lines earlier)
170
- $js = $this->replace($js);
171
-
172
- $js = $this->propertyNotation($js);
173
- $js = $this->shortenBools($js);
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
181
- $content = ltrim($content, ';');
182
- $content = substr($content, 0, -1);
183
-
184
- /*
185
- * Earlier, we extracted strings & regular expressions and replaced them
186
- * with placeholder text. This will restore them.
187
- */
188
- $content = $this->restoreExtractedData($content);
189
-
190
- return $content;
191
- }
192
-
193
- /**
194
- * Strip comments from source code.
195
- */
196
- protected function stripComments()
197
- {
198
- // single-line comments
199
- $this->registerPattern('/\/\/.*$/m', '');
200
-
201
- // multi-line comments
202
- $this->registerPattern('/\/\*.*?\*\//s', '');
203
- }
204
-
205
- /**
206
- * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
207
- *
208
- * The content inside the regex can contain characters that may be confused
209
- * for JS code: e.g. it could contain whitespace it needs to match & we
210
- * don't want to strip whitespace in there.
211
- *
212
- * The regex can be pretty simple: we don't have to care about comments,
213
- * (which also use slashes) because stripComments() will have stripped those
214
- * already.
215
- *
216
- * This method will replace all string content with simple REGEX#
217
- * placeholder text, so we've rid all regular expressions from characters
218
- * that may be misinterpreted. Original regex content will be saved in
219
- * $this->extracted and after doing all other minifying, we can restore the
220
- * original content via restoreRegex()
221
- */
222
- protected function extractRegex()
223
- {
224
- // PHP only supports $this inside anonymous functions since 5.4
225
- $minifier = $this;
226
- $callback = function ($match) use ($minifier) {
227
- $count = count($minifier->extracted);
228
- $placeholder = '"'.$count.'"';
229
- $minifier->extracted[$placeholder] = $match[0];
230
-
231
- return $placeholder;
232
- };
233
-
234
- // match all chars except `/` and `\`
235
- // `\` is allowed though, along with whatever char follows (which is the
236
- // one being escaped)
237
- // this should allow all chars, except for an unescaped `/` (= the one
238
- // closing the regex)
239
- $pattern = '\\/([^\\/\\\\]*+|(\\\\.)*+)+\\/[gimy]*';
240
-
241
- // a regular expression can only be followed by a few operators or some
242
- // of the RegExp methods (a `\` followed by a variable or value is
243
- // likely part of a division, not a regex)
244
- $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
245
- $before = '([=:,;\}\(\{\[&\|!\)]|^|'.implode('|', $keywords).')\s*';
246
- $propertiesAndMethods = array(
247
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
248
- 'constructor',
249
- 'flags',
250
- 'global',
251
- 'ignoreCase',
252
- 'multiline',
253
- 'source',
254
- 'sticky',
255
- 'unicode',
256
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
257
- 'compile(',
258
- 'exec(',
259
- 'test(',
260
- 'toSource(',
261
- 'toString(',
262
- );
263
- $delimiters = array_fill(0, count($propertiesAndMethods), '/');
264
- $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
265
- $after = '(?=\s*[\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).'))';
266
- $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
267
-
268
- // 1 more edge case: a regex can be followed by a lot more operators or
269
- // keywords if there's a newline (ASI) in between, where the operator
270
- // actually starts a new statement
271
- // (https://github.com/matthiasmullie/minify/issues/56)
272
- $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
273
- $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
274
- $after = '(?=\s*\n\s*('.implode('|', $operators).'))';
275
- $this->registerPattern('/'.$pattern.$after.'/', $callback);
276
- }
277
-
278
- /**
279
- * Strip whitespace.
280
- *
281
- * We won't strip *all* whitespace, but as much as possible. The thing that
282
- * we'll preserve are newlines we're unsure about.
283
- * JavaScript doesn't require statements to be terminated with a semicolon.
284
- * It will automatically fix missing semicolons with ASI (automatic semi-
285
- * colon insertion) at the end of line causing errors (without semicolon.)
286
- *
287
- * Because it's sometimes hard to tell if a newline is part of a statement
288
- * that should be terminated or not, we'll just leave some of them alone.
289
- *
290
- * @param string $content The content to strip the whitespace for
291
- *
292
- * @return string
293
- */
294
- protected function stripWhitespace($content)
295
- {
296
- // uniform line endings, make them all line feed
297
- $content = str_replace(array("\r\n", "\r"), "\n", $content);
298
-
299
- // collapse all non-line feed whitespace into a single space
300
- $content = preg_replace('/[^\S\n]+/', ' ', $content);
301
-
302
- // strip leading & trailing whitespace
303
- $content = str_replace(array(" \n", "\n "), "\n", $content);
304
-
305
- // collapse consecutive line feeds into just 1
306
- $content = preg_replace('/\n+/', "\n", $content);
307
-
308
- $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
309
- $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
310
- $operators = $this->getOperatorsForRegex($this->operators, '/');
311
- $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
312
- $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
313
-
314
- // strip whitespace that ends in (or next line begin with) an operator
315
- // that allows statements to be broken up over multiple lines
316
- unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
317
- $content = preg_replace(
318
- array(
319
- '/('.implode('|', $operatorsBefore).')\s+/',
320
- '/\s+('.implode('|', $operatorsAfter).')/',
321
- ), '\\1', $content
322
- );
323
-
324
- // make sure + and - can't be mistaken for, or joined into ++ and --
325
- $content = preg_replace(
326
- array(
327
- '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
328
- '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
329
- ), '\\1', $content
330
- );
331
-
332
- // collapse whitespace around reserved words into single space
333
- $content = preg_replace('/(^|[;\}\s])\K('.implode('|', $keywordsBefore).')\s+/', '\\2 ', $content);
334
- $content = preg_replace('/\s+('.implode('|', $keywordsAfter).')(?=([;\{\s]|$))/', ' \\1', $content);
335
-
336
- /*
337
- * We didn't strip whitespace after a couple of operators because they
338
- * could be used in different contexts and we can't be sure it's ok to
339
- * strip the newlines. However, we can safely strip any non-line feed
340
- * whitespace that follows them.
341
- */
342
- $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
343
- $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
344
- $content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
345
- $content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
346
-
347
- /*
348
- * Whitespace after `return` can be omitted in a few occasions
349
- * (such as when followed by a string or regex)
350
- */
351
- $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
352
-
353
- /*
354
- * Get rid of double semicolons, except where they can be used like:
355
- * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
356
- * I'll safeguard these double semicolons inside for-loops by
357
- * temporarily replacing them with an invalid condition: they won't have
358
- * a double semicolon and will be easy to spot to restore afterwards.
359
- */
360
- $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
361
- $content = preg_replace('/;+/', ';', $content);
362
- $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
363
-
364
- /*
365
- * Next, we'll be removing all semicolons where ASI kicks in.
366
- * for-loops however, can have an empty body (ending in only a
367
- * semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
368
- * Here, nothing happens during the loop; it's just used to keep
369
- * increasing `i`. With that ; omitted, the next line would be expected
370
- * to be the for-loop's body... Same goes for while loops.
371
- * I'm going to double that semicolon (if any) so after the next line,
372
- * which strips semicolons here & there, we're still left with this one.
373
- */
374
- $content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
375
- $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
376
- /*
377
- * Below will also keep `;` after a `do{}while();` along with `while();`
378
- * While these could be stripped after do-while, detecting this
379
- * distinction is cumbersome, so I'll play it safe and make sure `;`
380
- * after any kind of `while` is kept.
381
- */
382
- $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
383
-
384
- /*
385
- * We also can't strip empty else-statements. Even though they're
386
- * useless and probably shouldn't be in the code in the first place, we
387
- * shouldn't be stripping the `;` that follows it as it breaks the code.
388
- * We can just remove those useless else-statements completely.
389
- *
390
- * @see https://github.com/matthiasmullie/minify/issues/91
391
- */
392
- $content = preg_replace('/else;/s', '', $content);
393
-
394
- /*
395
- * We also don't really want to terminate statements followed by closing
396
- * curly braces (which we've ignored completely up until now) or end-of-
397
- * script: ASI will kick in here & we're all about minifying.
398
- * Semicolons at beginning of the file don't make any sense either.
399
- */
400
- $content = preg_replace('/;(\}|$)/s', '\\1', $content);
401
- $content = ltrim($content, ';');
402
-
403
- // get rid of remaining whitespace af beginning/end
404
- return trim($content);
405
- }
406
-
407
- /**
408
- * We'll strip whitespace around certain operators with regular expressions.
409
- * This will prepare the given array by escaping all characters.
410
- *
411
- * @param string[] $operators
412
- * @param string $delimiter
413
- *
414
- * @return string[]
415
- */
416
- protected function getOperatorsForRegex(array $operators, $delimiter = '/')
417
- {
418
- // escape operators for use in regex
419
- $delimiters = array_fill(0, count($operators), $delimiter);
420
- $escaped = array_map('preg_quote', $operators, $delimiters);
421
-
422
- $operators = array_combine($operators, $escaped);
423
-
424
- // ignore + & - for now, they'll get special treatment
425
- unset($operators['+'], $operators['-']);
426
-
427
- // dot can not just immediately follow a number; it can be confused for
428
- // decimal point, or calling a method on it, e.g. 42 .toString()
429
- $operators['.'] = '(?<![0-9]\s)\.';
430
-
431
- // don't confuse = with other assignment shortcuts (e.g. +=)
432
- $chars = preg_quote('+-*\=<>%&|', $delimiter);
433
- $operators['='] = '(?<!['.$chars.'])\=';
434
-
435
- return $operators;
436
- }
437
-
438
- /**
439
- * We'll strip whitespace around certain keywords with regular expressions.
440
- * This will prepare the given array by escaping all characters.
441
- *
442
- * @param string[] $keywords
443
- * @param string $delimiter
444
- *
445
- * @return string[]
446
- */
447
- protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
448
- {
449
- // escape keywords for use in regex
450
- $delimiter = array_fill(0, count($keywords), $delimiter);
451
- $escaped = array_map('preg_quote', $keywords, $delimiter);
452
-
453
- // add word boundaries
454
- array_walk($keywords, function ($value) {
455
- return '\b'.$value.'\b';
456
- });
457
-
458
- $keywords = array_combine($keywords, $escaped);
459
-
460
- return $keywords;
461
- }
462
-
463
- /**
464
- * Replaces all occurrences of array['key'] by array.key.
465
- *
466
- * @param string $content
467
- *
468
- * @return string
469
- */
470
- protected function propertyNotation($content)
471
- {
472
- // PHP only supports $this inside anonymous functions since 5.4
473
- $minifier = $this;
474
- $keywords = $this->keywordsReserved;
475
- $callback = function ($match) use ($minifier, $keywords) {
476
- $property = trim($minifier->extracted[$match[1]], '\'"');
477
-
478
- /*
479
- * Check if the property is a reserved keyword. In this context (as
480
- * property of an object literal/array) it shouldn't matter, but IE8
481
- * freaks out with "Expected identifier".
482
- */
483
- if (in_array($property, $keywords)) {
484
- return $match[0];
485
- }
486
-
487
- /*
488
- * See if the property is in a variable-like format (e.g.
489
- * array['key-here'] can't be replaced by array.key-here since '-'
490
- * is not a valid character there.
491
- */
492
- if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
493
- return $match[0];
494
- }
495
-
496
- return '.'.$property;
497
- };
498
-
499
- /*
500
- * Figure out if previous character is a variable name (of the array
501
- * we want to use property notation on) - this is to make sure
502
- * standalone ['value'] arrays aren't confused for keys-of-an-array.
503
- * We can (and only have to) check the last character, because PHP's
504
- * regex implementation doesn't allow unfixed-length look-behind
505
- * assertions.
506
- */
507
- preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
508
- $previousChar = $previousChar[1];
509
-
510
- /*
511
- * Make sure word preceding the ['value'] is not a keyword, e.g.
512
- * return['x']. Because -again- PHP's regex implementation doesn't allow
513
- * unfixed-length look-behind assertions, I'm just going to do a lot of
514
- * separate look-behind assertions, one for each keyword.
515
- */
516
- $keywords = $this->getKeywordsForRegex($keywords);
517
- $keywords = '(?<!'.implode(')(?<!', $keywords).')';
518
-
519
- return preg_replace_callback('/(?<='.$previousChar.'|\])'.$keywords.'\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
520
- }
521
-
522
- /**
523
- * Replaces true & false by !0 and !1.
524
- *
525
- * @param string $content
526
- *
527
- * @return string
528
- */
529
- protected function shortenBools($content)
530
- {
531
- /*
532
- * 'true' or 'false' could be used as property names (which may be
533
- * followed by whitespace) - we must not replace those!
534
- * Since PHP doesn't allow variable-length (to account for the
535
- * whitespace) lookbehind assertions, I need to capture the leading
536
- * character and check if it's a `.`
537
- */
538
- $callback = function ($match) {
539
- if (trim($match[1]) === '.') {
540
- return $match[0];
541
- }
542
-
543
- return $match[1].($match[2] === 'true' ? '!0' : '!1');
544
- };
545
- $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
546
-
547
- // for(;;) is exactly the same as while(true), but shorter :)
548
- $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
549
-
550
- // now make sure we didn't turn any do ... while(true) into do ... for(;;)
551
- preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
552
-
553
- // go backward to make sure positional offsets aren't altered when $content changes
554
- $dos = array_reverse($dos);
555
- foreach ($dos as $do) {
556
- $offsetDo = $do[0][1];
557
-
558
- // find all `while` (now `for`) following `do`: one of those must be
559
- // associated with the `do` and be turned back into `while`
560
- preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
561
- foreach ($whiles as $while) {
562
- $offsetWhile = $while[0][1];
563
-
564
- $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
565
- $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
566
- if ($open === $close) {
567
- // only restore `while` if amount of `{` and `}` are the same;
568
- // otherwise, that `for` isn't associated with this `do`
569
- $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
570
- break;
571
- }
572
- }
573
- }
574
-
575
- return $content;
576
- }
577
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/src/Minify.php DELETED
@@ -1,444 +0,0 @@
1
- <?php
2
- /**
3
- * Abstract minifier class
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace MatthiasMullie\Minify;
12
-
13
- use MatthiasMullie\Minify\Exceptions\IOException;
14
- use Psr\Cache\CacheItemInterface;
15
-
16
- /**
17
- * Abstract minifier class.
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
25
- */
26
- abstract class Minify
27
- {
28
- /**
29
- * The data to be minified.
30
- *
31
- * @var string[]
32
- */
33
- protected $data = array();
34
-
35
- /**
36
- * Array of patterns to match.
37
- *
38
- * @var string[]
39
- */
40
- protected $patterns = array();
41
-
42
- /**
43
- * This array will hold content of strings and regular expressions that have
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();
50
-
51
- /**
52
- * Init the minify class - optionally, code may be passed along already.
53
- */
54
- public function __construct(/* $data = null, ... */)
55
- {
56
- // it's possible to add the source through the constructor as well ;)
57
- if (func_num_args()) {
58
- call_user_func_array(array($this, 'add'), func_get_args());
59
- }
60
- }
61
-
62
- /**
63
- * Add a file or straight-up code to be minified.
64
- *
65
- * @param string|string[] $data
66
- *
67
- * @return static
68
- */
69
- public function add($data /* $data = null, ... */)
70
- {
71
- // bogus "usage" of parameter $data: scrutinizer warns this variable is
72
- // not used (we're using func_get_args instead to support overloading),
73
- // but it still needs to be defined because it makes no sense to have
74
- // this function without argument :)
75
- $args = array($data) + func_get_args();
76
-
77
- // this method can be overloaded
78
- foreach ($args as $data) {
79
- if (is_array($data)) {
80
- call_user_func_array(array($this, 'add'), $data);
81
- continue;
82
- }
83
-
84
- // redefine var
85
- $data = (string) $data;
86
-
87
- // load data
88
- $value = $this->load($data);
89
- $key = ($data != $value) ? $data : count($this->data);
90
-
91
- // replace CR linefeeds etc.
92
- // @see https://github.com/matthiasmullie/minify/pull/139
93
- $value = str_replace(array("\r\n", "\r"), "\n", $value);
94
-
95
- // store data
96
- $this->data[$key] = $value;
97
- }
98
-
99
- return $this;
100
- }
101
-
102
- /**
103
- * Minify the data & (optionally) saves it to a file.
104
- *
105
- * @param string[optional] $path Path to write the data to
106
- *
107
- * @return string The minified data
108
- */
109
- public function minify($path = null)
110
- {
111
- $content = $this->execute($path);
112
-
113
- // save to path
114
- if ($path !== null) {
115
- $this->save($content, $path);
116
- }
117
-
118
- return $content;
119
- }
120
-
121
- /**
122
- * Minify & gzip the data & (optionally) saves it to a file.
123
- *
124
- * @param string[optional] $path Path to write the data to
125
- * @param int[optional] $level Compression level, from 0 to 9
126
- *
127
- * @return string The minified & gzipped data
128
- */
129
- public function gzip($path = null, $level = 9)
130
- {
131
- $content = $this->execute($path);
132
- $content = gzencode($content, $level, FORCE_GZIP);
133
-
134
- // save to path
135
- if ($path !== null) {
136
- $this->save($content, $path);
137
- }
138
-
139
- return $content;
140
- }
141
-
142
- /**
143
- * Minify the data & write it to a CacheItemInterface object.
144
- *
145
- * @param CacheItemInterface $item Cache item to write the data to
146
- *
147
- * @return CacheItemInterface Cache item with the minifier data
148
- */
149
- public function cache(CacheItemInterface $item)
150
- {
151
- $content = $this->execute();
152
- $item->set($content);
153
-
154
- return $item;
155
- }
156
-
157
- /**
158
- * Minify the data.
159
- *
160
- * @param string[optional] $path Path to write the data to
161
- *
162
- * @return string The minified data
163
- */
164
- abstract public function execute($path = null);
165
-
166
- /**
167
- * Load data.
168
- *
169
- * @param string $data Either a path to a file or the content itself
170
- *
171
- * @return string
172
- */
173
- protected function load($data)
174
- {
175
- // check if the data is a file
176
- if ($this->canImportFile($data)) {
177
- $data = file_get_contents($data);
178
-
179
- // strip BOM, if any
180
- if (substr($data, 0, 3) == "\xef\xbb\xbf") {
181
- $data = substr($data, 3);
182
- }
183
- }
184
-
185
- return $data;
186
- }
187
-
188
- /**
189
- * Save to file.
190
- *
191
- * @param string $content The minified data
192
- * @param string $path The path to save the minified data to
193
- *
194
- * @throws IOException
195
- */
196
- protected function save($content, $path)
197
- {
198
- $handler = $this->openFileForWriting($path);
199
-
200
- $this->writeToFile($handler, $content);
201
-
202
- @fclose($handler);
203
- }
204
-
205
- /**
206
- * Register a pattern to execute against the source content.
207
- *
208
- * @param string $pattern PCRE pattern
209
- * @param string|callable $replacement Replacement value for matched pattern
210
- */
211
- protected function registerPattern($pattern, $replacement = '')
212
- {
213
- // study the pattern, we'll execute it more than once
214
- $pattern .= 'S';
215
-
216
- $this->patterns[] = array($pattern, $replacement);
217
- }
218
-
219
- /**
220
- * We can't "just" run some regular expressions against JavaScript: it's a
221
- * complex language. E.g. having an occurrence of // xyz would be a comment,
222
- * unless it's used within a string. Of you could have something that looks
223
- * like a 'string', but inside a comment.
224
- * The only way to accurately replace these pieces is to traverse the JS one
225
- * character at a time and try to find whatever starts first.
226
- *
227
- * @param string $content The content to replace patterns in
228
- *
229
- * @return string The (manipulated) content
230
- */
231
- protected function replace($content)
232
- {
233
- $processed = '';
234
- $positions = array_fill(0, count($this->patterns), -1);
235
- $matches = array();
236
-
237
- while ($content) {
238
- // find first match for all patterns
239
- foreach ($this->patterns as $i => $pattern) {
240
- list($pattern, $replacement) = $pattern;
241
-
242
- // no need to re-run matches that are still in the part of the
243
- // content that hasn't been processed
244
- if ($positions[$i] >= 0) {
245
- continue;
246
- }
247
-
248
- $match = null;
249
- if (preg_match($pattern, $content, $match)) {
250
- $matches[$i] = $match;
251
-
252
- // we'll store the match position as well; that way, we
253
- // don't have to redo all preg_matches after changing only
254
- // the first (we'll still know where those others are)
255
- $positions[$i] = strpos($content, $match[0]);
256
- } else {
257
- // if the pattern couldn't be matched, there's no point in
258
- // executing it again in later runs on this same content;
259
- // ignore this one until we reach end of content
260
- unset($matches[$i]);
261
- $positions[$i] = strlen($content);
262
- }
263
- }
264
-
265
- // no more matches to find: everything's been processed, break out
266
- if (!$matches) {
267
- $processed .= $content;
268
- break;
269
- }
270
-
271
- // see which of the patterns actually found the first thing (we'll
272
- // only want to execute that one, since we're unsure if what the
273
- // other found was not inside what the first found)
274
- $discardLength = min($positions);
275
- $firstPattern = array_search($discardLength, $positions);
276
- $match = $matches[$firstPattern][0];
277
-
278
- // execute the pattern that matches earliest in the content string
279
- list($pattern, $replacement) = $this->patterns[$firstPattern];
280
- $replacement = $this->replacePattern($pattern, $replacement, $content);
281
-
282
- // figure out which part of the string was unmatched; that's the
283
- // part we'll execute the patterns on again next
284
- $content = substr($content, $discardLength);
285
- $unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
286
-
287
- // move the replaced part to $processed and prepare $content to
288
- // again match batch of patterns against
289
- $processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
290
- $content = $unmatched;
291
-
292
- // first match has been replaced & that content is to be left alone,
293
- // the next matches will start after this replacement, so we should
294
- // fix their offsets
295
- foreach ($positions as $i => $position) {
296
- $positions[$i] -= $discardLength + strlen($match);
297
- }
298
- }
299
-
300
- return $processed;
301
- }
302
-
303
- /**
304
- * This is where a pattern is matched against $content and the matches
305
- * are replaced by their respective value.
306
- * This function will be called plenty of times, where $content will always
307
- * move up 1 character.
308
- *
309
- * @param string $pattern Pattern to match
310
- * @param string|callable $replacement Replacement value
311
- * @param string $content Content to match pattern against
312
- *
313
- * @return string
314
- */
315
- protected function replacePattern($pattern, $replacement, $content)
316
- {
317
- if (is_callable($replacement)) {
318
- return preg_replace_callback($pattern, $replacement, $content, 1, $count);
319
- } else {
320
- return preg_replace($pattern, $replacement, $content, 1, $count);
321
- }
322
- }
323
-
324
- /**
325
- * Strings are a pattern we need to match, in order to ignore potential
326
- * code-like content inside them, but we just want all of the string
327
- * content to remain untouched.
328
- *
329
- * This method will replace all string content with simple STRING#
330
- * placeholder text, so we've rid all strings from characters that may be
331
- * misinterpreted. Original string content will be saved in $this->extracted
332
- * and after doing all other minifying, we can restore the original content
333
- * via restoreStrings().
334
- *
335
- * @param string[optional] $chars
336
- * @param string[optional] $placeholderPrefix
337
- */
338
- protected function extractStrings($chars = '\'"', $placeholderPrefix = '')
339
- {
340
- // PHP only supports $this inside anonymous functions since 5.4
341
- $minifier = $this;
342
- $callback = function ($match) use ($minifier, $placeholderPrefix) {
343
- // check the second index here, because the first always contains a quote
344
- if ($match[2] === '') {
345
- /*
346
- * Empty strings need no placeholder; they can't be confused for
347
- * anything else anyway.
348
- * But we still needed to match them, for the extraction routine
349
- * to skip over this particular string.
350
- */
351
- return $match[0];
352
- }
353
-
354
- $count = count($minifier->extracted);
355
- $placeholder = $match[1].$placeholderPrefix.$count.$match[1];
356
- $minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
357
-
358
- return $placeholder;
359
- };
360
-
361
- /*
362
- * The \\ messiness explained:
363
- * * Don't count ' or " as end-of-string if it's escaped (has backslash
364
- * in front of it)
365
- * * Unless... that backslash itself is escaped (another leading slash),
366
- * in which case it's no longer escaping the ' or "
367
- * * So there can be either no backslash, or an even number
368
- * * multiply all of that times 4, to account for the escaping that has
369
- * to be done to pass the backslash into the PHP string without it being
370
- * considered as escape-char (times 2) and to get it in the regex,
371
- * escaped (times 2)
372
- */
373
- $this->registerPattern('/(['.$chars.'])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
374
- }
375
-
376
- /**
377
- * This method will restore all extracted data (strings, regexes) that were
378
- * replaced with placeholder text in extract*(). The original content was
379
- * saved in $this->extracted.
380
- *
381
- * @param string $content
382
- *
383
- * @return string
384
- */
385
- protected function restoreExtractedData($content)
386
- {
387
- if (!$this->extracted) {
388
- // nothing was extracted, nothing to restore
389
- return $content;
390
- }
391
-
392
- $content = strtr($content, $this->extracted);
393
-
394
- $this->extracted = array();
395
-
396
- return $content;
397
- }
398
-
399
- /**
400
- * Check if the path is a regular file and can be read.
401
- *
402
- * @param string $path
403
- *
404
- * @return bool
405
- */
406
- protected function canImportFile($path)
407
- {
408
- return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
409
- }
410
-
411
- /**
412
- * Attempts to open file specified by $path for writing.
413
- *
414
- * @param string $path The path to the file
415
- *
416
- * @return resource Specifier for the target file
417
- *
418
- * @throws IOException
419
- */
420
- protected function openFileForWriting($path)
421
- {
422
- if (($handler = @fopen($path, 'w')) === false) {
423
- throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
424
- }
425
-
426
- return $handler;
427
- }
428
-
429
- /**
430
- * Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
431
- *
432
- * @param resource $handler The resource to write to
433
- * @param string $content The content to write
434
- * @param string $path The path to the file (for exception printing only)
435
- *
436
- * @throws IOException
437
- */
438
- protected function writeToFile($handler, $content, $path = '')
439
- {
440
- if (($result = @fwrite($handler, $content)) === false || ($result < strlen($content))) {
441
- throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
442
- }
443
- }
444
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/minify/src/index.html DELETED
File without changes
libs/matthiasmullie/path-converter/index.html DELETED
File without changes
libs/matthiasmullie/path-converter/src/Converter.php DELETED
@@ -1,195 +0,0 @@
1
- <?php
2
-
3
- namespace MatthiasMullie\PathConverter;
4
-
5
- /**
6
- * Convert paths relative from 1 file to another.
7
- *
8
- * E.g.
9
- * ../../images/icon.jpg relative to /css/imports/icons.css
10
- * becomes
11
- * ../images/icon.jpg relative to /css/minified.css
12
- *
13
- * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
14
- *
15
- * @author Matthias Mullie <pathconverter@mullie.eu>
16
- * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
17
- * @license MIT License
18
- */
19
- class Converter implements ConverterInterface
20
- {
21
- /**
22
- * @var string
23
- */
24
- protected $from;
25
-
26
- /**
27
- * @var string
28
- */
29
- protected $to;
30
-
31
- /**
32
- * @param string $from The original base path (directory, not file!)
33
- * @param string $to The new base path (directory, not file!)
34
- */
35
- public function __construct($from, $to)
36
- {
37
- $shared = $this->shared($from, $to);
38
- if ($shared === '') {
39
- // when both paths have nothing in common, one of them is probably
40
- // absolute while the other is relative
41
- $cwd = getcwd();
42
- $from = strpos($from, $cwd) === 0 ? $from : $cwd.'/'.$from;
43
- $to = strpos($to, $cwd) === 0 ? $to : $cwd.'/'.$to;
44
-
45
- // or traveling the tree via `..`
46
- // attempt to resolve path, or assume it's fine if it doesn't exist
47
- $from = realpath($from) ?: $from;
48
- $to = realpath($to) ?: $to;
49
- }
50
-
51
- $from = $this->dirname($from);
52
- $to = $this->dirname($to);
53
-
54
- $from = $this->normalize($from);
55
- $to = $this->normalize($to);
56
-
57
- $this->from = $from;
58
- $this->to = $to;
59
- }
60
-
61
- /**
62
- * Normalize path.
63
- *
64
- * @param string $path
65
- *
66
- * @return string
67
- */
68
- protected function normalize($path)
69
- {
70
- // deal with different operating systems' directory structure
71
- $path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
72
-
73
- /*
74
- * Example:
75
- * /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif
76
- * to
77
- * /home/forkcms/frontend/core/layout/images/img.gif
78
- */
79
- do {
80
- $path = preg_replace('/[^\/]+(?<!\.\.)\/\.\.\//', '', $path, -1, $count);
81
- } while ($count);
82
-
83
- return $path;
84
- }
85
-
86
- /**
87
- * Figure out the shared path of 2 locations.
88
- *
89
- * Example:
90
- * /home/forkcms/frontend/core/layout/images/img.gif
91
- * and
92
- * /home/forkcms/frontend/cache/minified_css
93
- * share
94
- * /home/forkcms/frontend
95
- *
96
- * @param string $path1
97
- * @param string $path2
98
- *
99
- * @return string
100
- */
101
- protected function shared($path1, $path2)
102
- {
103
- // $path could theoretically be empty (e.g. no path is given), in which
104
- // case it shouldn't expand to array(''), which would compare to one's
105
- // root /
106
- $path1 = $path1 ? explode('/', $path1) : array();
107
- $path2 = $path2 ? explode('/', $path2) : array();
108
-
109
- $shared = array();
110
-
111
- // compare paths & strip identical ancestors
112
- foreach ($path1 as $i => $chunk) {
113
- if (isset($path2[$i]) && $path1[$i] == $path2[$i]) {
114
- $shared[] = $chunk;
115
- } else {
116
- break;
117
- }
118
- }
119
-
120
- return implode('/', $shared);
121
- }
122
-
123
- /**
124
- * Convert paths relative from 1 file to another.
125
- *
126
- * E.g.
127
- * ../images/img.gif relative to /home/forkcms/frontend/core/layout/css
128
- * should become:
129
- * ../../core/layout/images/img.gif relative to
130
- * /home/forkcms/frontend/cache/minified_css
131
- *
132
- * @param string $path The relative path that needs to be converted
133
- *
134
- * @return string The new relative path
135
- */
136
- public function convert($path)
137
- {
138
- // quit early if conversion makes no sense
139
- if ($this->from === $this->to) {
140
- return $path;
141
- }
142
-
143
- $path = $this->normalize($path);
144
- // if we're not dealing with a relative path, just return absolute
145
- if (strpos($path, '/') === 0) {
146
- return $path;
147
- }
148
-
149
- // normalize paths
150
- $path = $this->normalize($this->from.'/'.$path);
151
-
152
- // strip shared ancestor paths
153
- $shared = $this->shared($path, $this->to);
154
- $path = mb_substr($path, mb_strlen($shared));
155
- $to = mb_substr($this->to, mb_strlen($shared));
156
-
157
- // add .. for every directory that needs to be traversed to new path
158
- $to = str_repeat('../', mb_substr_count($to, '/'));
159
-
160
- return $to.ltrim($path, '/');
161
- }
162
-
163
- /**
164
- * Attempt to get the directory name from a path.
165
- *
166
- * @param string $path
167
- *
168
- * @return string
169
- */
170
- protected function dirname($path)
171
- {
172
- if (is_file($path)) {
173
- return dirname($path);
174
- }
175
-
176
- if (is_dir($path)) {
177
- return rtrim($path, '/');
178
- }
179
-
180
- // no known file/dir, start making assumptions
181
-
182
- // ends in / = dir
183
- if (mb_substr($path, -1) === '/') {
184
- return rtrim($path, '/');
185
- }
186
-
187
- // has a dot in the name, likely a file
188
- if (preg_match('/.*\..*$/', basename($path)) !== 0) {
189
- return dirname($path);
190
- }
191
-
192
- // you're on your own here!
193
- return $path;
194
- }
195
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/path-converter/src/ConverterInterface.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
-
3
- namespace MatthiasMullie\PathConverter;
4
-
5
- /**
6
- * Convert file paths.
7
- *
8
- * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
- *
10
- * @author Matthias Mullie <pathconverter@mullie.eu>
11
- * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
- * @license MIT License
13
- */
14
- interface ConverterInterface
15
- {
16
- /**
17
- * Convert file paths.
18
- *
19
- * @param string $path The path to be converted
20
- *
21
- * @return string The new path
22
- */
23
- public function convert($path);
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/path-converter/src/NoConverter.php DELETED
@@ -1,23 +0,0 @@
1
- <?php
2
-
3
- namespace MatthiasMullie\PathConverter;
4
-
5
- /**
6
- * Don't convert paths.
7
- *
8
- * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
- *
10
- * @author Matthias Mullie <pathconverter@mullie.eu>
11
- * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
- * @license MIT License
13
- */
14
- class NoConverter implements ConverterInterface
15
- {
16
- /**
17
- * {@inheritdoc}
18
- */
19
- public function convert($path)
20
- {
21
- return $path;
22
- }
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/matthiasmullie/path-converter/src/index.html DELETED
File without changes
libs/mrclay/HTML.php DELETED
@@ -1,241 +0,0 @@
1
- <?php
2
-
3
- /* https://github.com/mrclay/minify/blob/2.x/min/lib/Minify/HTML.php - same as WP ROCKET v2.91 + custom changes */
4
- # line:
5
-
6
- class fastvelocity_min_Minify_HTML {
7
- protected $_jsCleanComments = true;
8
-
9
- /**
10
- * "Minify" an HTML page
11
- *
12
- * @param string $html
13
- *
14
- * @param array $options
15
- *
16
- * 'cssMinifier' : (optional) callback function to process content of STYLE
17
- * elements.
18
- *
19
- * 'jsMinifier' : (optional) callback function to process content of SCRIPT
20
- * elements. Note: the type attribute is ignored.
21
- *
22
- * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
23
- * unset, minify will sniff for an XHTML doctype.
24
- *
25
- * @return string
26
- */
27
- public static function minify($html, $options = array()) {
28
- $min = new self($html, $options);
29
- return $min->process();
30
- }
31
-
32
-
33
- /**
34
- * Create a minifier object
35
- *
36
- * @param string $html
37
- *
38
- * @param array $options
39
- *
40
- * 'cssMinifier' : (optional) callback function to process content of STYLE
41
- * elements.
42
- *
43
- * 'jsMinifier' : (optional) callback function to process content of SCRIPT
44
- * elements. Note: the type attribute is ignored.
45
- *
46
- * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
47
- *
48
- * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
49
- * unset, minify will sniff for an XHTML doctype.
50
- */
51
- public function __construct($html, $options = array())
52
- {
53
- $this->_html = str_replace("\r\n", "\n", trim($html));
54
- if (isset($options['xhtml'])) {
55
- $this->_isXhtml = (bool)$options['xhtml'];
56
- }
57
- if (isset($options['cssMinifier'])) {
58
- $this->_cssMinifier = $options['cssMinifier'];
59
- }
60
- if (isset($options['jsMinifier'])) {
61
- $this->_jsMinifier = $options['jsMinifier'];
62
- }
63
- if (isset($options['jsCleanComments'])) {
64
- $this->_jsCleanComments = (bool)$options['jsCleanComments'];
65
- }
66
- }
67
-
68
-
69
- /**
70
- * Minify the markeup given in the constructor
71
- *
72
- * @return string
73
- */
74
- public function process()
75
- {
76
- if ($this->_isXhtml === null) {
77
- $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
78
- }
79
-
80
- $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
81
- $this->_placeholders = array();
82
-
83
- // replace SCRIPTs (and minify) with placeholders
84
- $this->_html = preg_replace_callback(
85
- '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
86
- ,array($this, '_removeScriptCB')
87
- ,$this->_html);
88
-
89
- // replace STYLEs (and minify) with placeholders
90
- $this->_html = preg_replace_callback(
91
- '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
92
- ,array($this, '_removeStyleCB')
93
- ,$this->_html);
94
-
95
- // remove HTML comments (not containing IE conditional comments).
96
- global $strip_htmlcomments;
97
- if($strip_htmlcomments) {
98
- $this->_html = preg_replace_callback('/<!--([\\s\\S]*?)-->/' ,array($this, '_commentCB') ,$this->_html);
99
- }
100
-
101
- // replace PREs with placeholders
102
- $this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
103
- ,array($this, '_removePreCB')
104
- ,$this->_html);
105
-
106
- // replace TEXTAREAs with placeholders
107
- $this->_html = preg_replace_callback(
108
- '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
109
- ,array($this, '_removeTextareaCB')
110
- ,$this->_html);
111
-
112
- // trim each line.
113
- // @todo take into account attribute values that span multiple lines.
114
- $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
115
-
116
- // remove ws around block/undisplayed elements
117
- $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
118
- .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
119
- .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
120
- .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
121
- .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
122
-
123
- // remove ws outside of all elements
124
- $this->_html = preg_replace_callback(
125
- '/>([^<]+)</'
126
- ,array($this, '_outsideTagCB')
127
- ,$this->_html);
128
-
129
- // use newlines before 1st attribute in open tags (to limit line lengths)
130
- //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
131
-
132
- // fill placeholders
133
- $this->_html = str_replace(
134
- array_keys($this->_placeholders)
135
- ,array_values($this->_placeholders)
136
- ,$this->_html
137
- );
138
- // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
139
- $this->_html = str_replace(
140
- array_keys($this->_placeholders)
141
- ,array_values($this->_placeholders)
142
- ,$this->_html
143
- );
144
- return $this->_html;
145
- }
146
-
147
- protected function _commentCB($m)
148
- {
149
- return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<![') || 0 === strpos($m[1], 'esi')) ? $m[0] : '';
150
- }
151
-
152
- protected function _reservePlace($content)
153
- {
154
- $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
155
- $this->_placeholders[$placeholder] = $content;
156
- return $placeholder;
157
- }
158
-
159
- protected $_isXhtml = null;
160
- protected $_replacementHash = null;
161
- protected $_placeholders = array();
162
- protected $_cssMinifier = null;
163
- protected $_jsMinifier = null;
164
-
165
- protected function _outsideTagCB($m)
166
- {
167
- return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
168
- }
169
-
170
- protected function _removePreCB($m)
171
- {
172
- return $this->_reservePlace("<pre{$m[1]}");
173
- }
174
-
175
- protected function _removeTextareaCB($m)
176
- {
177
- return $this->_reservePlace("<textarea{$m[1]}");
178
- }
179
-
180
- protected function _removeStyleCB($m)
181
- {
182
- $openStyle = "<style{$m[1]}";
183
- $css = $m[2];
184
- // remove css comments
185
- $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
186
-
187
- // remove CDATA section markers
188
- $css = $this->_removeCdata($css);
189
-
190
- // minify
191
- $minifier = $this->_cssMinifier ? $this->_cssMinifier : 'trim';
192
- $css = call_user_func($minifier, $css);
193
- $css = preg_replace('/\s+/', ' ', $css);
194
-
195
- return $this->_reservePlace($this->_needsCdata($css)
196
- ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
197
- : "{$openStyle}{$css}</style>"
198
- );
199
- }
200
-
201
- protected function _removeScriptCB($m)
202
- {
203
- $openScript = "<script{$m[2]}";
204
- $js = $m[3];
205
-
206
- // whitespace surrounding? preserve at least one space
207
- $ws1 = ($m[1] === '') ? '' : ' ';
208
- $ws2 = ($m[4] === '') ? '' : ' ';
209
-
210
- // remove HTML comments (and ending "//" if present)
211
- if ($this->_jsCleanComments) {
212
- $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
213
- }
214
-
215
- // remove CDATA section markers
216
- $js = $this->_removeCdata($js);
217
-
218
- // minify
219
- $minifier = $this->_jsMinifier
220
- ? $this->_jsMinifier
221
- : 'trim';
222
- $js = call_user_func($minifier, $js);
223
-
224
- return $this->_reservePlace($this->_needsCdata($js)
225
- ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
226
- : "{$ws1}{$openScript}{$js}</script>{$ws2}"
227
- );
228
- }
229
-
230
- protected function _removeCdata($str)
231
- {
232
- return (false !== strpos($str, '<![CDATA['))
233
- ? str_replace(array('<![CDATA[', ']]>'), '', $str)
234
- : $str;
235
- }
236
-
237
- protected function _needsCdata($str)
238
- {
239
- return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
240
- }
241
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/mrclay/index.html DELETED
File without changes
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === Fast Velocity Minify ===
2
  Contributors: Alignak
3
- Tags: PHP Minify, YUI Compressor, GTmetrix, Pingdom, Pagespeed, CSS Merging, JS Merging, CSS Minification, JS Minification, Speed Optimization, HTML Minification, Performance
4
  Requires at least: 4.5
5
- Stable tag: 2.2.6
6
- Tested up to: 4.9.1
7
  License: GPLv3 or later
8
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
 
@@ -30,26 +30,25 @@ I can offer you aditional `custom made` optimization on top of this plugin. If y
30
  * Handles scripts loaded both in the header & footer separately
31
  * Keeps the order of the scripts even if you exclude some files from minification
32
  * Supports localized scripts (https://codex.wordpress.org/Function_Reference/wp_localize_script)
33
- * Minifies CSS and JS with PHP Minify only, no third party software or libraries needed.
34
- * Option to use YUI Compressor rather than PHP Minify (you you have "exec" and "java" available on your system).
35
  * Option to defer JavaScript and CSS files.
36
- * Stores the cache files in the uploads directory.
37
- * View the status and logs on the WordPress admin page.
38
- * Option to Minify HTML for further improvements.
39
- * Ability to turn off minification
40
- * Ability to turn off CSS or JS optimization seperatly (by disabling either css or js processing)
41
  * Ability to manually ignore scripts or css
42
  * Support for conditional scripts and styles
43
  * Support for multisite installations
44
  * Support for gzip_static on Nginx
45
- * Support for the CDN Enabler plugin
46
  * Auto purging of cache files on W3 Total Cache, WP Supercache, WP Rocket, Wp Fastest Cache, Cachify, Comet Cache, Zen Cache, LiteSpeed Cache, Nginx Cache (by Till Krüss ), SG Optimizer, Godaddy Managed WordPress Hosting and WP Engine (read the FAQs)
47
  * Support for preconnect and preload headers
48
  * and some more...
49
 
50
 
51
  = Notes =
52
- * The JavaScript minification is by [PHP Minify](https://github.com/matthiasmullie/minify)
 
53
  * The alternative JavaScript minification is by [YUI Compressor](http://yui.github.io/yuicompressor/)
54
  * Compatible with Nginx, HHVM and PHP 7.
55
  * Minimum requirements are PHP 5.5 and WP 4.4, from version 1.4.0 onwards
@@ -71,151 +70,111 @@ I can offer you aditional `custom made` optimization on top of this plugin. If y
71
  == Frequently Asked Questions ==
72
 
73
  = How can I exclude certain assets by wildcard? =
74
-
75
- Each line on the ignore list will try to match a substring against all CSS or JS files, for example: `//yoursite.com/wp-content/plugins/some-plugin/js/` will ignore all files inside that directory. You can also shorten the url like `/some-plugin/js/` and then it will match any css or js url that has `/some-plugin/js/` on the path. Obviously, doing `/js/` would match any files inside any "/js/" directory and in any location, so to avoid unexpected situations please always use the longest, most specific path you can use.
76
-
77
  ...
78
 
79
  = Why is the ignore list not working? =
80
-
81
- The ignore list may be working, just try to use partial paths (see wildcard help above) and use relative urls only without any query vars.
82
-
83
  ...
84
 
85
-
86
- = Why are there several or lot's of JS and CSS files listed on the status page, or why the cache directory takes so much space? =
87
-
88
- Well, some sites and themes have a combined CSS size above 1+ MB, so when you have 200 files, that's 200+ MB.
89
- Different pages may need different JS and CSS files per page, post, category, tag, homepage or even custom post types, but if the requirements never change and you always load the exact same files on every page, you won't see as many files. Likewise, if you have some dynamic url for CSS or JS that always changes in each pageview (the query var for example), you must add it to the ignore list (else it will generate a new cache file every pageview).
90
-
91
  ...
92
-
93
  = Can I update plugins and themes? =
 
94
 
95
- Yes, but it's recommended that you purge the cache (from the plugin status page) in order for the merging and minification cache files to be regenerated and for you to be sure the updates went smoothly. The plugin will also try to automatically purge some popular cache plugins, however we still recommend that you purge all caches on your cache plugin / server (whatever you use) "after" purging Fast Velocity Minify cache.
96
-
97
- ...
98
-
99
- = Is it compatible with other caching plugins? =
100
-
101
- You must disable any features on your theme or cache plugins, that perform minification of css, html and js.
102
- The plugin will try to automatically purge several popular cache plugins, however we still recommend you to purge all caches (on whatever you use) if you also manually purge the cache on the plugin settings for some reason.
103
  The automatic purge is active for the following plugins and hosting: W3 Total Cache, WP Supercache, WP Rocket, Wp Fastest Cache, Cachify, Comet Cache, Zen Cache, LiteSpeed Cache, SG Optimizer, Godaddy Managed WordPress Hosting and WP Engine
104
 
105
  ...
106
 
107
  = Is it resource intensive, or will it use too much CPU on my shared hosting plan? =
108
-
109
- No it's not. On the first run, each single file is minified into an intermediate cache. When a new group of files is found, it reuses those files and merges them into a new static cache file. All pages that request the same group of CSS or JS files will also make use of that file.
110
-
111
  ...
112
-
113
  = Is it compatible with multisites? =
114
-
115
- Yes, it generates a new cache file for every different set of JS and CSS requirements it finds.
116
-
117
  ...
118
-
119
  = Is it compatible with Adsense and other ad networks? =
120
-
121
- The plugin is compatible with any add network but also depends on how you're loading the ads into the site. We only merge and minify css and javascript files enqueued in the header and footer that match your own domain name... which would exclude any external ads. If you're using a plugin that uses JS to insert the ads on the page, there could be issues. Please report on the support forum if you found such case.
122
 
123
  ...
124
 
125
  = After installing, why did my site feels slow to load? =
126
-
127
- The cache regeration happen's once per url or if the included CSS + JS files change. If you need the same set of CSS and JS files in every page, the cache file will only be generated once and reused for all other pages, however if you have a CSS or JS that is generated dynamically and uses a time based query string, (url changes on every pageview), you must add it to the ignore list by wildcard.
128
 
129
  ...
130
 
131
- = How do I use the precompressed files with gzip_static on Nginx? =
132
 
133
  When we merge and minify the css and js files, we also create a `.gz` file to be used with `gzip_static` on Nginx. You need to enable this feature on your Nginx configuration file if you want to make use of it.
134
 
135
  ...
136
 
137
- = Where is the YUI Compressor option gone to? =
138
-
139
- This functionality depends on wheter you have exec and java available on your system and PHP can detect it or not. It will be visible on the basic Settings page under the JavaScript Options section and it's available for JS files only.
140
 
141
  ...
142
 
143
  = After installing, why are some images and sliders not working? =
144
 
145
- a) You cannot do double minification as it will break things, so make sure you have disabled any features on your theme or other plugins, that perform minification of css, html and js files.
146
 
147
  b) Are you trying to use the defer JS or CSS options, without understanding exactly how it works? Be advised that most themes do not work properly with those options on.
148
 
149
- c) The plugin relies on PHP Minify to minify javascript and css files, however it's not a perfect library and there are plugins that are already minified and do not output a min.js or min.css name and end up being minified again. Try to disable minification on JS and CSS files and purge the cache.
150
 
151
- d) Sometimes a plugin conflicts with another when merged. Try to disable CSS processing first and see if it works. Try to disable JS processing second and see if it works. Try to disable HTML minification last and see if it works. If one of those work, you know there's a conflict.
152
 
153
- e) If you have a conflict, try to add each CSS and each JS to the ignore list, one by one until you find the one that causes the conflict. If you have no idea which files to add, check the log file on the status page for a list of files.
154
 
155
  ...
156
 
157
- = Why are some of the CSS and JS files not being merged ? =
158
 
159
  The plugin only processes (same domain) JS and CSS files enqueued using the official method outlined here: https://developer.wordpress.org/themes/basics/including-css-javascript/
160
-
161
- Some developers enqueue all files properly but may still "print" conditional tags directly in the header, while they should be following the example as explained on the codex: https://developer.wordpress.org/reference/functions/wp_script_add_data/
162
-
163
- Because "printing CSS and JS tags on the header and footer is evil" (seriously), we cannot capture those files and merge them together.
164
-
165
- There are also specific ways to enqueue files meant to be loaded for IE users only, mobile users, desktop users, printers, etc, else those may be merged together and break things. There is a blacklist and default ignore list on the PRO tab because of this.
166
 
167
  ...
168
 
169
  = How can I load CSS async, get the critical path or why is there a flash of unstyle content when I enable this? =
170
-
171
  This is an advanced option for highly skilled developers. Do not try to fiddle with these settings if you are not one, as it will almost certainly break your site layout and functionality.
172
-
173
  Loading CSS async only works properly and consistently, when you have one single CSS file being generated and your critical path is generic enough to be common on all pages.
174
 
175
  ...
176
 
177
  = How to undo all changes done by the plugin? =
178
-
179
- The plugin itself doesn't do any "changes" to your site and all original files are untouched. It intercepts the enqueued CSS and JS files, processes and hides them, while enqueuing the newly optimized cached version of those files. As with any plugin, simply disable or uninstall the plugin, purge all caches you may have in use (plugins, server, cloudflare, etc) and the site will go back to what it was before installing it. The plugin doesn't delete anything from the database or modify any of your files.
180
-
181
  ...
182
 
183
  = Why is it that even though I have disabled or deleted the plugin, the design is still broken? =
184
-
185
- Some "cheap" or so called "optimized" hosting providers, implement a misconfigured and agressive cache on their servers, and then are also "smart" enough to ignore your multiple cache purge requests if those are too frequent.
186
-
187
- Some providers use a "deploy system", where you upload / replace / delete the files via sftp, but those are not reflected on the live site immediatly. This means that in some cases, you can delete your whole wordpress instalation via sftp, and the site still works perfectly fine even after purging the cache.
188
-
189
- You may have deleted the physical files from the disk, but that's either just some random storage they gave you and that they sync to the main server every few hours, or it may be the actual live server but with a file cache layer on top of it (so the old code keeps running even though you have deleted the files).
190
-
191
- The only solution is to contact your hosting company and ask them why you have deleted the plugin and purged your cache, but the live site doesn't reflect the changes.
192
-
193
  Providers well known to have this issue are hostgator and iPage (please report others if you find them).
194
-
195
  ...
196
 
197
- = Why is my Visual Composer, or Page Editor not working ? =
198
-
199
- Some plugins and themes need to edit the layout and styles on the frontend. When they need to do that, they enqueue several extra js and css files that are caught by this plugin and get merged together, when in fact those need to load seperatly. If you encounter such issue of your page editor not working on the frontend, kindly enable the "Fix Page Editors" and purge your caches.
200
-
201
- This option hides all optimization from logged in users, so it's as iff the plugin has been disabled. Not logged in users and search engines, still see the optimized version.
202
-
203
  ...
204
 
205
- = How should I use the "Preload Images" and what is it for? =
 
 
 
206
 
207
- Certain themes and plugins, either load large images or sliders on the homepage. Most of them will also load "above the fold" causing the "Prioritize visible content" or the "Eliminate render-blocking JavaScript and CSS in above-the-fold content" message on pagespeed insights.
 
208
 
209
- While this may not work when the images are large, you can use the "Preload Images" for the first relevant images that load above the fold, such as the logo or the first image of a slider. Please note however, this is for images that show up in all pages, not just the homepage.
210
 
211
- Don't put too many images here as those are downloaded in high priority and it will slow down the rest of the page load.
212
 
 
 
213
  ...
214
 
215
  = What are the recommended cloudflare settings for this plugin? =
216
-
217
  On the "Speed" tab, deselect the Auto Minify for JavaScript, CSS and HTML as well as the Rocket Loader option as there is no benefit of using them with our plugin (we already minify things).
218
-
219
  Those options can also break the design due to double minification or the fact that the Rocket Loader is still experimental (you can read about that on the "Help" link under each selected option on cloudflare).
220
 
221
  ...
@@ -226,7 +185,7 @@ Before getting angry because you have no answer within a few hours (even with pa
226
 
227
  The plugins directory is an open source, free service where developers and programmers contribute (on their free time) with plugins that can be downloaded and installed by anyone "at their own risk" and are all released under the GPL license.
228
 
229
- While all plugins have to be approved and reviewed by the wordpress team before being published ( for dangerous code, spam, etc ) this doesn't change the license or add any warranty. All plugins are provided as they are, free of charge and should be used at your own risk (so you should make backups before installing any plugin or performing updates) and it's your sole responsability if you break your site after installing a plugin from the plugins directory.
230
 
231
  Support is provided by plugin authors on their free time and without warranty of a reply, so you can experience different levels of support level from plugin to plugin. As the author of this plugin I strive to provide support on a daily basis and I can take a look and help you with some issues related with my plugin, but please note that this is done out of my goodwill and in no way I have any legal or moral obligation for doing this. I'm also available for hiring if you need custom made speed optimizations (check my profile links).
232
 
@@ -236,18 +195,19 @@ For a full version of the license, please read: https://wordpress.org/about/gpl/
236
 
237
  = Where can I get support or report bugs? =
238
 
239
- You can get support on the official wordpress plugin page at: https://wordpress.org/support/plugin/fast-velocity-minify
240
- You can also see my profile and check my links if you wish to hire me for custom speed optimization on wordpress or extra features.
241
 
242
  ...
243
 
244
  = How can I donate to the plugin author? =
245
 
246
- If you would like to donate any amount to the plugin author (thank you in advance), you can do it via Paypal at https://goo.gl/vpLrSV
247
 
248
  ...
249
 
250
 
 
251
  == Upgrade Notice ==
252
 
253
  = 2.2.4 =
@@ -256,6 +216,13 @@ Note: Kindly re-save all options and purge all caches (the plugin cache as well
256
 
257
  == Changelog ==
258
 
 
 
 
 
 
 
 
259
  = 2.2.6 [2018.01.06] =
260
  * fixed a bug with html minification on some files that should not be minified
261
  * fixed a bug with the defer for pagespeed insights
1
  === Fast Velocity Minify ===
2
  Contributors: Alignak
3
+ Tags: CSSTidy, JSMin, YUI Compressor, GTmetrix, Pingdom, Pagespeed, CSS Minification, JS Minification, Speed Optimization, HTML Minification, Performance
4
  Requires at least: 4.5
5
+ Stable tag: 2.2.7
6
+ Tested up to: 4.9.4
7
  License: GPLv3 or later
8
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
 
30
  * Handles scripts loaded both in the header & footer separately
31
  * Keeps the order of the scripts even if you exclude some files from minification
32
  * Supports localized scripts (https://codex.wordpress.org/Function_Reference/wp_localize_script)
33
+ * Minifies CSS, JS and HTML with PHP only
34
+ * Option to use YUI Compressor (you must have "exec" and "java" available on your system).
35
  * Option to defer JavaScript and CSS files.
36
+ * Cache files are stored and served directly from the disk.
37
+ * View merging logs on the admin page.
38
+ * Ability to turn off CSS, JS or HTML processing
 
 
39
  * Ability to manually ignore scripts or css
40
  * Support for conditional scripts and styles
41
  * Support for multisite installations
42
  * Support for gzip_static on Nginx
43
+ * CDN support
44
  * Auto purging of cache files on W3 Total Cache, WP Supercache, WP Rocket, Wp Fastest Cache, Cachify, Comet Cache, Zen Cache, LiteSpeed Cache, Nginx Cache (by Till Krüss ), SG Optimizer, Godaddy Managed WordPress Hosting and WP Engine (read the FAQs)
45
  * Support for preconnect and preload headers
46
  * and some more...
47
 
48
 
49
  = Notes =
50
+ * The JavaScript minification is by JSMin](https://github.com/rgrove/jsmin-php/blob/master/jsmin.php)
51
+ * The CSS minification is by [CSSTidy](https://github.com/Cerdic/CSSTidy)
52
  * The alternative JavaScript minification is by [YUI Compressor](http://yui.github.io/yuicompressor/)
53
  * Compatible with Nginx, HHVM and PHP 7.
54
  * Minimum requirements are PHP 5.5 and WP 4.4, from version 1.4.0 onwards
70
  == Frequently Asked Questions ==
71
 
72
  = How can I exclude certain assets by wildcard? =
73
+ Each line on the ignore list will try to match a substring against all CSS or JS files, for example: `//yoursite.com/wp-content/plugins/some-plugin/js/` will ignore all files that have this on the path. You can also shorten the url like `/some-plugin/js/` and then it will match any css or js url that has `/some-plugin/js/` on the path. Obviously, doing `/js/` would match any files inside any "/js/" directory and in any location, so to avoid unexpected situations please always use the longest, most specific path you can use.
 
 
74
  ...
75
 
76
  = Why is the ignore list not working? =
77
+ The ignore list may be working, just try to use partial paths (see wildcard help above) and use relative urls only without any query vars. Example, if you want to exclude a file names `/jquery.js?v=1234` simply add the wildcard `/jquery.js` before the question mark.
 
 
78
  ...
79
 
80
+ = There are too many JS or CSS files generated, or the cache directory is taking too much space. =
81
+ a) Different pages may need different JS and CSS files per page, post, category, tag, homepage or even custom post types, therefore you need a file per requirement. b) If you have a dynamic CSS file that changes its name on each page view (the query var for example), you must add it by wildcard to the ignore list (else it will generate a new cache file every page view).
 
 
 
 
82
  ...
 
83
  = Can I update plugins and themes? =
84
+ Yes, but it is recommended that you purge all caches in order. First from the plugin status page, then your cache plugin and finally your hosting provider cache system. This is to make sure that your update, is not breaking things, which you wouldn’t see until FVM cached files or the page cache expires.
85
 
86
+ = Is it compatible with other caching plugins, or other speed optimization plugins? =
87
+ Yes and No. You must disable any features on your theme or plugins, which perform minification of css, html and js, else double minification may break your layout. It’s also inefficient to do the same work twice.
88
+ The plugin will try to automatically purge several popular cache plugins, however we still recommend you to purge all caches (on whatever you use) if you also manually purge the cache on the plugin settings for some reason.
 
 
 
 
 
89
  The automatic purge is active for the following plugins and hosting: W3 Total Cache, WP Supercache, WP Rocket, Wp Fastest Cache, Cachify, Comet Cache, Zen Cache, LiteSpeed Cache, SG Optimizer, Godaddy Managed WordPress Hosting and WP Engine
90
 
91
  ...
92
 
93
  = Is it resource intensive, or will it use too much CPU on my shared hosting plan? =
94
+ No, it is not. Cache files are saved on your disk, so as long as you have a fast SSD disk, you have nothing to worry about.
 
 
95
  ...
 
96
  = Is it compatible with multisites? =
97
+ Yes, it generates a new cache file for every different set of JS and CSS requirements it finds, but you need to setup the settings for each site individually (the settings may need to be different for each site).
 
 
98
  ...
 
99
  = Is it compatible with Adsense and other ad networks? =
100
+ The plugin is compatible with any add network but also depends on how you are loading the ads into the site. We only merge and minify css and JavaScript files enqueued in the header and footer that match your own domain name... which would exclude any external ads. If you are using a plugin that uses JS to insert the ads on the page, there could be issues. Please report on the support forum if you found such case.
 
101
 
102
  ...
103
 
104
  = After installing, why did my site feels slow to load? =
105
+ If you have a CSS or JS that is generated dynamically with PHP and uses a time based query string, (url changes on every page view), you must add it to the ignore list by wildcard. If you don’t do this, every single pageview will generate a new file cache, thus filling your storage very quickly.
 
106
 
107
  ...
108
 
109
+ = How do I use the pre-compressed files with gzip_static on Nginx? =
110
 
111
  When we merge and minify the css and js files, we also create a `.gz` file to be used with `gzip_static` on Nginx. You need to enable this feature on your Nginx configuration file if you want to make use of it.
112
 
113
  ...
114
 
115
+ = Where is the YUI Compressor option? =
116
+ This functionality depends on whether you have exec and java available on your system and PHP can detect it or not. It will be visible on the basic Settings page under the JavaScript Options section and it is available for JS files only.
 
117
 
118
  ...
119
 
120
  = After installing, why are some images and sliders not working? =
121
 
122
+ a) You cannot do double minification as it will break things, so make sure you have disabled any features on your theme or other plugins, which perform minification of css, html and js files.
123
 
124
  b) Are you trying to use the defer JS or CSS options, without understanding exactly how it works? Be advised that most themes do not work properly with those options on.
125
 
126
+ c) Try to disable minification on JS and CSS files and purge the cache.
127
 
128
+ d) Sometimes a plugin conflicts with another when merged. Try to disable CSS processing first and see if it works. Try to disable JS processing second and see if it works. Try to disable HTML minification last and see if it works. If one of those work, you know where is a conflict.
129
 
130
+ e) If you have a conflict, try to add each CSS and each JS to the ignore list, one by one until you find the one that causes the conflict. If you have no idea, which files to add, check the log file on the status page for a list of files.
131
 
132
  ...
133
 
134
+ = Why are some of the CSS and JS files not being merged? =
135
 
136
  The plugin only processes (same domain) JS and CSS files enqueued using the official method outlined here: https://developer.wordpress.org/themes/basics/including-css-javascript/
137
+ Furthermore, there is a blacklist, an ignore list and a default ignore list (check the Pro tab for a list of files). These are all known files, which cause trouble, but feel free to try again.
 
 
 
 
 
138
 
139
  ...
140
 
141
  = How can I load CSS async, get the critical path or why is there a flash of unstyle content when I enable this? =
 
142
  This is an advanced option for highly skilled developers. Do not try to fiddle with these settings if you are not one, as it will almost certainly break your site layout and functionality.
 
143
  Loading CSS async only works properly and consistently, when you have one single CSS file being generated and your critical path is generic enough to be common on all pages.
144
 
145
  ...
146
 
147
  = How to undo all changes done by the plugin? =
148
+ The plugin itself does not do any "changes" to your site and all original files are untouched. It also does not create or delete tables on the database. As with any plugin, simply disable or uninstall the plugin, purge all caches (plugins, server, cloudflare, etc.) and the site will go back to what it was before installing it. The plugin does not delete anything from the database or modify any of your files.
 
 
149
  ...
150
 
151
  = Why is it that even though I have disabled or deleted the plugin, the design is still broken? =
152
+ Some hosting providers have very aggressive caches on their servers, and they will ignore your multiple cache purge requests if those are too frequent. Contact your hosting and have them purge varnish, object cache, memcached, redis, APC, or whatever system they may use.
153
+ Some providers also use a "deploy system" where you can upload / replace / delete the files via sftp, but those are not reflected on the live site immediately. It’s merely a copy, which will be synced sometime later automatically. The only solution is to contact your hosting company and ask them why you have deleted the plugin and purged your cache, but the live site does not reflect the changes.
 
 
 
 
 
 
 
154
  Providers well known to have this issue are hostgator and iPage (please report others if you find them).
 
155
  ...
156
 
157
+ = My layout is broken if I disable the plugin, but works fine if I enable it. =
158
+ See the previous answer for the reason. As soon as you disable the plugin, the cache is purged and any references to `/wp-content/uploads/fvm/` on your html, must disappear. If it did not, there is a cache problem somewhere causing this. Contact your hosting company to manually purge your caches.
 
 
 
 
159
  ...
160
 
161
+ = Why is my Visual Composer or Page Editor not working? =
162
+ Kindly enable the "Fix Page Editors" and purge the cache.
163
+ This option hides all optimization from logged in users, so it is as if the plugin is disabled. Not logged in users and search engines, still see the optimized version. If this doesn’t work for you, please open a support request on the forum.
164
+ ...
165
 
166
+ = Why is NextGEN Gallery not working? =
167
+ You need to have render blocking jQuery for this plugin (it's a requirement), so you must exclude the jquery library via the ignore list. NextGEN Gallery CSS files, for the majority cannot be merged (even if you copy paste manually one after the other), due to the lack of speceficity on CSS. I have reported this to the author on https://wordpress.org/support/topic/lack-of-css-specificity-and-conflict-when-merging/ so meanwhile, you can either inline all css or add their css files to the ignore list.
168
 
169
+ ...
170
 
 
171
 
172
+ = How should I use the "Preload Images" and what is it for? =
173
+ You can use the "Preload Images" for the first relevant images that load above the fold on all pages, such as the logo or social icons. Do not put too many images here as those are downloaded in high priority and it will slow down the rest of the page load.
174
  ...
175
 
176
  = What are the recommended cloudflare settings for this plugin? =
 
177
  On the "Speed" tab, deselect the Auto Minify for JavaScript, CSS and HTML as well as the Rocket Loader option as there is no benefit of using them with our plugin (we already minify things).
 
178
  Those options can also break the design due to double minification or the fact that the Rocket Loader is still experimental (you can read about that on the "Help" link under each selected option on cloudflare).
179
 
180
  ...
185
 
186
  The plugins directory is an open source, free service where developers and programmers contribute (on their free time) with plugins that can be downloaded and installed by anyone "at their own risk" and are all released under the GPL license.
187
 
188
+ While all plugins have to be approved and reviewed by the WordPress team before being published ( for dangerous code, spam, etc. ) this doesn't change the license or add any warranty. All plugins are provided as they are, free of charge and should be used at your own risk (so you should make backups before installing any plugin or performing updates) and it's your sole responsibility if you break your site after installing a plugin from the plugins directory.
189
 
190
  Support is provided by plugin authors on their free time and without warranty of a reply, so you can experience different levels of support level from plugin to plugin. As the author of this plugin I strive to provide support on a daily basis and I can take a look and help you with some issues related with my plugin, but please note that this is done out of my goodwill and in no way I have any legal or moral obligation for doing this. I'm also available for hiring if you need custom made speed optimizations (check my profile links).
191
 
195
 
196
  = Where can I get support or report bugs? =
197
 
198
+ You can get support on the official WordPress plugin page at https://wordpress.org/support/plugin/fast-velocity-minify
199
+ You can also see my profile and check my links if you wish to hire me for custom speed optimization on WordPress or extra features.
200
 
201
  ...
202
 
203
  = How can I donate to the plugin author? =
204
 
205
+ If you would like to donate any amount to the plugin author (thank you in advance), you can do it via PayPal at https://goo.gl/vpLrSV
206
 
207
  ...
208
 
209
 
210
+
211
  == Upgrade Notice ==
212
 
213
  = 2.2.4 =
216
 
217
  == Changelog ==
218
 
219
+ = 2.2.7 [2018.02.19] =
220
+ * fixed a bug with the blacklist functionality
221
+ * replaced PHP Minify with JSMin as the default JS minification
222
+ * replaced PHP Minify with CSSTidy as the default CSS minification
223
+ * replaced PHP Minify with Minify HTML as the default HTML minification
224
+ * moved the intermediary cache from transients to disk files
225
+
226
  = 2.2.6 [2018.01.06] =
227
  * fixed a bug with html minification on some files that should not be minified
228
  * fixed a bug with the defer for pagespeed insights