Breeze – WordPress Cache Plugin - Version 1.1.8

Version Description

  • Fix: Cache refresh issue when Varnish is disabled.
  • Fix: Replaced functions deprecated in WordPress 5.5 that were causing warning messages.
  • Fix: Replaced deprecated minification libraries to improve compatibility with PHP 7.x onward.
  • Fix: resolved the warning generated by the Query Monitor plugin.
  • Add: compatibility with PHP 7.4
Download this release

Release Info

Developer adeelkhan
Plugin Icon 128x128 Breeze – WordPress Cache Plugin
Version 1.1.8
Comparing to
See all releases

Code changes from version 1.1.7 to 1.1.8

Files changed (53) hide show
  1. assets/js/breeze-backend.js +1 -1
  2. breeze.php +16 -11
  3. composer.json +5 -0
  4. inc/breeze-configuration.php +14 -14
  5. inc/cache/config-cache.php +4 -0
  6. inc/cache/execute-cache.php +17 -10
  7. inc/cache/purge-per-time.php +9 -3
  8. inc/functions.php +13 -5
  9. inc/helpers.php +3 -3
  10. inc/minification/breeze-js-deferred-loading.php +480 -0
  11. inc/minification/breeze-minification-scripts.php +40 -36
  12. inc/minification/breeze-minify-main.php +26 -11
  13. inc/minification/minify/jsmin-1.1.1.php +0 -291
  14. inc/minification/minify/minify-2.1.7-html.php +0 -257
  15. inc/minification/minify/minify-2.1.7-jsmin.php +0 -447
  16. inc/minification/minify/yui-php-cssmin-2.4.8-4.php +0 -777
  17. readme.txt +10 -3
  18. vendor/autoload.php +7 -0
  19. vendor/bin/minifycss +1 -0
  20. vendor/bin/minifyjs +1 -0
  21. vendor/composer/ClassLoader.php +445 -0
  22. vendor/composer/LICENSE +21 -0
  23. vendor/composer/autoload_classmap.php +9 -0
  24. vendor/composer/autoload_namespaces.php +9 -0
  25. vendor/composer/autoload_psr4.php +11 -0
  26. vendor/composer/autoload_real.php +52 -0
  27. vendor/composer/autoload_static.php +36 -0
  28. vendor/composer/installed.json +115 -0
  29. vendor/matthiasmullie/minify/CONTRIBUTING.md +59 -0
  30. vendor/matthiasmullie/minify/Dockerfile +13 -0
  31. vendor/matthiasmullie/minify/LICENSE +18 -0
  32. vendor/matthiasmullie/minify/bin/minifycss +45 -0
  33. vendor/matthiasmullie/minify/bin/minifyjs +45 -0
  34. vendor/matthiasmullie/minify/composer.json +38 -0
  35. vendor/matthiasmullie/minify/data/js/keywords_after.txt +7 -0
  36. vendor/matthiasmullie/minify/data/js/keywords_before.txt +26 -0
  37. vendor/matthiasmullie/minify/data/js/keywords_reserved.txt +63 -0
  38. vendor/matthiasmullie/minify/data/js/operators.txt +46 -0
  39. vendor/matthiasmullie/minify/data/js/operators_after.txt +43 -0
  40. vendor/matthiasmullie/minify/data/js/operators_before.txt +43 -0
  41. vendor/matthiasmullie/minify/docker-compose.yml +31 -0
  42. vendor/matthiasmullie/minify/src/CSS.php +752 -0
  43. vendor/matthiasmullie/minify/src/Exception.php +20 -0
  44. vendor/matthiasmullie/minify/src/Exceptions/BasicException.php +23 -0
  45. vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php +21 -0
  46. vendor/matthiasmullie/minify/src/Exceptions/IOException.php +21 -0
  47. vendor/matthiasmullie/minify/src/JS.php +612 -0
  48. vendor/matthiasmullie/minify/src/Minify.php +497 -0
  49. vendor/matthiasmullie/path-converter/LICENSE +18 -0
  50. vendor/matthiasmullie/path-converter/composer.json +28 -0
  51. vendor/matthiasmullie/path-converter/src/Converter.php +204 -0
  52. vendor/matthiasmullie/path-converter/src/ConverterInterface.php +24 -0
  53. vendor/matthiasmullie/path-converter/src/NoConverter.php +23 -0
assets/js/breeze-backend.js CHANGED
@@ -94,7 +94,7 @@ jQuery(document).ready(function ($) {
94
  var url = location.href;
95
  var fileClean = parseFloat(getParameterByName('file',url) );
96
 
97
- $( window ).load(function() {
98
  var patt = /wp-admin/i;
99
  if(patt.test(url)){
100
  //backend
94
  var url = location.href;
95
  var fileClean = parseFloat(getParameterByName('file',url) );
96
 
97
+ $( window ).on('load',function() {
98
  var patt = /wp-admin/i;
99
  if(patt.test(url)){
100
  //backend
breeze.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Plugin Name: Breeze
4
  * Description: Breeze is a WordPress cache plugin with extensive options to speed up your website. All the options including Varnish Cache are compatible with Cloudways hosting.
5
- * Version: 1.1.7
6
  * Text Domain: breeze
7
  * Domain Path: /languages
8
  * Author: Cloudways
@@ -12,7 +12,7 @@
12
  */
13
 
14
  /**
15
- * @copyright 2017 Cloudways https://www.cloudways.com
16
  *
17
  * This plugin is inspired from WP Speed of Light by JoomUnited.
18
  *
@@ -31,13 +31,13 @@
31
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
  */
33
 
34
- defined('ABSPATH') || die('No direct script access allowed!');
35
 
36
  if ( ! defined( 'BREEZE_PLUGIN_DIR' ) ) {
37
  define( 'BREEZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
38
  }
39
  if ( ! defined( 'BREEZE_VERSION' ) ) {
40
- define( 'BREEZE_VERSION', '1.1.6' );
41
  }
42
  if ( ! defined( 'BREEZE_SITEURL' ) ) {
43
  define( 'BREEZE_SITEURL', get_site_url() );
@@ -62,7 +62,6 @@ define( 'BREEZE_CACHE_DELAY', true );
62
  define( 'BREEZE_CACHE_NOGZIP', true );
63
  define( 'BREEZE_ROOT_DIR', str_replace( BREEZE_WP_CONTENT_NAME, '', WP_CONTENT_DIR ) );
64
 
65
-
66
  // Compatibility checks
67
  require_once BREEZE_PLUGIN_DIR . 'inc/plugin-incompatibility/class-breeze-incompatibility-plugins.php';
68
 
@@ -95,10 +94,15 @@ if ( is_admin() || 'cli' === php_sapi_name() ) {
95
  }, 0 );
96
 
97
  } else {
98
- $cdn_conf = breeze_get_option( 'cdn_integration' );
99
- $basic_conf = breeze_get_option( 'basic_settings' );
100
-
101
- if ( ! empty( $cdn_conf['cdn-active'] ) || ! empty( $basic_conf['breeze-minify-js'] ) || ! empty( $basic_conf['breeze-minify-css'] ) || ! empty( $basic_conf['breeze-minify-html'] ) ) {
 
 
 
 
 
102
  // Call back ob start
103
  ob_start( 'breeze_ob_start_callback' );
104
  }
@@ -168,8 +172,9 @@ add_action( 'upgrader_process_complete', 'breeze_after_plugin_update_done', 10,
168
 
169
  function breeze_check_for_new_version() {
170
  if ( ! empty( get_option( 'breeze_new_update', '' ) ) ) {
171
- if(class_exists('Breeze_Configuration') && method_exists('Breeze_Configuration','update_htaccess'))
172
- Breeze_Configuration::update_htaccess();
 
173
  delete_option( 'breeze_new_update' );
174
  }
175
  }
2
  /**
3
  * Plugin Name: Breeze
4
  * Description: Breeze is a WordPress cache plugin with extensive options to speed up your website. All the options including Varnish Cache are compatible with Cloudways hosting.
5
+ * Version: 1.1.8
6
  * Text Domain: breeze
7
  * Domain Path: /languages
8
  * Author: Cloudways
12
  */
13
 
14
  /**
15
+ * @copyright 2017 Cloudways https://www.cloudways.com
16
  *
17
  * This plugin is inspired from WP Speed of Light by JoomUnited.
18
  *
31
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
  */
33
 
34
+ defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
35
 
36
  if ( ! defined( 'BREEZE_PLUGIN_DIR' ) ) {
37
  define( 'BREEZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
38
  }
39
  if ( ! defined( 'BREEZE_VERSION' ) ) {
40
+ define( 'BREEZE_VERSION', '1.1.8' );
41
  }
42
  if ( ! defined( 'BREEZE_SITEURL' ) ) {
43
  define( 'BREEZE_SITEURL', get_site_url() );
62
  define( 'BREEZE_CACHE_NOGZIP', true );
63
  define( 'BREEZE_ROOT_DIR', str_replace( BREEZE_WP_CONTENT_NAME, '', WP_CONTENT_DIR ) );
64
 
 
65
  // Compatibility checks
66
  require_once BREEZE_PLUGIN_DIR . 'inc/plugin-incompatibility/class-breeze-incompatibility-plugins.php';
67
 
94
  }, 0 );
95
 
96
  } else {
97
+ $cdn_conf = breeze_get_option( 'cdn_integration' );
98
+ $basic_conf = breeze_get_option( 'basic_settings' );
99
+ $config_advanced = breeze_get_option( 'advanced_settings' );
100
+
101
+ if ( ! empty( $cdn_conf['cdn-active'] )
102
+ || ! empty( $basic_conf['breeze-minify-js'] )
103
+ || ! empty( $basic_conf['breeze-minify-css'] )
104
+ || ! empty( $basic_conf['breeze-minify-html'] )
105
+ || ! empty( $config_advanced['breeze-defer-js'] ) ) {
106
  // Call back ob start
107
  ob_start( 'breeze_ob_start_callback' );
108
  }
172
 
173
  function breeze_check_for_new_version() {
174
  if ( ! empty( get_option( 'breeze_new_update', '' ) ) ) {
175
+ if ( class_exists( 'Breeze_Configuration' ) && method_exists( 'Breeze_Configuration', 'update_htaccess' ) ) {
176
+ Breeze_Configuration::update_htaccess();
177
+ }
178
  delete_option( 'breeze_new_update' );
179
  }
180
  }
composer.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ {
2
+ "require": {
3
+ "matthiasmullie/minify": "^1.3"
4
+ }
5
+ }
inc/breeze-configuration.php CHANGED
@@ -414,7 +414,7 @@ class Breeze_Configuration{
414
  // Loop through caching type rules.
415
  foreach ( $rules as $var_name => $method_name ) {
416
  $has_cache_var = 'has_' . $var_name;
417
- if ( ! $$has_cache_var ) {
418
  // No sites using rules, clean up.
419
  self::$method_name( true );
420
  } else {
@@ -422,7 +422,7 @@ class Breeze_Configuration{
422
  $disabled_sites = 'no_' . $var_name . '_sites';
423
  $regex_string = '';
424
 
425
- if ( empty( $$disabled_sites ) ) {
426
  // Rule is active across sites, do not include conditional directives.
427
  self::$method_name( $clean );
428
  continue;
@@ -430,46 +430,46 @@ class Breeze_Configuration{
430
 
431
  if ( defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) {
432
  // Subdomain sites are matched using host alone.
433
- $regex_string = '%{HTTP_HOST} =~ m#^(' . implode( '|', $$enabled_sites ) . ')#';
434
  } else {
435
  // Subdirectory sites are matched using "THE_REQUEST".
436
  $network_site_url = preg_quote( preg_replace( '(^https?://)', '', untrailingslashit( network_site_url() ) ) );
437
 
438
  // Remove host part from URLs.
439
- $$enabled_sites = array_filter(
440
  array_map(
441
  function( $url ) use ( $network_site_url ) {
442
  $modified = str_replace( $network_site_url, '', $url );
443
  return empty( $modified ) ? '/' : $modified;
444
  },
445
- $$enabled_sites
446
  )
447
  );
448
 
449
- if ( ! empty( $$enabled_sites ) ) {
450
- $regex_string = '%{THE_REQUEST} =~ m#^GET (' . implode( '|', $$enabled_sites ) . ')#';
451
  }
452
 
453
  // Remove main site URL from disabled sites array.
454
- $network_site_url_index = array_search( $network_site_url, $$disabled_sites );
455
  if ( false !== $network_site_url_index ) {
456
- unset( $$disabled_sites[ $network_site_url_index ] );
457
  }
458
  // Remove host part from URLs.
459
- $$disabled_sites = array_filter(
460
  array_map(
461
  function( $url ) use ( $network_site_url ) {
462
  $modified = str_replace( $network_site_url, '', $url );
463
  return empty( $modified ) ? '/' : $modified;
464
  },
465
- $$disabled_sites
466
  )
467
  );
468
- if ( ! empty( $$disabled_sites ) ) {
469
- if ( ! empty( $$enabled_sites ) ) {
470
  $regex_string .= ' && ';
471
  }
472
- $regex_string .= '%{THE_REQUEST} !~ m#^GET (' . implode( '|', $$disabled_sites ) . ')#';
473
  }
474
  }
475
 
414
  // Loop through caching type rules.
415
  foreach ( $rules as $var_name => $method_name ) {
416
  $has_cache_var = 'has_' . $var_name;
417
+ if ( ! ${$has_cache_var} ) {
418
  // No sites using rules, clean up.
419
  self::$method_name( true );
420
  } else {
422
  $disabled_sites = 'no_' . $var_name . '_sites';
423
  $regex_string = '';
424
 
425
+ if ( empty( ${$disabled_sites} ) ) {
426
  // Rule is active across sites, do not include conditional directives.
427
  self::$method_name( $clean );
428
  continue;
430
 
431
  if ( defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) {
432
  // Subdomain sites are matched using host alone.
433
+ $regex_string = '%{HTTP_HOST} =~ m#^(' . implode( '|', ${$enabled_sites} ) . ')#';
434
  } else {
435
  // Subdirectory sites are matched using "THE_REQUEST".
436
  $network_site_url = preg_quote( preg_replace( '(^https?://)', '', untrailingslashit( network_site_url() ) ) );
437
 
438
  // Remove host part from URLs.
439
+ ${$enabled_sites} = array_filter(
440
  array_map(
441
  function( $url ) use ( $network_site_url ) {
442
  $modified = str_replace( $network_site_url, '', $url );
443
  return empty( $modified ) ? '/' : $modified;
444
  },
445
+ ${$enabled_sites}
446
  )
447
  );
448
 
449
+ if ( ! empty( ${$enabled_sites} ) ) {
450
+ $regex_string = '%{THE_REQUEST} =~ m#^GET (' . implode( '|', ${$enabled_sites} ) . ')#';
451
  }
452
 
453
  // Remove main site URL from disabled sites array.
454
+ $network_site_url_index = array_search( $network_site_url, ${$disabled_sites} );
455
  if ( false !== $network_site_url_index ) {
456
+ unset( ${$disabled_sites[ $network_site_url_index ]} );
457
  }
458
  // Remove host part from URLs.
459
+ ${$disabled_sites} = array_filter(
460
  array_map(
461
  function( $url ) use ( $network_site_url ) {
462
  $modified = str_replace( $network_site_url, '', $url );
463
  return empty( $modified ) ? '/' : $modified;
464
  },
465
+ ${$disabled_sites}
466
  )
467
  );
468
+ if ( ! empty( ${$disabled_sites} ) ) {
469
+ if ( ! empty( ${$enabled_sites} ) ) {
470
  $regex_string .= ' && ';
471
  }
472
+ $regex_string .= '%{THE_REQUEST} !~ m#^GET (' . implode( '|', ${$disabled_sites} ) . ')#';
473
  }
474
  }
475
 
inc/cache/config-cache.php CHANGED
@@ -147,6 +147,10 @@ class Breeze_ConfigCache {
147
  'exclude_url' => array(),
148
  );
149
 
 
 
 
 
150
  if( class_exists('WooCommerce')){
151
  $ecommerce_exclude_urls = Breeze_Ecommerce_Cache::factory()->ecommerce_exclude_pages();
152
  }
147
  'exclude_url' => array(),
148
  );
149
 
150
+ if(is_multisite()){
151
+ $storage['blog_id'] = get_current_blog_id();
152
+ }
153
+
154
  if( class_exists('WooCommerce')){
155
  $ecommerce_exclude_urls = Breeze_Ecommerce_Cache::factory()->ecommerce_exclude_pages();
156
  }
inc/cache/execute-cache.php CHANGED
@@ -35,14 +35,16 @@ if ( isset( $_GET['debug_config'] ) ) {
35
  var_dump( $GLOBALS['breeze_config'] );
36
  exit;
37
  }
38
-
39
- $url_path = breeze_get_url_path();
40
- $user_logged = false;
41
 
42
  if ( substr_count( $url_path, '?' ) > 0 ) {
43
- $filename = $url_path . '&guest';
 
44
  } else {
45
- $filename = $url_path . '?guest';
 
46
  }
47
 
48
  // Don't cache
@@ -155,7 +157,8 @@ function breeze_cache( $buffer, $flags ) {
155
  }
156
  $url_path = breeze_get_url_path();
157
 
158
- $cache_base_path = breeze_get_cache_base_path();
 
159
 
160
  $path = $cache_base_path . md5( $url_path );
161
 
@@ -211,7 +214,8 @@ function breeze_cache( $buffer, $flags ) {
211
  $url_path .= $current_user->user_login;
212
  }
213
  } else {
214
- $url_path .= 'guest';
 
215
  }
216
  $devices = $GLOBALS['breeze_config']['cache_options'];
217
  // Detect devices
@@ -296,10 +300,13 @@ function breeze_serve_cache( $filename, $url_path, $X1, $opts ) {
296
  $file_name = md5( $filename . '/index.html' ) . '.php';
297
  }
298
 
 
 
299
 
300
- $path = breeze_get_cache_base_path() . md5( $url_path ) . '/' . $file_name;
301
-
302
- $modified_time = (int) @filemtime( $path );
 
303
 
304
  if ( ! empty( $opts['breeze-browser-cache'] ) && ! empty( $modified_time ) && ! empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) && strtotime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) === $modified_time ) {
305
  header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 );
35
  var_dump( $GLOBALS['breeze_config'] );
36
  exit;
37
  }
38
+ $filename_guest_suffix = '';
39
+ $url_path = breeze_get_url_path();
40
+ $user_logged = false;
41
 
42
  if ( substr_count( $url_path, '?' ) > 0 ) {
43
+ $filename = $url_path . '&guest';
44
+ $filename_guest_suffix = '&guest';
45
  } else {
46
+ $filename = $url_path . '?guest';
47
+ $filename_guest_suffix = '?guest';
48
  }
49
 
50
  // Don't cache
157
  }
158
  $url_path = breeze_get_url_path();
159
 
160
+ $blog_id_requested = isset( $GLOBALS['breeze_config']['blog_id'] ) ? $GLOBALS['breeze_config']['blog_id'] : 0;
161
+ $cache_base_path = breeze_get_cache_base_path( false, $blog_id_requested );
162
 
163
  $path = $cache_base_path . md5( $url_path );
164
 
214
  $url_path .= $current_user->user_login;
215
  }
216
  } else {
217
+ global $filename_guest_suffix;
218
+ $url_path .= $filename_guest_suffix;
219
  }
220
  $devices = $GLOBALS['breeze_config']['cache_options'];
221
  // Detect devices
300
  $file_name = md5( $filename . '/index.html' ) . '.php';
301
  }
302
 
303
+ $blog_id_requested = isset( $GLOBALS['breeze_config']['blog_id'] ) ? $GLOBALS['breeze_config']['blog_id'] : 0;
304
+ $path = breeze_get_cache_base_path(false, $blog_id_requested) . md5( $url_path ) . '/' . $file_name;
305
 
306
+ $modified_time = 0;
307
+ if ( file_exists( $path ) ) {
308
+ $modified_time = (int) @filemtime( $path );
309
+ }
310
 
311
  if ( ! empty( $opts['breeze-browser-cache'] ) && ! empty( $modified_time ) && ! empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) && strtotime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) === $modified_time ) {
312
  header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 );
inc/cache/purge-per-time.php CHANGED
@@ -26,11 +26,17 @@ class Breeze_PurgeCacheTime {
26
  protected $varnishcache = 0;
27
  public function __construct($settings = null)
28
  {
29
- $this->timettl = $settings['breeze-ttl'];
 
 
30
 
31
- $this->normalcache = (int)$settings['breeze-active'];
 
 
32
 
33
- $this->varnishcache = (int)$settings['breeze-varnish-purge'];
 
 
34
 
35
  add_action( 'breeze_purge_cache', array( $this, 'schedule_varnish' ) );
36
  add_action( 'init', array( $this, 'schedule_events' ) );
26
  protected $varnishcache = 0;
27
  public function __construct($settings = null)
28
  {
29
+ if ( isset( $settings['breeze-ttl'] ) ) {
30
+ $this->timettl = $settings['breeze-ttl'];
31
+ }
32
 
33
+ if ( isset( $settings['breeze-active'] ) ) {
34
+ $this->normalcache = (int) $settings['breeze-active'];
35
+ }
36
 
37
+ if ( isset( $settings['breeze-varnish-purge'] ) ) {
38
+ $this->varnishcache = (int) $settings['breeze-varnish-purge'];
39
+ }
40
 
41
  add_action( 'breeze_purge_cache', array( $this, 'schedule_varnish' ) );
42
  add_action( 'init', array( $this, 'schedule_events' ) );
inc/functions.php CHANGED
@@ -23,17 +23,25 @@ defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
23
  *
24
  * @param bool $is_network Whether to include the blog ID in the path on multisite.
25
  *
 
 
26
  * @return string
27
  */
28
- function breeze_get_cache_base_path( $is_network = false ) {
29
 
30
  if ( ! $is_network && is_multisite() ) {
31
- global $blog_id;
32
- $path = rtrim( WP_CONTENT_DIR, '/\\' ) . '/cache/breeze/';
33
 
34
- if ( ! empty( $blog_id ) ) {
35
- $path .= abs( intval( $blog_id ) ) . DIRECTORY_SEPARATOR;
 
 
 
 
 
 
 
36
  }
 
37
  } else {
38
  $path = rtrim( WP_CONTENT_DIR, '/\\' ) . '/cache/breeze/';
39
  }
23
  *
24
  * @param bool $is_network Whether to include the blog ID in the path on multisite.
25
  *
26
+ * @param int $blog_id_requested Folder for specific blog ID.
27
+ *
28
  * @return string
29
  */
30
+ function breeze_get_cache_base_path( $is_network = false, $blog_id_requested = 0 ) {
31
 
32
  if ( ! $is_network && is_multisite() ) {
 
 
33
 
34
+ if ( empty( $blog_id_requested ) ) {
35
+ global $blog_id;
36
+ $path = rtrim( WP_CONTENT_DIR, '/\\' ) . '/cache/breeze/';
37
+ if ( ! empty( $blog_id ) ) {
38
+ $path .= abs( intval( $blog_id ) ) . DIRECTORY_SEPARATOR;
39
+ }
40
+ } else {
41
+ $path = rtrim( WP_CONTENT_DIR, '/\\' ) . '/cache/breeze/';
42
+ $path .= abs( intval( $blog_id_requested ) ) . DIRECTORY_SEPARATOR;
43
  }
44
+
45
  } else {
46
  $path = rtrim( WP_CONTENT_DIR, '/\\' ) . '/cache/breeze/';
47
  }
inc/helpers.php CHANGED
@@ -409,8 +409,9 @@ function is_varnish_cache_started( $retry = 1, $time_fresh = 0, $use_headers = f
409
  'verify_peer' => false,
410
  ),
411
  );
412
- $context = stream_context_create( $context_options );
413
- $headers = get_headers( $url_ping, 1, $context );
 
414
 
415
  if ( empty( $headers ) ) {
416
  $use_headers = false;
@@ -561,7 +562,6 @@ function is_varnish_layer_started() {
561
  return false;
562
  }
563
 
564
-
565
  return true;
566
  }
567
 
409
  'verify_peer' => false,
410
  ),
411
  );
412
+
413
+ stream_context_set_default( $context_options );
414
+ $headers = get_headers( $url_ping, 1 );
415
 
416
  if ( empty( $headers ) ) {
417
  $use_headers = false;
562
  return false;
563
  }
564
 
 
565
  return true;
566
  }
567
 
inc/minification/breeze-js-deferred-loading.php ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ header( 'Status: 403 Forbidden' );
4
+ header( 'HTTP/1.1 403 Forbidden' );
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * When minification is not enabled but there are scripts into deferred loading option
10
+ * that need to be handles.
11
+ *
12
+ * Class Breeze_Js_Deferred_Loading
13
+ * @since 1.1.8
14
+ */
15
+ class Breeze_Js_Deferred_Loading extends Breeze_MinificationBase {
16
+
17
+ /**
18
+ * Javascript URLs that need the defer tag.
19
+ *
20
+ * @var array
21
+ * @since 1.1.8
22
+ * @access private
23
+ */
24
+ private $defer_js = array();
25
+
26
+ /**
27
+ * Will hold the JS Scripts found in the header
28
+ * @var array
29
+ * @since 1.1.8
30
+ * @access private
31
+ */
32
+ private $head_scripts = array();
33
+
34
+ /**
35
+ * Will hold the JS Scripts found in the body/footer
36
+ * @var array
37
+ * @since 1.1.8
38
+ * @access private
39
+ */
40
+ private $footer_scripts = array();
41
+
42
+ /**
43
+ * Will hold scripts that need to be removed.
44
+ * @var array
45
+ * @since 1.1.8
46
+ * @access private
47
+ */
48
+ private $jsremovables = array();
49
+
50
+ /**
51
+ * Holds scripts that need to be moved to the footer.
52
+ *
53
+ * @var array
54
+ * @since 1.1.8
55
+ * @access private
56
+ */
57
+ private $move_to_footer_js = array();
58
+
59
+ /**
60
+ * Prepared scripts that need to be moved to footer.
61
+ *
62
+ * @var array
63
+ * @since 1.1.8
64
+ * @access private
65
+ */
66
+ private $move_to_footer = array();
67
+
68
+ /**
69
+ * Files first or last.
70
+ *
71
+ * @var array[]
72
+ * @since 1.1.8
73
+ * @access private
74
+ */
75
+ private $move = array(
76
+ 'first' => array(),
77
+ 'last' => array(),
78
+ );
79
+
80
+ private $domove = array(
81
+ 'gaJsHost',
82
+ 'load_cmc',
83
+ 'jd.gallery.transitions.js',
84
+ 'swfobject.embedSWF(',
85
+ 'tiny_mce.js',
86
+ 'tinyMCEPreInit.go'
87
+ );
88
+
89
+ private $domovelast = array(
90
+ 'addthis.com',
91
+ '/afsonline/show_afs_search.js',
92
+ 'disqus.js',
93
+ 'networkedblogs.com/getnetworkwidget',
94
+ 'infolinks.com/js/',
95
+ 'jd.gallery.js.php',
96
+ 'jd.gallery.transitions.js',
97
+ 'swfobject.embedSWF(',
98
+ 'linkwithin.com/widget.js',
99
+ 'tiny_mce.js',
100
+ 'tinyMCEPreInit.go',
101
+ );
102
+
103
+ private $dontmove = array(
104
+ 'gtag',
105
+ 'document.write',
106
+ 'html5.js',
107
+ 'show_ads.js',
108
+ 'google_ad',
109
+ 'blogcatalog.com/w',
110
+ 'tweetmeme.com/i',
111
+ 'mybloglog.com/',
112
+ 'histats.com/js',
113
+ 'ads.smowtion.com/ad.js',
114
+ 'statcounter.com/counter/counter.js',
115
+ 'widgets.amung.us',
116
+ 'ws.amazon.com/widgets',
117
+ 'media.fastclick.net',
118
+ '/ads/',
119
+ 'comment-form-quicktags/quicktags.php',
120
+ 'edToolbar',
121
+ 'intensedebate.com',
122
+ 'scripts.chitika.net/',
123
+ '_gaq.push',
124
+ 'jotform.com/',
125
+ 'admin-bar.min.js',
126
+ 'GoogleAnalyticsObject',
127
+ 'plupload.full.min.js',
128
+ 'syntaxhighlighter',
129
+ 'adsbygoogle',
130
+ 'gist.github.com',
131
+ '_stq',
132
+ 'nonce',
133
+ 'post_id',
134
+ 'data-noptimize',
135
+ );
136
+
137
+ /**
138
+ * Reads the page content and fetches the JavaScript script tags.
139
+ *
140
+ * @param array $options Script options.
141
+ *
142
+ * @return bool
143
+ * @since 1.1.8
144
+ * @access public
145
+ */
146
+ public function read( $options = array() ) {
147
+
148
+ // Read the list of scripts that need defer tag.
149
+ if ( ! empty( $options['defer_js'] ) ) {
150
+ $this->defer_js = $options['defer_js'];
151
+ }
152
+
153
+ // JS files will move to footer
154
+ if ( ! empty( $options['move_to_footer_js'] ) ) {
155
+ $this->move_to_footer_js = $options['move_to_footer_js'];
156
+ }
157
+
158
+ // is there JS we should simply remove
159
+ $removableJS = apply_filters( 'breeze_filter_js_removables', '' );
160
+ if ( ! empty( $removableJS ) ) {
161
+ $this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removableJS ) ) );
162
+ }
163
+
164
+ // noptimize me
165
+ $this->content = $this->hide_noptimize( $this->content );
166
+ // Save IE hacks
167
+ $this->content = $this->hide_iehacks( $this->content );
168
+ // comments
169
+ $this->content = $this->hide_comments( $this->content );
170
+
171
+ //Get script files
172
+ $split_content = explode( '</head>', $this->content, 2 );
173
+ $this->fetch_javascript( $split_content[0] );
174
+ $this->fetch_javascript( $split_content[1], false );
175
+
176
+ if ( ! empty( $this->head_scripts ) || ! empty( $this->footer_scripts ) ) {
177
+ // Re-order moving to footer JS files
178
+ $ordered_moving_js = array_intersect_key( $this->move_to_footer_js, $this->move_to_footer );
179
+ $ordered_moving_js = array_map( array( $this, 'getpath' ), $ordered_moving_js );
180
+ $this->footer_scripts = array_merge( $ordered_moving_js, $this->footer_scripts );
181
+
182
+ // JS Scripts found, wen can start processing them.
183
+ return true;
184
+ }
185
+
186
+ // The page holds no JS scripts
187
+ return false;
188
+ }
189
+
190
+ /**
191
+ * Returns the found javascript
192
+ *
193
+ * @param string $content HTML content
194
+ * @param bool $head to process header or not.
195
+ *
196
+ * @return bool
197
+ * @since 1.1.8
198
+ * @access private
199
+ */
200
+ private function fetch_javascript( $content = '', $head = true ) {
201
+ if ( preg_match_all( '#<script.*</script>#Usmi', $content, $matches ) ) {
202
+ foreach ( $matches[0] as $tag ) {
203
+ // only consider aggregation whitelisted in should_aggregate-function
204
+ if ( ! $this->should_aggregate( $tag ) ) {
205
+ $tag = '';
206
+ continue;
207
+ }
208
+
209
+ // handle only the scripts that have the a file as source.
210
+ if ( preg_match( '/\ssrc=("|\')?(.*(\ |\>))("|\')?/Usmi', $tag, $source ) ) {
211
+ $source[2] = substr( $source[2], 0, - 1 );
212
+ if ( $this->isremovable( $tag, $this->jsremovables ) ) {
213
+ $content = str_replace( $tag, '', $content );
214
+ continue;
215
+ }
216
+
217
+ // External script
218
+ $url = current( explode( '?', $source[2], 2 ) );
219
+ if ( $url[0] == "'" || $url[0] == '"' ) {
220
+ $url = substr( $url, 1 );
221
+ }
222
+ if ( $url[ strlen( $url ) - 1 ] == '"' || $url[ strlen( $url ) - 1 ] == "'" ) {
223
+ $url = substr( $url, 0, - 1 );
224
+ }
225
+
226
+ $path = $this->getpath( $url );
227
+ if ( $path !== false && preg_match( '#\.js$#', $path ) ) {
228
+
229
+ if ( $this->is_merge_valid( $tag ) ) {
230
+ //We can merge it
231
+ if ( true === $head ) {
232
+ // If this file will be move to footer
233
+ if ( in_array( $url, $this->move_to_footer_js ) ) {
234
+ $this->move_to_footer[ $url ] = $path;
235
+ } else {
236
+ $this->head_scripts[ $url ] = $path;
237
+ }
238
+ } else {
239
+ $this->footer_scripts[ $url ] = $path;
240
+ }
241
+ } else {
242
+ //No merge, but maybe we can move it
243
+ if ( $this->is_movable( $tag ) ) {
244
+ //Yeah, move it
245
+ if ( $this->move_to_last( $tag ) ) {
246
+ $this->move['last'][] = $tag;
247
+ } else {
248
+ $this->move['first'][] = $tag;
249
+ }
250
+ } else {
251
+ //We shouldn't touch this
252
+ $tag = '';
253
+ }
254
+ }
255
+ } else {
256
+ if ( breeze_validate_url_via_regexp( $url ) ) {
257
+
258
+ if ( true === $head ) {
259
+
260
+ if ( in_array( $url, $this->move_to_footer_js ) ) {
261
+ $this->move_to_footer[ $url ] = $url;
262
+ } else {
263
+ $this->head_scripts[ $url ] = $url;
264
+ }
265
+ } else {
266
+ $this->footer_scripts[ $url ] = $url;
267
+ }
268
+ }
269
+ }
270
+ //Remove the original script tag
271
+ $content = str_replace( $tag, '', $content );
272
+ }
273
+ }
274
+ }
275
+
276
+ if ( true === $head ) {
277
+ $this->content = $content;
278
+ } else {
279
+ $this->content .= '</head>' . $content;
280
+ }
281
+
282
+ return true;
283
+ }
284
+
285
+ /**
286
+ * Needed function to match Breeze_MinificationBase class pattern
287
+ *
288
+ * @since 1.1.8
289
+ * @access public
290
+ */
291
+ public function minify() {
292
+
293
+ return true;
294
+ }
295
+
296
+ /**
297
+ * Needed function to match Breeze_MinificationBase class pattern
298
+ *
299
+ * @since 1.1.8
300
+ * @access public
301
+ */
302
+ public function cache() {
303
+
304
+ return true;
305
+ }
306
+
307
+ /**
308
+ * Needed function to match Breeze_MinificationBase class pattern
309
+ *
310
+ * @since 1.1.8
311
+ * @access public
312
+ */
313
+ public function getcontent() {
314
+
315
+ // Load inline JS to html
316
+ if ( ! empty( $this->head_scripts ) ) {
317
+
318
+ $replaceTag = array( '</head>', 'before' );
319
+ $js_head = array();
320
+
321
+ foreach ( $this->head_scripts as $js_url => $js_path ) {
322
+ $defer = '';
323
+ if ( gettype( $js_url ) == 'string' && in_array( $js_url, $this->defer_js ) ) {
324
+ $defer = 'defer ';
325
+ }
326
+
327
+ $js_head[] = "<script type='application/javascript' {$defer}src='{$js_url}'></script>\n";
328
+ }
329
+ $js_replacement = '';
330
+ $js_replacement .= implode( '', $js_head );
331
+ $this->inject_in_html( $js_replacement, $replaceTag );
332
+ }
333
+
334
+ if ( ! empty( $this->footer_scripts ) ) {
335
+ $replaceTag = array( '</body>', 'before' );
336
+ $js_footer = array();
337
+
338
+ foreach ( $this->footer_scripts as $js_url => $js_path ) {
339
+ $defer = '';
340
+ if ( gettype( $js_url ) == 'string' && in_array( $js_url, $this->defer_js ) ) {
341
+ $defer = 'defer ';
342
+ }
343
+
344
+ $js_footer[] = "<script type='application/javascript' {$defer}src='{$js_url}'></script>\n";
345
+ }
346
+ $js_replacement = '';
347
+ $js_replacement .= implode( '', $js_footer );
348
+ $this->inject_in_html( $js_replacement, $replaceTag );
349
+ }
350
+
351
+
352
+ // restore comments
353
+ $this->content = $this->restore_comments( $this->content );
354
+
355
+ // Restore IE hacks
356
+ $this->content = $this->restore_iehacks( $this->content );
357
+
358
+ // Restore noptimize
359
+ $this->content = $this->restore_noptimize( $this->content );
360
+
361
+ return $this->content;
362
+ }
363
+
364
+
365
+ // Checks against the white- and blacklists
366
+ private function is_merge_valid( $tag ) {
367
+ if ( ! empty( $this->whitelist ) ) {
368
+ foreach ( $this->whitelist as $match ) {
369
+ if ( strpos( $tag, $match ) !== false ) {
370
+ return true;
371
+ }
372
+ }
373
+
374
+ // no match with whitelist
375
+ return false;
376
+ } else {
377
+ foreach ( $this->domove as $match ) {
378
+ if ( strpos( $tag, $match ) !== false ) {
379
+ //Matched something
380
+ return false;
381
+ }
382
+ }
383
+
384
+ if ( $this->move_to_last( $tag ) ) {
385
+ return false;
386
+ }
387
+
388
+ foreach ( $this->dontmove as $match ) {
389
+ if ( strpos( $tag, $match ) !== false ) {
390
+ //Matched something
391
+ return false;
392
+ }
393
+ }
394
+
395
+ // If we're here it's safe to merge
396
+ return true;
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Check if the script can be moved
402
+ *
403
+ * @param $tag
404
+ *
405
+ * @return bool
406
+ * @since 1.1.8
407
+ * @access private
408
+ */
409
+ private function is_movable( $tag ) {
410
+
411
+ foreach ( $this->domove as $match ) {
412
+ if ( strpos( $tag, $match ) !== false ) {
413
+ //Matched something
414
+ return true;
415
+ }
416
+ }
417
+
418
+ if ( $this->move_to_last( $tag ) ) {
419
+ return true;
420
+ }
421
+
422
+ foreach ( $this->dontmove as $match ) {
423
+ if ( strpos( $tag, $match ) !== false ) {
424
+ //Matched something
425
+ return false;
426
+ }
427
+ }
428
+
429
+ //If we're here it's safe to move
430
+ return true;
431
+ }
432
+
433
+ /**
434
+ * Move the script last
435
+ *
436
+ * @param $tag
437
+ *
438
+ * @return bool
439
+ * @since 1.1.8
440
+ * @access private
441
+ */
442
+ private function move_to_last( $tag ) {
443
+ foreach ( $this->domovelast as $match ) {
444
+ if ( strpos( $tag, $match ) !== false ) {
445
+ //Matched, return true
446
+ return true;
447
+ }
448
+ }
449
+
450
+ //Should be in 'first'
451
+ return false;
452
+ }
453
+
454
+ /**
455
+ * Determines whether a <script> $tag should be aggregated or not.
456
+ *
457
+ * We consider these as "aggregation-safe" currently:
458
+ * - script tags without a `type` attribute
459
+ * - script tags with an explicit `type` of `text/javascript`, 'text/ecmascript',
460
+ * 'application/javascript' or 'application/ecmascript'
461
+ *
462
+ * Everything else should return false.
463
+ *
464
+ * @param string $tag
465
+ *
466
+ * @return bool
467
+ *
468
+ * original function by https://github.com/zytzagoo/ on his AO fork, thanks Tomas!
469
+ */
470
+ public function should_aggregate( $tag ) {
471
+ preg_match( '#<(script[^>]*)>#i', $tag, $scripttag );
472
+ if ( strpos( $scripttag[1], 'type=' ) === false ) {
473
+ return true;
474
+ } elseif ( preg_match( '/type=["\']?(?:text|application)\/(?:javascript|ecmascript)["\']?/i', $scripttag[1] ) ) {
475
+ return true;
476
+ } else {
477
+ return false;
478
+ }
479
+ }
480
+ }
inc/minification/breeze-minification-scripts.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
- /*
 
 
3
  * Based on some work of autoptimize plugin
4
  */
5
  if ( ! defined( 'ABSPATH' ) ) {
@@ -449,14 +451,14 @@ class Breeze_MinificationScripts extends Breeze_MinificationBase {
449
 
450
  if ( $this->alreadyminified !== true ) {
451
 
452
- if ( class_exists( 'JSMin' ) && apply_filters( 'breeze_js_do_minify', true ) ) {
453
- if ( @is_callable( array( 'JSMin', 'minify' ) ) ) {
 
 
454
 
455
- $tmp_jscode = trim( JSMin::minify( $this->full_script ) );
456
- if ( ! empty( $tmp_jscode ) ) {
457
- $this->full_script = $tmp_jscode;
458
- unset( $tmp_jscode );
459
- }
460
  }
461
  }
462
 
@@ -468,13 +470,14 @@ class Breeze_MinificationScripts extends Breeze_MinificationBase {
468
  if ( ! empty( $this->js_head_group ) ) {
469
  foreach ( $this->js_head_group as $jscode ) {
470
  //$this->jscode has all the uncompressed code now.
471
- if ( class_exists( 'JSMin' ) && apply_filters( 'breeze_js_do_minify', true ) ) {
472
- if ( @is_callable( array( 'JSMin', 'minify' ) ) ) {
473
- $tmp_jscode = trim( JSMin::minify( $jscode ) );
474
- if ( ! empty( $tmp_jscode ) ) {
475
- $jscode = $tmp_jscode;
476
- unset( $tmp_jscode );
477
- }
 
478
  }
479
  }
480
 
@@ -485,13 +488,14 @@ class Breeze_MinificationScripts extends Breeze_MinificationBase {
485
  if ( ! empty( $this->js_footer_group ) ) {
486
  foreach ( $this->js_footer_group as $jscode ) {
487
  //$this->jscode has all the uncompressed code now.
488
- if ( class_exists( 'JSMin' ) && apply_filters( 'breeze_js_do_minify', true ) ) {
489
- if ( @is_callable( array( 'JSMin', 'minify' ) ) ) {
490
- $tmp_jscode = trim( JSMin::minify( $jscode ) );
491
- if ( ! empty( $tmp_jscode ) ) {
492
- $jscode = $tmp_jscode;
493
- unset( $tmp_jscode );
494
- }
 
495
  }
496
  }
497
 
@@ -519,14 +523,14 @@ class Breeze_MinificationScripts extends Breeze_MinificationBase {
519
  unset( $ccheck );
520
 
521
  //$this->jscode has all the uncompressed code now.
 
 
 
 
522
 
523
- if ( class_exists( 'JSMin' ) && apply_filters( 'breeze_js_do_minify', true ) ) {
524
- if ( @is_callable( array( 'JSMin', 'minify' ) ) ) {
525
- $tmp_jscode = trim( JSMin::minify( $jscode ) );
526
- if ( ! empty( $tmp_jscode ) ) {
527
- $jscode = $tmp_jscode;
528
- unset( $tmp_jscode );
529
- }
530
  }
531
  }
532
 
@@ -548,14 +552,14 @@ class Breeze_MinificationScripts extends Breeze_MinificationBase {
548
  unset( $ccheck );
549
 
550
  //$this->jscode has all the uncompressed code now.
 
 
 
 
551
 
552
- if ( class_exists( 'JSMin' ) && apply_filters( 'breeze_js_do_minify', true ) ) {
553
- if ( @is_callable( array( 'JSMin', 'minify' ) ) ) {
554
- $tmp_jscode = trim( JSMin::minify( $jscode ) );
555
- if ( ! empty( $tmp_jscode ) ) {
556
- $jscode = $tmp_jscode;
557
- unset( $tmp_jscode );
558
- }
559
  }
560
  }
561
 
1
  <?php
2
+
3
+ use MatthiasMullie\Minify;
4
+ /*
5
  * Based on some work of autoptimize plugin
6
  */
7
  if ( ! defined( 'ABSPATH' ) ) {
451
 
452
  if ( $this->alreadyminified !== true ) {
453
 
454
+ if ( apply_filters( 'breeze_js_do_minify', true ) && ! empty( $this->full_script ) ) {
455
+ $minifier = new Minify\JS();
456
+ $minifier->add( $this->full_script );
457
+ $tmp_jscode = $minifier->minify();
458
 
459
+ if ( ! empty( $tmp_jscode ) ) {
460
+ $this->full_script = trim( $tmp_jscode );
461
+ unset( $tmp_jscode );
 
 
462
  }
463
  }
464
 
470
  if ( ! empty( $this->js_head_group ) ) {
471
  foreach ( $this->js_head_group as $jscode ) {
472
  //$this->jscode has all the uncompressed code now.
473
+ if ( apply_filters( 'breeze_js_do_minify', true ) ) {
474
+ $minifier = new Minify\JS();
475
+ $minifier->add( $jscode );
476
+ $tmp_jscode = $minifier->minify();
477
+
478
+ if ( ! empty( $tmp_jscode ) ) {
479
+ $jscode = $tmp_jscode;
480
+ unset( $tmp_jscode );
481
  }
482
  }
483
 
488
  if ( ! empty( $this->js_footer_group ) ) {
489
  foreach ( $this->js_footer_group as $jscode ) {
490
  //$this->jscode has all the uncompressed code now.
491
+ if ( apply_filters( 'breeze_js_do_minify', true ) ) {
492
+ $minifier = new Minify\JS();
493
+ $minifier->add( $jscode );
494
+ $tmp_jscode = $minifier->minify();
495
+
496
+ if ( ! empty( $tmp_jscode ) ) {
497
+ $jscode = $tmp_jscode;
498
+ unset( $tmp_jscode );
499
  }
500
  }
501
 
523
  unset( $ccheck );
524
 
525
  //$this->jscode has all the uncompressed code now.
526
+ if( apply_filters( 'breeze_js_do_minify', true ) ){
527
+ $minifier = new Minify\JS();
528
+ $minifier->add( $jscode );
529
+ $tmp_jscode = $minifier->minify();
530
 
531
+ if ( ! empty( $tmp_jscode ) ) {
532
+ $jscode = $tmp_jscode;
533
+ unset( $tmp_jscode );
 
 
 
 
534
  }
535
  }
536
 
552
  unset( $ccheck );
553
 
554
  //$this->jscode has all the uncompressed code now.
555
+ if( apply_filters( 'breeze_js_do_minify', true ) ){
556
+ $minifier = new Minify\JS();
557
+ $minifier->add( $jscode );
558
+ $tmp_jscode = $minifier->minify();
559
 
560
+ if ( ! empty( $tmp_jscode ) ) {
561
+ $jscode = $tmp_jscode;
562
+ unset( $tmp_jscode );
 
 
 
 
563
  }
564
  }
565
 
inc/minification/breeze-minify-main.php CHANGED
@@ -35,7 +35,8 @@ class Breeze_Minify {
35
  //cache minification
36
  if ( Breeze_MinificationCache::create_cache_minification_folder() ) {
37
  $conf = breeze_get_option( 'basic_settings' );
38
- if ( ! empty( $conf['breeze-minify-html'] ) || ! empty( $conf['breeze-minify-css'] ) || ! empty( $conf['breeze-minify-js'] ) ) {
 
39
 
40
  if ( defined( 'breeze_INIT_EARLIER' ) ) {
41
  add_action( 'init', array( $this, 'breeze_start_buffering' ), - 1 );
@@ -65,6 +66,7 @@ class Breeze_Minify {
65
  */
66
  public function breeze_start_buffering() {
67
  $ao_noptimize = false;
 
68
  // check for DONOTMINIFY constant as used by e.g. WooCommerce POS
69
  if ( defined( 'DONOTMINIFY' ) && ( constant( 'DONOTMINIFY' ) === true || constant( 'DONOTMINIFY' ) === "true" ) ) {
70
  $ao_noptimize = true;
@@ -73,7 +75,9 @@ class Breeze_Minify {
73
  $ao_noptimize = (bool) apply_filters( 'breeze_filter_noptimize', $ao_noptimize );
74
  if ( ! is_feed() && ! $ao_noptimize && ! is_admin() ) {
75
  // Config element
76
- $conf = breeze_get_option( 'basic_settings' );
 
 
77
  // Load our base class
78
  include_once( BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-base.php' );
79
 
@@ -86,22 +90,25 @@ class Breeze_Minify {
86
  }
87
  }
88
 
 
89
  if ( ! empty( $conf['breeze-minify-js'] ) ) {
 
90
  include_once( BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-scripts.php' );
91
- if ( ! class_exists( 'JSMin' ) ) {
92
- if ( defined( 'breeze_LEGACY_MINIFIERS' ) ) {
93
- @include( BREEZE_PLUGIN_DIR . 'inc/minification/minify/jsmin-1.1.1.php' );
94
- } else {
95
- @include( BREEZE_PLUGIN_DIR . 'inc/minification/minify/minify-2.1.7-jsmin.php' );
96
- }
97
- }
98
  if ( ! defined( 'CONCATENATE_SCRIPTS' ) ) {
99
  define( 'CONCATENATE_SCRIPTS', false );
100
  }
101
  if ( ! defined( 'COMPRESS_SCRIPTS' ) ) {
102
  define( 'COMPRESS_SCRIPTS', false );
103
  }
 
 
 
104
  }
 
105
  if ( ! empty( $conf['breeze-minify-css'] ) ) {
106
  include_once( BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-styles.php' );
107
  if ( defined( 'breeze_LEGACY_MINIFIERS' ) ) {
@@ -117,7 +124,6 @@ class Breeze_Minify {
117
  define( 'COMPRESS_CSS', false );
118
  }
119
  }
120
-
121
  // Now, start the real thing!
122
  add_filter( 'breeze_minify_content_return', array( $this, 'breeze_end_buffering' ) );
123
  }
@@ -128,6 +134,7 @@ class Breeze_Minify {
128
  */
129
 
130
  public function breeze_end_buffering( $content ) {
 
131
  if ( stripos( $content, "<html" ) === false || stripos( $content, "<html amp" ) !== false || stripos( $content, "<html ⚡" ) !== false || stripos( $content, "<xsl:stylesheet" ) !== false ) {
132
  return $content;
133
  }
@@ -157,7 +164,10 @@ class Breeze_Minify {
157
  $js_include_inline = $css_include_inline = false;
158
  if ( ! empty( $conf['breeze-minify-js'] ) ) {
159
  $classes[] = 'Breeze_MinificationScripts';
 
 
160
  }
 
161
  if ( ! empty( $conf['breeze-minify-css'] ) ) {
162
  $classes[] = 'Breeze_MinificationStyles';
163
  }
@@ -209,7 +219,11 @@ class Breeze_Minify {
209
  ),
210
  'Breeze_MinificationHtml' => array(
211
  'keepcomments' => false
212
- )
 
 
 
 
213
  );
214
 
215
  $content = apply_filters( 'breeze_filter_html_before_minify', $content );
@@ -220,6 +234,7 @@ class Breeze_Minify {
220
  } else {
221
  // Run the classes
222
  foreach ( $classes as $name ) {
 
223
  $do_process = false;
224
  $instance = new $name( $content );
225
  if ( 'Breeze_MinificationStyles' === $name ) {
35
  //cache minification
36
  if ( Breeze_MinificationCache::create_cache_minification_folder() ) {
37
  $conf = breeze_get_option( 'basic_settings' );
38
+ $config_advanced = breeze_get_option( 'advanced_settings' );
39
+ if ( ! empty( $conf['breeze-minify-html'] ) || ! empty( $conf['breeze-minify-css'] ) || ! empty( $conf['breeze-minify-js'] ) || ! empty( $config_advanced['breeze-defer-js'] )) {
40
 
41
  if ( defined( 'breeze_INIT_EARLIER' ) ) {
42
  add_action( 'init', array( $this, 'breeze_start_buffering' ), - 1 );
66
  */
67
  public function breeze_start_buffering() {
68
  $ao_noptimize = false;
69
+
70
  // check for DONOTMINIFY constant as used by e.g. WooCommerce POS
71
  if ( defined( 'DONOTMINIFY' ) && ( constant( 'DONOTMINIFY' ) === true || constant( 'DONOTMINIFY' ) === "true" ) ) {
72
  $ao_noptimize = true;
75
  $ao_noptimize = (bool) apply_filters( 'breeze_filter_noptimize', $ao_noptimize );
76
  if ( ! is_feed() && ! $ao_noptimize && ! is_admin() ) {
77
  // Config element
78
+ $conf = breeze_get_option( 'basic_settings' );
79
+ $config_advanced = breeze_get_option( 'advanced_settings' );
80
+
81
  // Load our base class
82
  include_once( BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-base.php' );
83
 
90
  }
91
  }
92
 
93
+
94
  if ( ! empty( $conf['breeze-minify-js'] ) ) {
95
+
96
  include_once( BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-scripts.php' );
97
+
98
+ // JS/CSS minifier library
99
+ include_once(BREEZE_PLUGIN_DIR.'vendor/autoload.php');
100
+
 
 
 
101
  if ( ! defined( 'CONCATENATE_SCRIPTS' ) ) {
102
  define( 'CONCATENATE_SCRIPTS', false );
103
  }
104
  if ( ! defined( 'COMPRESS_SCRIPTS' ) ) {
105
  define( 'COMPRESS_SCRIPTS', false );
106
  }
107
+ } elseif ( ! empty( $config_advanced['breeze-defer-js'] ) ) {
108
+ // If we have defer scripts to handle, load only the script for this action.
109
+ include_once( BREEZE_PLUGIN_DIR . 'inc/minification/breeze-js-deferred-loading.php' );
110
  }
111
+
112
  if ( ! empty( $conf['breeze-minify-css'] ) ) {
113
  include_once( BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-styles.php' );
114
  if ( defined( 'breeze_LEGACY_MINIFIERS' ) ) {
124
  define( 'COMPRESS_CSS', false );
125
  }
126
  }
 
127
  // Now, start the real thing!
128
  add_filter( 'breeze_minify_content_return', array( $this, 'breeze_end_buffering' ) );
129
  }
134
  */
135
 
136
  public function breeze_end_buffering( $content ) {
137
+
138
  if ( stripos( $content, "<html" ) === false || stripos( $content, "<html amp" ) !== false || stripos( $content, "<html ⚡" ) !== false || stripos( $content, "<xsl:stylesheet" ) !== false ) {
139
  return $content;
140
  }
164
  $js_include_inline = $css_include_inline = false;
165
  if ( ! empty( $conf['breeze-minify-js'] ) ) {
166
  $classes[] = 'Breeze_MinificationScripts';
167
+ } elseif ( ! empty( $minify['breeze-defer-js'] ) ) {
168
+ $classes[] = 'Breeze_Js_Deferred_Loading';
169
  }
170
+
171
  if ( ! empty( $conf['breeze-minify-css'] ) ) {
172
  $classes[] = 'Breeze_MinificationStyles';
173
  }
219
  ),
220
  'Breeze_MinificationHtml' => array(
221
  'keepcomments' => false
222
+ ),
223
+ 'Breeze_Js_Deferred_Loading' => array(
224
+ 'move_to_footer_js' => $minify['breeze-move-to-footer-js'],
225
+ 'defer_js' => $minify['breeze-defer-js'],
226
+ ),
227
  );
228
 
229
  $content = apply_filters( 'breeze_filter_html_before_minify', $content );
234
  } else {
235
  // Run the classes
236
  foreach ( $classes as $name ) {
237
+
238
  $do_process = false;
239
  $instance = new $name( $content );
240
  if ( 'Breeze_MinificationStyles' === $name ) {
inc/minification/minify/jsmin-1.1.1.php DELETED
@@ -1,291 +0,0 @@
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
- * @license http://opensource.org/licenses/mit-license.php MIT License
44
- * @version 1.1.1 (2008-03-02)
45
- * @link http://code.google.com/p/jsmin-php/
46
- */
47
-
48
- class JSMin {
49
- const ORD_LF = 10;
50
- const ORD_SPACE = 32;
51
-
52
- protected $a = '';
53
- protected $b = '';
54
- protected $input = '';
55
- protected $inputIndex = 0;
56
- protected $inputLength = 0;
57
- protected $lookAhead = null;
58
- protected $output = '';
59
-
60
- // -- Public Static Methods --------------------------------------------------
61
-
62
- public static function minify($js) {
63
- $jsmin = new JSMin($js);
64
- return $jsmin->min();
65
- }
66
-
67
- // -- Public Instance Methods ------------------------------------------------
68
-
69
- public function __construct($input) {
70
- $this->input = str_replace("\r\n", "\n", $input);
71
- $this->inputLength = strlen($this->input);
72
- }
73
-
74
- // -- Protected Instance Methods ---------------------------------------------
75
-
76
- protected function action($d) {
77
- switch($d) {
78
- case 1:
79
- $this->output .= $this->a;
80
-
81
- case 2:
82
- $this->a = $this->b;
83
-
84
- if ($this->a === "'" || $this->a === '"') {
85
- for (;;) {
86
- $this->output .= $this->a;
87
- $this->a = $this->get();
88
-
89
- if ($this->a === $this->b) {
90
- break;
91
- }
92
-
93
- if (ord($this->a) <= self::ORD_LF) {
94
- throw new JSMinException('Unterminated string literal.');
95
- }
96
-
97
- if ($this->a === '\\') {
98
- $this->output .= $this->a;
99
- $this->a = $this->get();
100
- }
101
- }
102
- }
103
-
104
- case 3:
105
- $this->b = $this->next();
106
-
107
- if ($this->b === '/' && (
108
- $this->a === '(' || $this->a === ',' || $this->a === '=' ||
109
- $this->a === ':' || $this->a === '[' || $this->a === '!' ||
110
- $this->a === '&' || $this->a === '|' || $this->a === '?')) {
111
-
112
- $this->output .= $this->a . $this->b;
113
-
114
- for (;;) {
115
- $this->a = $this->get();
116
-
117
- if ($this->a === '/') {
118
- break;
119
- } elseif ($this->a === '\\') {
120
- $this->output .= $this->a;
121
- $this->a = $this->get();
122
- } elseif (ord($this->a) <= self::ORD_LF) {
123
- throw new JSMinException('Unterminated regular expression '.
124
- 'literal.');
125
- }
126
-
127
- $this->output .= $this->a;
128
- }
129
-
130
- $this->b = $this->next();
131
- }
132
- }
133
- }
134
-
135
- protected function get() {
136
- $c = $this->lookAhead;
137
- $this->lookAhead = null;
138
-
139
- if ($c === null) {
140
- if ($this->inputIndex < $this->inputLength) {
141
- $c = $this->input[$this->inputIndex];
142
- $this->inputIndex += 1;
143
- } else {
144
- $c = null;
145
- }
146
- }
147
-
148
- if ($c === "\r") {
149
- return "\n";
150
- }
151
-
152
- if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
153
- return $c;
154
- }
155
-
156
- return ' ';
157
- }
158
-
159
- protected function isAlphaNum($c) {
160
- return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
161
- }
162
-
163
- protected function min() {
164
- $this->a = "\n";
165
- $this->action(3);
166
-
167
- while ($this->a !== null) {
168
- switch ($this->a) {
169
- case ' ':
170
- if ($this->isAlphaNum($this->b)) {
171
- $this->action(1);
172
- } else {
173
- $this->action(2);
174
- }
175
- break;
176
-
177
- case "\n":
178
- switch ($this->b) {
179
- case '{':
180
- case '[':
181
- case '(':
182
- case '+':
183
- case '-':
184
- $this->action(1);
185
- break;
186
-
187
- case ' ':
188
- $this->action(3);
189
- break;
190
-
191
- default:
192
- if ($this->isAlphaNum($this->b)) {
193
- $this->action(1);
194
- }
195
- else {
196
- $this->action(2);
197
- }
198
- }
199
- break;
200
-
201
- default:
202
- switch ($this->b) {
203
- case ' ':
204
- if ($this->isAlphaNum($this->a)) {
205
- $this->action(1);
206
- break;
207
- }
208
-
209
- $this->action(3);
210
- break;
211
-
212
- case "\n":
213
- switch ($this->a) {
214
- case '}':
215
- case ']':
216
- case ')':
217
- case '+':
218
- case '-':
219
- case '"':
220
- case "'":
221
- $this->action(1);
222
- break;
223
-
224
- default:
225
- if ($this->isAlphaNum($this->a)) {
226
- $this->action(1);
227
- }
228
- else {
229
- $this->action(3);
230
- }
231
- }
232
- break;
233
-
234
- default:
235
- $this->action(1);
236
- break;
237
- }
238
- }
239
- }
240
-
241
- return $this->output;
242
- }
243
-
244
- protected function next() {
245
- $c = $this->get();
246
-
247
- if ($c === '/') {
248
- switch($this->peek()) {
249
- case '/':
250
- for (;;) {
251
- $c = $this->get();
252
-
253
- if (ord($c) <= self::ORD_LF) {
254
- return $c;
255
- }
256
- }
257
-
258
- case '*':
259
- $this->get();
260
-
261
- for (;;) {
262
- switch($this->get()) {
263
- case '*':
264
- if ($this->peek() === '/') {
265
- $this->get();
266
- return ' ';
267
- }
268
- break;
269
-
270
- case null:
271
- throw new JSMinException('Unterminated comment.');
272
- }
273
- }
274
-
275
- default:
276
- return $c;
277
- }
278
- }
279
-
280
- return $c;
281
- }
282
-
283
- protected function peek() {
284
- $this->lookAhead = $this->get();
285
- return $this->lookAhead;
286
- }
287
- }
288
-
289
- // -- Exceptions ---------------------------------------------------------------
290
- class JSMinException extends Exception {}
291
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/minification/minify/minify-2.1.7-html.php DELETED
@@ -1,257 +0,0 @@
1
- <?php
2
- /**
3
- * Class Minify_HTML
4
- * @package Minify
5
- */
6
-
7
- /**
8
- * Compress HTML
9
- *
10
- * This is a heavy regex-based removal of whitespace, unnecessary comments and
11
- * tokens. IE conditional comments are preserved. There are also options to have
12
- * STYLE and SCRIPT blocks compressed by callback functions.
13
- *
14
- * A test suite is available.
15
- *
16
- * @package Minify
17
- * @author Stephen Clay <steve@mrclay.org>
18
- */
19
- class Minify_HTML {
20
- /**
21
- * @var boolean
22
- */
23
- protected $_jsCleanComments = true;
24
-
25
- /**
26
- * "Minify" an HTML page
27
- *
28
- * @param string $html
29
- *
30
- * @param array $options
31
- *
32
- * 'cssMinifier' : (optional) callback function to process content of STYLE
33
- * elements.
34
- *
35
- * 'jsMinifier' : (optional) callback function to process content of SCRIPT
36
- * elements. Note: the type attribute is ignored.
37
- *
38
- * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
39
- * unset, minify will sniff for an XHTML doctype.
40
- *
41
- * @return string
42
- */
43
- public static function minify($html, $options = array()) {
44
- $min = new self($html, $options);
45
- return $min->process();
46
- }
47
-
48
-
49
- /**
50
- * Create a minifier object
51
- *
52
- * @param string $html
53
- *
54
- * @param array $options
55
- *
56
- * 'cssMinifier' : (optional) callback function to process content of STYLE
57
- * elements.
58
- *
59
- * 'jsMinifier' : (optional) callback function to process content of SCRIPT
60
- * elements. Note: the type attribute is ignored.
61
- *
62
- * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
63
- *
64
- * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
65
- * unset, minify will sniff for an XHTML doctype.
66
- *
67
- * @return null
68
- */
69
- public function __construct($html, $options = array())
70
- {
71
- $this->_html = str_replace("\r\n", "\n", trim($html));
72
- if (isset($options['xhtml'])) {
73
- $this->_isXhtml = (bool)$options['xhtml'];
74
- }
75
- if (isset($options['cssMinifier'])) {
76
- $this->_cssMinifier = $options['cssMinifier'];
77
- }
78
- if (isset($options['jsMinifier'])) {
79
- $this->_jsMinifier = $options['jsMinifier'];
80
- }
81
- if (isset($options['jsCleanComments'])) {
82
- $this->_jsCleanComments = (bool)$options['jsCleanComments'];
83
- }
84
- }
85
-
86
-
87
- /**
88
- * Minify the markeup given in the constructor
89
- *
90
- * @return string
91
- */
92
- public function process()
93
- {
94
- if ($this->_isXhtml === null) {
95
- $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
96
- }
97
-
98
- $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
99
- $this->_placeholders = array();
100
-
101
- // replace SCRIPTs (and minify) with placeholders
102
- $this->_html = preg_replace_callback(
103
- '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
104
- ,array($this, '_removeScriptCB')
105
- ,$this->_html);
106
-
107
- // replace STYLEs (and minify) with placeholders
108
- $this->_html = preg_replace_callback(
109
- '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
110
- ,array($this, '_removeStyleCB')
111
- ,$this->_html);
112
-
113
- // remove HTML comments (not containing IE conditional comments).
114
- $this->_html = preg_replace_callback(
115
- '/<!--([\\s\\S]*?)-->/'
116
- ,array($this, '_commentCB')
117
- ,$this->_html);
118
-
119
- // replace PREs with placeholders
120
- $this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
121
- ,array($this, '_removePreCB')
122
- ,$this->_html);
123
-
124
- // replace TEXTAREAs with placeholders
125
- $this->_html = preg_replace_callback(
126
- '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
127
- ,array($this, '_removeTextareaCB')
128
- ,$this->_html);
129
-
130
- // trim each line.
131
- // @todo take into account attribute values that span multiple lines.
132
- $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
133
-
134
- // remove ws around block/undisplayed elements
135
- $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
136
- .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
137
- .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
138
- .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
139
- .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
140
-
141
- // remove ws outside of all elements
142
- $this->_html = preg_replace(
143
- '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
144
- ,'>$1$2$3<'
145
- ,$this->_html);
146
-
147
- // use newlines before 1st attribute in open tags (to limit line lengths)
148
- $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
149
-
150
- // fill placeholders
151
- $this->_html = str_replace(
152
- array_keys($this->_placeholders)
153
- ,array_values($this->_placeholders)
154
- ,$this->_html
155
- );
156
- // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
157
- $this->_html = str_replace(
158
- array_keys($this->_placeholders)
159
- ,array_values($this->_placeholders)
160
- ,$this->_html
161
- );
162
- return $this->_html;
163
- }
164
-
165
- protected function _commentCB($m)
166
- {
167
- return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
168
- ? $m[0]
169
- : '';
170
- }
171
-
172
- protected function _reservePlace($content)
173
- {
174
- $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
175
- $this->_placeholders[$placeholder] = $content;
176
- return $placeholder;
177
- }
178
-
179
- protected $_isXhtml = null;
180
- protected $_replacementHash = null;
181
- protected $_placeholders = array();
182
- protected $_cssMinifier = null;
183
- protected $_jsMinifier = null;
184
-
185
- protected function _removePreCB($m)
186
- {
187
- return $this->_reservePlace("<pre{$m[1]}");
188
- }
189
-
190
- protected function _removeTextareaCB($m)
191
- {
192
- return $this->_reservePlace("<textarea{$m[1]}");
193
- }
194
-
195
- protected function _removeStyleCB($m)
196
- {
197
- $openStyle = "<style{$m[1]}";
198
- $css = $m[2];
199
- // remove HTML comments
200
- $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
201
-
202
- // remove CDATA section markers
203
- $css = $this->_removeCdata($css);
204
-
205
- // minify
206
- $minifier = $this->_cssMinifier
207
- ? $this->_cssMinifier
208
- : 'trim';
209
- $css = call_user_func($minifier, $css);
210
-
211
- return $this->_reservePlace($this->_needsCdata($css)
212
- ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
213
- : "{$openStyle}{$css}</style>"
214
- );
215
- }
216
-
217
- protected function _removeScriptCB($m)
218
- {
219
- $openScript = "<script{$m[2]}";
220
- $js = $m[3];
221
-
222
- // whitespace surrounding? preserve at least one space
223
- $ws1 = ($m[1] === '') ? '' : ' ';
224
- $ws2 = ($m[4] === '') ? '' : ' ';
225
-
226
- // remove HTML comments (and ending "//" if present)
227
- if ($this->_jsCleanComments) {
228
- $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
229
- }
230
-
231
- // remove CDATA section markers
232
- $js = $this->_removeCdata($js);
233
-
234
- // minify
235
- $minifier = $this->_jsMinifier
236
- ? $this->_jsMinifier
237
- : 'trim';
238
- $js = call_user_func($minifier, $js);
239
-
240
- return $this->_reservePlace($this->_needsCdata($js)
241
- ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
242
- : "{$ws1}{$openScript}{$js}</script>{$ws2}"
243
- );
244
- }
245
-
246
- protected function _removeCdata($str)
247
- {
248
- return (false !== strpos($str, '<![CDATA['))
249
- ? str_replace(array('<![CDATA[', ']]>'), '', $str)
250
- : $str;
251
- }
252
-
253
- protected function _needsCdata($str)
254
- {
255
- return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
256
- }
257
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/minification/minify/minify-2.1.7-jsmin.php DELETED
@@ -1,447 +0,0 @@
1
- <?php
2
- /**
3
- * JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
4
- *
5
- * <code>
6
- * $minifiedJs = JSMin::minify($js);
7
- * </code>
8
- *
9
- * This is a modified port of jsmin.c. Improvements:
10
- *
11
- * Does not choke on some regexp literals containing quote characters. E.g. /'/
12
- *
13
- * Spaces are preserved after some add/sub operators, so they are not mistakenly
14
- * converted to post-inc/dec. E.g. a + ++b -> a+ ++b
15
- *
16
- * Preserves multi-line comments that begin with /*!
17
- *
18
- * PHP 5 or higher is required.
19
- *
20
- * Permission is hereby granted to use this version of the library under the
21
- * same terms as jsmin.c, which has the following license:
22
- *
23
- * --
24
- * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
25
- *
26
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
27
- * this software and associated documentation files (the "Software"), to deal in
28
- * the Software without restriction, including without limitation the rights to
29
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
30
- * of the Software, and to permit persons to whom the Software is furnished to do
31
- * so, subject to the following conditions:
32
- *
33
- * The above copyright notice and this permission notice shall be included in all
34
- * copies or substantial portions of the Software.
35
- *
36
- * The Software shall be used for Good, not Evil.
37
- *
38
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
44
- * SOFTWARE.
45
- * --
46
- *
47
- * @package JSMin
48
- * @author Ryan Grove <ryan@wonko.com> (PHP port)
49
- * @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
50
- * @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
51
- * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
52
- * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
53
- * @license http://opensource.org/licenses/mit-license.php MIT License
54
- * @link http://code.google.com/p/jsmin-php/
55
- */
56
-
57
- class JSMin {
58
- const ORD_LF = 10;
59
- const ORD_SPACE = 32;
60
- const ACTION_KEEP_A = 1;
61
- const ACTION_DELETE_A = 2;
62
- const ACTION_DELETE_A_B = 3;
63
-
64
- protected $a = "\n";
65
- protected $b = '';
66
- protected $input = '';
67
- protected $inputIndex = 0;
68
- protected $inputLength = 0;
69
- protected $lookAhead = null;
70
- protected $output = '';
71
- protected $lastByteOut = '';
72
- protected $keptComment = '';
73
-
74
- /**
75
- * Minify Javascript.
76
- *
77
- * @param string $js Javascript to be minified
78
- *
79
- * @return string
80
- */
81
- public static function minify($js)
82
- {
83
- $jsmin = new JSMin($js);
84
- return $jsmin->min();
85
- }
86
-
87
- /**
88
- * @param string $input
89
- */
90
- public function __construct($input)
91
- {
92
- $this->input = $input;
93
- }
94
-
95
- /**
96
- * Perform minification, return result
97
- *
98
- * @return string
99
- */
100
- public function min()
101
- {
102
- if ($this->output !== '') { // min already run
103
- return $this->output;
104
- }
105
-
106
- $mbIntEnc = null;
107
- if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
108
- $mbIntEnc = mb_internal_encoding();
109
- mb_internal_encoding('8bit');
110
- }
111
- $this->input = str_replace("\r\n", "\n", $this->input);
112
- $this->inputLength = strlen($this->input);
113
-
114
- $this->action(self::ACTION_DELETE_A_B);
115
-
116
- while ($this->a !== null) {
117
- // determine next command
118
- $command = self::ACTION_KEEP_A; // default
119
- if ($this->a === ' ') {
120
- if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
121
- && ($this->b === $this->lastByteOut)) {
122
- // Don't delete this space. If we do, the addition/subtraction
123
- // could be parsed as a post-increment
124
- } elseif (! $this->isAlphaNum($this->b)) {
125
- $command = self::ACTION_DELETE_A;
126
- }
127
- } elseif ($this->a === "\n") {
128
- if ($this->b === ' ') {
129
- $command = self::ACTION_DELETE_A_B;
130
-
131
- // in case of mbstring.func_overload & 2, must check for null b,
132
- // otherwise mb_strpos will give WARNING
133
- } elseif ($this->b === null
134
- || (false === strpos('{[(+-!~', $this->b)
135
- && ! $this->isAlphaNum($this->b))) {
136
- $command = self::ACTION_DELETE_A;
137
- }
138
- } elseif (! $this->isAlphaNum($this->a)) {
139
- if ($this->b === ' '
140
- || ($this->b === "\n"
141
- && (false === strpos('}])+-"\'', $this->a)))) {
142
- $command = self::ACTION_DELETE_A_B;
143
- }
144
- }
145
- $this->action($command);
146
- }
147
- $this->output = trim($this->output);
148
-
149
- if ($mbIntEnc !== null) {
150
- mb_internal_encoding($mbIntEnc);
151
- }
152
- return $this->output;
153
- }
154
-
155
- /**
156
- * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
157
- * ACTION_DELETE_A = Copy B to A. Get the next B.
158
- * ACTION_DELETE_A_B = Get the next B.
159
- *
160
- * @param int $command
161
- * @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
162
- */
163
- protected function action($command)
164
- {
165
- // make sure we don't compress "a + ++b" to "a+++b", etc.
166
- if ($command === self::ACTION_DELETE_A_B
167
- && $this->b === ' '
168
- && ($this->a === '+' || $this->a === '-')) {
169
- // Note: we're at an addition/substraction operator; the inputIndex
170
- // will certainly be a valid index
171
- if ($this->input[$this->inputIndex] === $this->a) {
172
- // This is "+ +" or "- -". Don't delete the space.
173
- $command = self::ACTION_KEEP_A;
174
- }
175
- }
176
-
177
- switch ($command) {
178
- case self::ACTION_KEEP_A: // 1
179
- $this->output .= $this->a;
180
-
181
- if ($this->keptComment) {
182
- $this->output = rtrim($this->output, "\n");
183
- $this->output .= $this->keptComment;
184
- $this->keptComment = '';
185
- }
186
-
187
- $this->lastByteOut = $this->a;
188
-
189
- // fallthrough intentional
190
- case self::ACTION_DELETE_A: // 2
191
- $this->a = $this->b;
192
- if ($this->a === "'" || $this->a === '"') { // string literal
193
- $str = $this->a; // in case needed for exception
194
- for(;;) {
195
- $this->output .= $this->a;
196
- $this->lastByteOut = $this->a;
197
-
198
- $this->a = $this->get();
199
- if ($this->a === $this->b) { // end quote
200
- break;
201
- }
202
- if ($this->isEOF($this->a)) {
203
- throw new JSMin_UnterminatedStringException(
204
- "JSMin: Unterminated String at byte {$this->inputIndex}: {$str}");
205
- }
206
- $str .= $this->a;
207
- if ($this->a === '\\') {
208
- $this->output .= $this->a;
209
- $this->lastByteOut = $this->a;
210
-
211
- $this->a = $this->get();
212
- $str .= $this->a;
213
- }
214
- }
215
- }
216
-
217
- // fallthrough intentional
218
- case self::ACTION_DELETE_A_B: // 3
219
- $this->b = $this->next();
220
- if ($this->b === '/' && $this->isRegexpLiteral()) {
221
- $this->output .= $this->a . $this->b;
222
- $pattern = '/'; // keep entire pattern in case we need to report it in the exception
223
- for(;;) {
224
- $this->a = $this->get();
225
- $pattern .= $this->a;
226
- if ($this->a === '[') {
227
- for(;;) {
228
- $this->output .= $this->a;
229
- $this->a = $this->get();
230
- $pattern .= $this->a;
231
- if ($this->a === ']') {
232
- break;
233
- }
234
- if ($this->a === '\\') {
235
- $this->output .= $this->a;
236
- $this->a = $this->get();
237
- $pattern .= $this->a;
238
- }
239
- if ($this->isEOF($this->a)) {
240
- throw new JSMin_UnterminatedRegExpException(
241
- "JSMin: Unterminated set in RegExp at byte "
242
- . $this->inputIndex .": {$pattern}");
243
- }
244
- }
245
- }
246
-
247
- if ($this->a === '/') { // end pattern
248
- break; // while (true)
249
- } elseif ($this->a === '\\') {
250
- $this->output .= $this->a;
251
- $this->a = $this->get();
252
- $pattern .= $this->a;
253
- } elseif ($this->isEOF($this->a)) {
254
- throw new JSMin_UnterminatedRegExpException(
255
- "JSMin: Unterminated RegExp at byte {$this->inputIndex}: {$pattern}");
256
- }
257
- $this->output .= $this->a;
258
- $this->lastByteOut = $this->a;
259
- }
260
- $this->b = $this->next();
261
- }
262
- // end case ACTION_DELETE_A_B
263
- }
264
- }
265
-
266
- /**
267
- * @return bool
268
- */
269
- protected function isRegexpLiteral()
270
- {
271
- if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
272
- // we obviously aren't dividing
273
- return true;
274
- }
275
-
276
- // we have to check for a preceding keyword, and we don't need to pattern
277
- // match over the whole output.
278
- $recentOutput = substr($this->output, -10);
279
-
280
- // check if return/typeof directly precede a pattern without a space
281
- foreach (array('return', 'typeof') as $keyword) {
282
- if ($this->a !== substr($keyword, -1)) {
283
- // certainly wasn't keyword
284
- continue;
285
- }
286
- if (preg_match("~(^|[\\s\\S])" . substr($keyword, 0, -1) . "$~", $recentOutput, $m)) {
287
- if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
288
- return true;
289
- }
290
- }
291
- }
292
-
293
- // check all keywords
294
- if ($this->a === ' ' || $this->a === "\n") {
295
- if (preg_match('~(^|[\\s\\S])(?:case|else|in|return|typeof)$~', $recentOutput, $m)) {
296
- if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
297
- return true;
298
- }
299
- }
300
- }
301
-
302
- return false;
303
- }
304
-
305
- /**
306
- * Return the next character from stdin. Watch out for lookahead. If the character is a control character,
307
- * translate it to a space or linefeed.
308
- *
309
- * @return string
310
- */
311
- protected function get()
312
- {
313
- $c = $this->lookAhead;
314
- $this->lookAhead = null;
315
- if ($c === null) {
316
- // getc(stdin)
317
- if ($this->inputIndex < $this->inputLength) {
318
- $c = $this->input[$this->inputIndex];
319
- $this->inputIndex += 1;
320
- } else {
321
- $c = null;
322
- }
323
- }
324
- if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
325
- return $c;
326
- }
327
- if ($c === "\r") {
328
- return "\n";
329
- }
330
- return ' ';
331
- }
332
-
333
- /**
334
- * Does $a indicate end of input?
335
- *
336
- * @param string $a
337
- * @return bool
338
- */
339
- protected function isEOF($a)
340
- {
341
- return ord($a) <= self::ORD_LF;
342
- }
343
-
344
- /**
345
- * Get next char (without getting it). If is ctrl character, translate to a space or newline.
346
- *
347
- * @return string
348
- */
349
- protected function peek()
350
- {
351
- $this->lookAhead = $this->get();
352
- return $this->lookAhead;
353
- }
354
-
355
- /**
356
- * Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
357
- *
358
- * @param string $c
359
- *
360
- * @return bool
361
- */
362
- protected function isAlphaNum($c)
363
- {
364
- return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
365
- }
366
-
367
- /**
368
- * Consume a single line comment from input (possibly retaining it)
369
- */
370
- protected function consumeSingleLineComment()
371
- {
372
- $comment = '';
373
- while (true) {
374
- $get = $this->get();
375
- $comment .= $get;
376
- if (ord($get) <= self::ORD_LF) { // end of line reached
377
- // if IE conditional comment
378
- if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
379
- $this->keptComment .= "/{$comment}";
380
- }
381
- return;
382
- }
383
- }
384
- }
385
-
386
- /**
387
- * Consume a multiple line comment from input (possibly retaining it)
388
- *
389
- * @throws JSMin_UnterminatedCommentException
390
- */
391
- protected function consumeMultipleLineComment()
392
- {
393
- $this->get();
394
- $comment = '';
395
- for(;;) {
396
- $get = $this->get();
397
- if ($get === '*') {
398
- if ($this->peek() === '/') { // end of comment reached
399
- $this->get();
400
- if (0 === strpos($comment, '!')) {
401
- // preserved by YUI Compressor
402
- if (!$this->keptComment) {
403
- // don't prepend a newline if two comments right after one another
404
- $this->keptComment = "\n";
405
- }
406
- $this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
407
- } else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
408
- // IE conditional
409
- $this->keptComment .= "/*{$comment}*/";
410
- }
411
- return;
412
- }
413
- } elseif ($get === null) {
414
- throw new JSMin_UnterminatedCommentException(
415
- "JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
416
- }
417
- $comment .= $get;
418
- }
419
- }
420
-
421
- /**
422
- * Get the next character, skipping over comments. Some comments may be preserved.
423
- *
424
- * @return string
425
- */
426
- protected function next()
427
- {
428
- $get = $this->get();
429
- if ($get === '/') {
430
- switch ($this->peek()) {
431
- case '/':
432
- $this->consumeSingleLineComment();
433
- $get = "\n";
434
- break;
435
- case '*':
436
- $this->consumeMultipleLineComment();
437
- $get = ' ';
438
- break;
439
- }
440
- }
441
- return $get;
442
- }
443
- }
444
-
445
- class JSMin_UnterminatedStringException extends Exception {}
446
- class JSMin_UnterminatedCommentException extends Exception {}
447
- class JSMin_UnterminatedRegExpException extends Exception {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/minification/minify/yui-php-cssmin-2.4.8-4.php DELETED
@@ -1,777 +0,0 @@
1
- <?php
2
-
3
- /*!
4
- * cssmin.php v2.4.8-4
5
- * Author: Tubal Martin - http://tubalmartin.me/
6
- * Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
7
- *
8
- * This is a PHP port of the CSS minification tool distributed with YUICompressor,
9
- * itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
10
- * Permission is hereby granted to use the PHP version under the same
11
- * conditions as the YUICompressor.
12
- */
13
-
14
- /*!
15
- * YUI Compressor
16
- * http://developer.yahoo.com/yui/compressor/
17
- * Author: Julien Lecomte - http://www.julienlecomte.net/
18
- * Copyright (c) 2013 Yahoo! Inc. All rights reserved.
19
- * The copyrights embodied in the content of this file are licensed
20
- * by Yahoo! Inc. under the BSD (revised) open source license.
21
- */
22
-
23
- class CSSmin
24
- {
25
- const NL = '___YUICSSMIN_PRESERVED_NL___';
26
- const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
27
- const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
28
- const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
29
- const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
30
-
31
- private $comments;
32
- private $preserved_tokens;
33
- private $memory_limit;
34
- private $max_execution_time;
35
- private $pcre_backtrack_limit;
36
- private $pcre_recursion_limit;
37
- private $raise_php_limits;
38
-
39
- /**
40
- * @param bool|int $raise_php_limits
41
- * If true, PHP settings will be raised if needed
42
- */
43
- public function __construct($raise_php_limits = TRUE)
44
- {
45
- // Set suggested PHP limits
46
- $this->memory_limit = 128 * 1048576; // 128MB in bytes
47
- $this->max_execution_time = 60; // 1 min
48
- $this->pcre_backtrack_limit = 1000 * 1000;
49
- $this->pcre_recursion_limit = 500 * 1000;
50
-
51
- $this->raise_php_limits = (bool) $raise_php_limits;
52
- }
53
-
54
- /**
55
- * Minify a string of CSS
56
- * @param string $css
57
- * @param int|bool $linebreak_pos
58
- * @return string
59
- */
60
- public function run($css = '', $linebreak_pos = FALSE)
61
- {
62
- if (empty($css)) {
63
- return '';
64
- }
65
-
66
- if ($this->raise_php_limits) {
67
- $this->do_raise_php_limits();
68
- }
69
-
70
- $this->comments = array();
71
- $this->preserved_tokens = array();
72
-
73
- $start_index = 0;
74
- $length = strlen($css);
75
-
76
- $css = $this->extract_data_urls($css);
77
-
78
- // collect all comment blocks...
79
- while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
80
- $end_index = $this->index_of($css, '*/', $start_index + 2);
81
- if ($end_index < 0) {
82
- $end_index = $length;
83
- }
84
- $comment_found = $this->str_slice($css, $start_index + 2, $end_index);
85
- $this->comments[] = $comment_found;
86
- $comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
87
- $css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
88
- // Set correct start_index: Fixes issue #2528130
89
- $start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
90
- }
91
-
92
- // preserve strings so their content doesn't get accidentally minified
93
- $css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
94
-
95
- // Let's divide css code in chunks of 5.000 chars aprox.
96
- // Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
97
- // of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
98
- // long strings and a (sub)pattern matches a number of chars greater than
99
- // the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
100
- // returning NULL and $css would be empty.
101
- $charset = '';
102
- $charset_regexp = '/(@charset)( [^;]+;)/i';
103
- $css_chunks = array();
104
- $css_chunk_length = 5000; // aprox size, not exact
105
- $start_index = 0;
106
- $i = $css_chunk_length; // save initial iterations
107
- $l = strlen($css);
108
-
109
-
110
- // if the number of characters is 5000 or less, do not chunk
111
- if ($l <= $css_chunk_length) {
112
- $css_chunks[] = $css;
113
- } else {
114
- // chunk css code securely
115
- while ($i < $l) {
116
- $i += 50; // save iterations
117
- if ($l - $start_index <= $css_chunk_length || $i >= $l) {
118
- $css_chunks[] = $this->str_slice($css, $start_index);
119
- break;
120
- }
121
- if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
122
- // If there are two ending curly braces }} separated or not by spaces,
123
- // join them in the same chunk (i.e. @media blocks)
124
- $next_chunk = substr($css, $i);
125
- if (preg_match('/^\s*\}/', $next_chunk)) {
126
- $i = $i + $this->index_of($next_chunk, '}') + 1;
127
- }
128
-
129
- $css_chunks[] = $this->str_slice($css, $start_index, $i);
130
- $start_index = $i;
131
- }
132
- }
133
- }
134
-
135
- // Minify each chunk
136
- for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
137
- $css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
138
- // Keep the first @charset at-rule found
139
- if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
140
- $charset = strtolower($matches[1]) . $matches[2];
141
- }
142
- // Delete all @charset at-rules
143
- $css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
144
- }
145
-
146
- // Update the first chunk and push the charset to the top of the file.
147
- $css_chunks[0] = $charset . $css_chunks[0];
148
-
149
- return implode('', $css_chunks);
150
- }
151
-
152
- /**
153
- * Sets the memory limit for this script
154
- * @param int|string $limit
155
- */
156
- public function set_memory_limit($limit)
157
- {
158
- $this->memory_limit = $this->normalize_int($limit);
159
- }
160
-
161
- /**
162
- * Sets the maximum execution time for this script
163
- * @param int|string $seconds
164
- */
165
- public function set_max_execution_time($seconds)
166
- {
167
- $this->max_execution_time = (int) $seconds;
168
- }
169
-
170
- /**
171
- * Sets the PCRE backtrack limit for this script
172
- * @param int $limit
173
- */
174
- public function set_pcre_backtrack_limit($limit)
175
- {
176
- $this->pcre_backtrack_limit = (int) $limit;
177
- }
178
-
179
- /**
180
- * Sets the PCRE recursion limit for this script
181
- * @param int $limit
182
- */
183
- public function set_pcre_recursion_limit($limit)
184
- {
185
- $this->pcre_recursion_limit = (int) $limit;
186
- }
187
-
188
- /**
189
- * Try to configure PHP to use at least the suggested minimum settings
190
- */
191
- private function do_raise_php_limits()
192
- {
193
- $php_limits = array(
194
- 'memory_limit' => $this->memory_limit,
195
- 'max_execution_time' => $this->max_execution_time,
196
- 'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
197
- 'pcre.recursion_limit' => $this->pcre_recursion_limit
198
- );
199
-
200
- // If current settings are higher respect them.
201
- foreach ($php_limits as $name => $suggested) {
202
- $current = $this->normalize_int(ini_get($name));
203
- // memory_limit exception: allow -1 for "no memory limit".
204
- if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
205
- ini_set($name, $suggested);
206
- }
207
- }
208
- }
209
-
210
- /**
211
- * Does bulk of the minification
212
- * @param string $css
213
- * @param int|bool $linebreak_pos
214
- * @return string
215
- */
216
- private function minify($css, $linebreak_pos)
217
- {
218
- // strings are safe, now wrestle the comments
219
- for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
220
-
221
- $token = $this->comments[$i];
222
- $placeholder = '/' . self::COMMENT . $i . '___/';
223
-
224
- // ! in the first position of the comment means preserve
225
- // so push to the preserved tokens keeping the !
226
- if (substr($token, 0, 1) === '!') {
227
- $this->preserved_tokens[] = $token;
228
- $token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
229
- $css = preg_replace($placeholder, $token_tring, $css, 1);
230
- // Preserve new lines for /*! important comments
231
- $css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
232
- $css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
233
- continue;
234
- }
235
-
236
- // \ in the last position looks like hack for Mac/IE5
237
- // shorten that to /*\*/ and the next one to /**/
238
- if (substr($token, (strlen($token) - 1), 1) === '\\') {
239
- $this->preserved_tokens[] = '\\';
240
- $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
241
- $i = $i + 1; // attn: advancing the loop
242
- $this->preserved_tokens[] = '';
243
- $css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
244
- continue;
245
- }
246
-
247
- // keep empty comments after child selectors (IE7 hack)
248
- // e.g. html >/**/ body
249
- if (strlen($token) === 0) {
250
- $start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
251
- if ($start_index > 2) {
252
- if (substr($css, $start_index - 3, 1) === '>') {
253
- $this->preserved_tokens[] = '';
254
- $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
255
- }
256
- }
257
- }
258
-
259
- // in all other cases kill the comment
260
- $css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
261
- }
262
-
263
-
264
- // Normalize all whitespace strings to single spaces. Easier to work with that way.
265
- $css = preg_replace('/\s+/', ' ', $css);
266
-
267
- // Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
268
- $css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);
269
-
270
- // Shorten & preserve calculations calc(...) since spaces are important
271
- $css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
272
-
273
- // Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
274
- // +1.2em to 1.2em, +.8px to .8px, +2% to 2%
275
- $css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
276
-
277
- // Remove leading zeros from integer and float numbers preceded by : or a white-space
278
- // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
279
- $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
280
-
281
- // Remove trailing zeros from float numbers preceded by : or a white-space
282
- // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
283
- $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
284
-
285
- // Remove trailing .0 -> -9.0 to -9
286
- $css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
287
-
288
- // Replace 0 length numbers with 0
289
- $css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
290
-
291
- // Remove the spaces before the things that should not have spaces before them.
292
- // But, be careful not to turn "p :link {...}" into "p:link{...}"
293
- // Swap out any pseudo-class colons with the token, and then swap back.
294
- $css = preg_replace_callback('/(?:^|\})[^\{]*\s+\:/', array($this, 'replace_colon'), $css);
295
-
296
- // Remove spaces before the things that should not have spaces before them.
297
- $css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
298
-
299
- // Restore spaces for !important
300
- $css = preg_replace('/\!important/i', ' !important', $css);
301
-
302
- // bring back the colon
303
- $css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
304
-
305
- // retain space for special IE6 cases
306
- $css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
307
-
308
- // no space after the end of a preserved comment
309
- $css = preg_replace('/\*\/ /', '*/', $css);
310
-
311
- // lowercase some popular @directives
312
- $css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
313
-
314
- // lowercase some more common pseudo-elements
315
- $css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css);
316
-
317
- // lowercase some more common functions
318
- $css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css);
319
-
320
- // lower case some common function that can be values
321
- // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
322
- $css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);
323
-
324
- // Put the space back in some cases, to support stuff like
325
- // @media screen and (-webkit-min-device-pixel-ratio:0){
326
- $css = preg_replace('/\band\(/i', 'and (', $css);
327
-
328
- // Remove the spaces after the things that should not have spaces after them.
329
- $css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
330
-
331
- // remove unnecessary semicolons
332
- $css = preg_replace('/;+\}/', '}', $css);
333
-
334
- // Fix for issue: #2528146
335
- // Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
336
- // to avoid issues on Symbian S60 3.x browsers.
337
- $css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
338
-
339
- // Replace 0 <length> and 0 <percentage> values with 0.
340
- // <length> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/length
341
- // <percentage> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
342
- $css = preg_replace('/([^\\\\]\:|\s)0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%)/iS', '${1}0', $css);
343
-
344
- // 0% step in a keyframe? restore the % unit
345
- $css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]+\{)(.*?)(\}\})/iS', array($this, 'replace_keyframe_zero'), $css);
346
-
347
- // Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
348
- $css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
349
-
350
- // Fix for issue: #2528142
351
- // Replace text-shadow:0; with text-shadow:0 0 0;
352
- $css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
353
-
354
- // Replace background-position:0; with background-position:0 0;
355
- // same for transform-origin
356
- // Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
357
- $css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
358
-
359
- // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
360
- // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
361
- // This makes it more likely that it'll get further compressed in the next step.
362
- $css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
363
- $css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
364
-
365
- // Shorten colors from #AABBCC to #ABC or short color name.
366
- $css = $this->compress_hex_colors($css);
367
-
368
- // border: none to border:0, outline: none to outline:0
369
- $css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
370
-
371
- // shorter opacity IE filter
372
- $css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
373
-
374
- // Find a fraction that is used for Opera's -o-device-pixel-ratio query
375
- // Add token to add the "\" back in later
376
- $css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
377
-
378
- // Remove empty rules.
379
- $css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
380
-
381
- // Add "/" back to fix Opera -o-device-pixel-ratio query
382
- $css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
383
-
384
- // Replace multiple semi-colons in a row by a single one
385
- // See SF bug #1980989
386
- $css = preg_replace('/;;+/', ';', $css);
387
-
388
- // Restore new lines for /*! important comments
389
- $css = preg_replace('/'. self::NL .'/', "\n", $css);
390
-
391
- // Lowercase all uppercase properties
392
- $css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
393
-
394
- // Some source control tools don't like it when files containing lines longer
395
- // than, say 8000 characters, are checked in. The linebreak option is used in
396
- // that case to split long lines after a specific column.
397
- if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
398
- $linebreak_pos = (int) $linebreak_pos;
399
- $start_index = $i = 0;
400
- while ($i < strlen($css)) {
401
- $i++;
402
- if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
403
- $css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
404
- $start_index = $i;
405
- }
406
- }
407
- }
408
-
409
- // restore preserved comments and strings in reverse order
410
- for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
411
- $css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
412
- }
413
-
414
- // Trim the final string (for any leading or trailing white spaces)
415
- return trim($css);
416
- }
417
-
418
- /**
419
- * Utility method to replace all data urls with tokens before we start
420
- * compressing, to avoid performance issues running some of the subsequent
421
- * regexes against large strings chunks.
422
- *
423
- * @param string $css
424
- * @return string
425
- */
426
- private function extract_data_urls($css)
427
- {
428
- // Leave data urls alone to increase parse performance.
429
- $max_index = strlen($css) - 1;
430
- $append_index = $index = $last_index = $offset = 0;
431
- $sb = array();
432
- $pattern = '/url\(\s*(["\']?)data\:/i';
433
-
434
- // Since we need to account for non-base64 data urls, we need to handle
435
- // ' and ) being part of the data string. Hence switching to indexOf,
436
- // to determine whether or not we have matching string terminators and
437
- // handling sb appends directly, instead of using matcher.append* methods.
438
-
439
- while (preg_match($pattern, $css, $m, 0, $offset)) {
440
- $index = $this->index_of($css, $m[0], $offset);
441
- $last_index = $index + strlen($m[0]);
442
- $start_index = $index + 4; // "url(".length()
443
- $end_index = $last_index - 1;
444
- $terminator = $m[1]; // ', " or empty (not quoted)
445
- $found_terminator = FALSE;
446
-
447
- if (strlen($terminator) === 0) {
448
- $terminator = ')';
449
- }
450
-
451
- while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
452
- $end_index = $this->index_of($css, $terminator, $end_index + 1);
453
-
454
- // endIndex == 0 doesn't really apply here
455
- if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
456
- $found_terminator = TRUE;
457
- if (')' != $terminator) {
458
- $end_index = $this->index_of($css, ')', $end_index);
459
- }
460
- }
461
- }
462
-
463
- // Enough searching, start moving stuff over to the buffer
464
- $sb[] = $this->str_slice($css, $append_index, $index);
465
-
466
- if ($found_terminator) {
467
- $token = $this->str_slice($css, $start_index, $end_index);
468
- $token = preg_replace('/\s+/', '', $token);
469
- $this->preserved_tokens[] = $token;
470
-
471
- $preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
472
- $sb[] = $preserver;
473
-
474
- $append_index = $end_index + 1;
475
- } else {
476
- // No end terminator found, re-add the whole match. Should we throw/warn here?
477
- $sb[] = $this->str_slice($css, $index, $last_index);
478
- $append_index = $last_index;
479
- }
480
-
481
- $offset = $last_index;
482
- }
483
-
484
- $sb[] = $this->str_slice($css, $append_index);
485
-
486
- return implode('', $sb);
487
- }
488
-
489
- /**
490
- * Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
491
- *
492
- * DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
493
- * e.g. #AddressForm { ... }
494
- *
495
- * DOES NOT compress IE filters, which have hex color values (which would break things).
496
- * e.g. filter: chroma(color="#FFFFFF");
497
- *
498
- * DOES NOT compress invalid hex values.
499
- * e.g. background-color: #aabbccdd
500
- *
501
- * @param string $css
502
- * @return string
503
- */
504
- private function compress_hex_colors($css)
505
- {
506
- // Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
507
- $pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
508
- $_index = $index = $last_index = $offset = 0;
509
- $sb = array();
510
- // See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
511
- $short_safe = array(
512
- '#808080' => 'gray',
513
- '#008000' => 'green',
514
- '#800000' => 'maroon',
515
- '#000080' => 'navy',
516
- '#808000' => 'olive',
517
- '#ffa500' => 'orange',
518
- '#800080' => 'purple',
519
- '#c0c0c0' => 'silver',
520
- '#008080' => 'teal',
521
- '#f00' => 'red'
522
- );
523
-
524
- while (preg_match($pattern, $css, $m, 0, $offset)) {
525
- $index = $this->index_of($css, $m[0], $offset);
526
- $last_index = $index + strlen($m[0]);
527
- $is_filter = $m[1] !== null && $m[1] !== '';
528
-
529
- $sb[] = $this->str_slice($css, $_index, $index);
530
-
531
- if ($is_filter) {
532
- // Restore, maintain case, otherwise filter will break
533
- $sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
534
- } else {
535
- if (strtolower($m[2]) == strtolower($m[3]) &&
536
- strtolower($m[4]) == strtolower($m[5]) &&
537
- strtolower($m[6]) == strtolower($m[7])) {
538
- // Compress.
539
- $hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
540
- } else {
541
- // Non compressible color, restore but lower case.
542
- $hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
543
- }
544
- // replace Hex colors to short safe color names
545
- $sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
546
- }
547
-
548
- $_index = $offset = $last_index - strlen($m[8]);
549
- }
550
-
551
- $sb[] = $this->str_slice($css, $_index);
552
-
553
- return implode('', $sb);
554
- }
555
-
556
- /* CALLBACKS
557
- * ---------------------------------------------------------------------------------------------
558
- */
559
-
560
- private function replace_string($matches)
561
- {
562
- $match = $matches[0];
563
- $quote = substr($match, 0, 1);
564
- // Must use addcslashes in PHP to avoid parsing of backslashes
565
- $match = addcslashes($this->str_slice($match, 1, -1), '\\');
566
-
567
- // maybe the string contains a comment-like substring?
568
- // one, maybe more? put'em back then
569
- if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
570
- for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
571
- $match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
572
- }
573
- }
574
-
575
- // minify alpha opacity in filter strings
576
- $match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
577
-
578
- $this->preserved_tokens[] = $match;
579
- return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
580
- }
581
-
582
- private function replace_colon($matches)
583
- {
584
- return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
585
- }
586
-
587
- private function replace_calc($matches)
588
- {
589
- $this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]));
590
- return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
591
- }
592
-
593
- private function preserve_old_IE_specific_matrix_definition($matches)
594
- {
595
- $this->preserved_tokens[] = $matches[1];
596
- return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
597
- }
598
-
599
- private function replace_keyframe_zero($matches)
600
- {
601
- return $matches[1] . preg_replace('/0(\{|,[^\)\{]+\{)/', '0%$1', $matches[2]) . $matches[3];
602
- }
603
-
604
- private function rgb_to_hex($matches)
605
- {
606
- // Support for percentage values rgb(100%, 0%, 45%);
607
- if ($this->index_of($matches[1], '%') >= 0){
608
- $rgbcolors = explode(',', str_replace('%', '', $matches[1]));
609
- for ($i = 0; $i < count($rgbcolors); $i++) {
610
- $rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
611
- }
612
- } else {
613
- $rgbcolors = explode(',', $matches[1]);
614
- }
615
-
616
- // Values outside the sRGB color space should be clipped (0-255)
617
- for ($i = 0; $i < count($rgbcolors); $i++) {
618
- $rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
619
- $rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
620
- }
621
-
622
- // Fix for issue #2528093
623
- if (!preg_match('/[\s\,\);\}]/', $matches[2])){
624
- $matches[2] = ' ' . $matches[2];
625
- }
626
-
627
- return '#' . implode('', $rgbcolors) . $matches[2];
628
- }
629
-
630
- private function hsl_to_hex($matches)
631
- {
632
- $values = explode(',', str_replace('%', '', $matches[1]));
633
- $h = floatval($values[0]);
634
- $s = floatval($values[1]);
635
- $l = floatval($values[2]);
636
-
637
- // Wrap and clamp, then fraction!
638
- $h = ((($h % 360) + 360) % 360) / 360;
639
- $s = $this->clamp_number($s, 0, 100) / 100;
640
- $l = $this->clamp_number($l, 0, 100) / 100;
641
-
642
- if ($s == 0) {
643
- $r = $g = $b = $this->round_number(255 * $l);
644
- } else {
645
- $v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
646
- $v1 = (2 * $l) - $v2;
647
- $r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
648
- $g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
649
- $b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
650
- }
651
-
652
- return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
653
- }
654
-
655
- private function lowercase_pseudo_first($matches)
656
- {
657
- return ':first-'. strtolower($matches[1]) .' '. $matches[2];
658
- }
659
-
660
- private function lowercase_directives($matches)
661
- {
662
- return '@'. strtolower($matches[1]);
663
- }
664
-
665
- private function lowercase_pseudo_elements($matches)
666
- {
667
- return ':'. strtolower($matches[1]);
668
- }
669
-
670
- private function lowercase_common_functions($matches)
671
- {
672
- return ':'. strtolower($matches[1]) .'(';
673
- }
674
-
675
- private function lowercase_common_functions_values($matches)
676
- {
677
- return $matches[1] . strtolower($matches[2]);
678
- }
679
-
680
- private function lowercase_properties($matches)
681
- {
682
- return $matches[1].strtolower($matches[2]).$matches[3];
683
- }
684
-
685
- /* HELPERS
686
- * ---------------------------------------------------------------------------------------------
687
- */
688
-
689
- private function hue_to_rgb($v1, $v2, $vh)
690
- {
691
- $vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
692
- if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
693
- if ($vh * 2 < 1) return $v2;
694
- if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
695
- return $v1;
696
- }
697
-
698
- private function round_number($n)
699
- {
700
- return intval(floor(floatval($n) + 0.5), 10);
701
- }
702
-
703
- private function clamp_number($n, $min, $max)
704
- {
705
- return min(max($n, $min), $max);
706
- }
707
-
708
- /**
709
- * PHP port of Javascript's "indexOf" function for strings only
710
- * Author: Tubal Martin http://blog.margenn.com
711
- *
712
- * @param string $haystack
713
- * @param string $needle
714
- * @param int $offset index (optional)
715
- * @return int
716
- */
717
- private function index_of($haystack, $needle, $offset = 0)
718
- {
719
- $index = strpos($haystack, $needle, $offset);
720
-
721
- return ($index !== FALSE) ? $index : -1;
722
- }
723
-
724
- /**
725
- * PHP port of Javascript's "slice" function for strings only
726
- * Author: Tubal Martin http://blog.margenn.com
727
- * Tests: http://margenn.com/tubal/str_slice/
728
- *
729
- * @param string $str
730
- * @param int $start index
731
- * @param int|bool $end index (optional)
732
- * @return string
733
- */
734
- private function str_slice($str, $start = 0, $end = FALSE)
735
- {
736
- if ($end !== FALSE && ($start < 0 || $end <= 0)) {
737
- $max = strlen($str);
738
-
739
- if ($start < 0) {
740
- if (($start = $max + $start) < 0) {
741
- return '';
742
- }
743
- }
744
-
745
- if ($end < 0) {
746
- if (($end = $max + $end) < 0) {
747
- return '';
748
- }
749
- }
750
-
751
- if ($end <= $start) {
752
- return '';
753
- }
754
- }
755
-
756
- $slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
757
- return ($slice === FALSE) ? '' : $slice;
758
- }
759
-
760
- /**
761
- * Convert strings like "64M" or "30" to int values
762
- * @param mixed $size
763
- * @return int
764
- */
765
- private function normalize_int($size)
766
- {
767
- if (is_string($size)) {
768
- switch (substr($size, -1)) {
769
- case 'M': case 'm': return $size * 1048576;
770
- case 'K': case 'k': return $size * 1024;
771
- case 'G': case 'g': return $size * 1073741824;
772
- }
773
- }
774
-
775
- return (int) $size;
776
- }
777
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: Cloudways
3
  Tags: cache,caching, performance, wp-cache, cdn, combine, compress, speed plugin, database cache,gzip, http compression, js cache, minify, optimize, page cache, performance, speed, expire headers
4
  Requires at least: 4.5
5
- Tested up to: 5.4.2
6
- Stable tag: 1.1.7
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -145,6 +145,13 @@ Using Gzip, Breeze compresses the request files, further reducing the size of th
145
 
146
  == Changelog ==
147
 
 
 
 
 
 
 
 
148
  = 1.1.7 =
149
  * Fix: Add HTTP and HTTPS for validation of CDN integration.
150
  * Fix: Custom settings for multisite will be reapplied after Breeze reactivation.
@@ -252,4 +259,4 @@ Update Breeze through WordPress Admin > Dashboard >Updates. The settings will re
252
 
253
  == Requirements ==
254
 
255
- PHP 5.3+, PHP7 or 7.1 recommended for better performance, WordPress 4.5+
2
  Contributors: Cloudways
3
  Tags: cache,caching, performance, wp-cache, cdn, combine, compress, speed plugin, database cache,gzip, http compression, js cache, minify, optimize, page cache, performance, speed, expire headers
4
  Requires at least: 4.5
5
+ Tested up to: 5.5.1
6
+ Stable tag: 1.1.8
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
145
 
146
  == Changelog ==
147
 
148
+ = 1.1.8 =
149
+ * Fix: Cache refresh issue when Varnish is disabled.
150
+ * Fix: Replaced functions deprecated in WordPress 5.5 that were causing warning messages.
151
+ * Fix: Replaced deprecated minification libraries to improve compatibility with PHP 7.x onward.
152
+ * Fix: resolved the warning generated by the Query Monitor plugin.
153
+ * Add: compatibility with PHP 7.4
154
+
155
  = 1.1.7 =
156
  * Fix: Add HTTP and HTTPS for validation of CDN integration.
157
  * Fix: Custom settings for multisite will be reapplied after Breeze reactivation.
259
 
260
  == Requirements ==
261
 
262
+ PHP 7.x ,PHP 7.4 recommended for better performance, WordPress 4.5+
vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInit34a6ba7dda38aa84053804314c2a6c35::getLoader();
vendor/bin/minifycss ADDED
@@ -0,0 +1 @@
 
1
+ ../matthiasmullie/minify/bin/minifycss
vendor/bin/minifyjs ADDED
@@ -0,0 +1 @@
 
1
+ ../matthiasmullie/minify/bin/minifyjs
vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath . '\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'MatthiasMullie\\PathConverter\\' => array($vendorDir . '/matthiasmullie/path-converter/src'),
10
+ 'MatthiasMullie\\Minify\\' => array($vendorDir . '/matthiasmullie/minify/src'),
11
+ );
vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit34a6ba7dda38aa84053804314c2a6c35
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ public static function getLoader()
17
+ {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInit34a6ba7dda38aa84053804314c2a6c35', 'loadClassLoader'), true, true);
23
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit34a6ba7dda38aa84053804314c2a6c35', 'loadClassLoader'));
25
+
26
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
+ if ($useStaticLoader) {
28
+ require_once __DIR__ . '/autoload_static.php';
29
+
30
+ call_user_func(\Composer\Autoload\ComposerStaticInit34a6ba7dda38aa84053804314c2a6c35::getInitializer($loader));
31
+ } else {
32
+ $map = require __DIR__ . '/autoload_namespaces.php';
33
+ foreach ($map as $namespace => $path) {
34
+ $loader->set($namespace, $path);
35
+ }
36
+
37
+ $map = require __DIR__ . '/autoload_psr4.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->setPsr4($namespace, $path);
40
+ }
41
+
42
+ $classMap = require __DIR__ . '/autoload_classmap.php';
43
+ if ($classMap) {
44
+ $loader->addClassMap($classMap);
45
+ }
46
+ }
47
+
48
+ $loader->register(true);
49
+
50
+ return $loader;
51
+ }
52
+ }
vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInit34a6ba7dda38aa84053804314c2a6c35
8
+ {
9
+ public static $prefixLengthsPsr4 = array (
10
+ 'M' =>
11
+ array (
12
+ 'MatthiasMullie\\PathConverter\\' => 29,
13
+ 'MatthiasMullie\\Minify\\' => 22,
14
+ ),
15
+ );
16
+
17
+ public static $prefixDirsPsr4 = array (
18
+ 'MatthiasMullie\\PathConverter\\' =>
19
+ array (
20
+ 0 => __DIR__ . '/..' . '/matthiasmullie/path-converter/src',
21
+ ),
22
+ 'MatthiasMullie\\Minify\\' =>
23
+ array (
24
+ 0 => __DIR__ . '/..' . '/matthiasmullie/minify/src',
25
+ ),
26
+ );
27
+
28
+ public static function getInitializer(ClassLoader $loader)
29
+ {
30
+ return \Closure::bind(function () use ($loader) {
31
+ $loader->prefixLengthsPsr4 = ComposerStaticInit34a6ba7dda38aa84053804314c2a6c35::$prefixLengthsPsr4;
32
+ $loader->prefixDirsPsr4 = ComposerStaticInit34a6ba7dda38aa84053804314c2a6c35::$prefixDirsPsr4;
33
+
34
+ }, null, ClassLoader::class);
35
+ }
36
+ }
vendor/composer/installed.json ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "matthiasmullie/minify",
4
+ "version": "1.3.63",
5
+ "version_normalized": "1.3.63.0",
6
+ "source": {
7
+ "type": "git",
8
+ "url": "https://github.com/matthiasmullie/minify.git",
9
+ "reference": "9ba1b459828adc13430f4dd6c49dae4950dc4117"
10
+ },
11
+ "dist": {
12
+ "type": "zip",
13
+ "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/9ba1b459828adc13430f4dd6c49dae4950dc4117",
14
+ "reference": "9ba1b459828adc13430f4dd6c49dae4950dc4117",
15
+ "shasum": ""
16
+ },
17
+ "require": {
18
+ "ext-pcre": "*",
19
+ "matthiasmullie/path-converter": "~1.1",
20
+ "php": ">=5.3.0"
21
+ },
22
+ "require-dev": {
23
+ "friendsofphp/php-cs-fixer": "~2.0",
24
+ "matthiasmullie/scrapbook": "~1.0",
25
+ "phpunit/phpunit": "~4.8"
26
+ },
27
+ "suggest": {
28
+ "psr/cache-implementation": "Cache implementation to use with Minify::cache"
29
+ },
30
+ "time": "2020-01-21T20:21:08+00:00",
31
+ "bin": [
32
+ "bin/minifycss",
33
+ "bin/minifyjs"
34
+ ],
35
+ "type": "library",
36
+ "installation-source": "dist",
37
+ "autoload": {
38
+ "psr-4": {
39
+ "MatthiasMullie\\Minify\\": "src/"
40
+ }
41
+ },
42
+ "notification-url": "https://packagist.org/downloads/",
43
+ "license": [
44
+ "MIT"
45
+ ],
46
+ "authors": [
47
+ {
48
+ "name": "Matthias Mullie",
49
+ "email": "minify@mullie.eu",
50
+ "homepage": "http://www.mullie.eu",
51
+ "role": "Developer"
52
+ }
53
+ ],
54
+ "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
55
+ "homepage": "http://www.minifier.org",
56
+ "keywords": [
57
+ "JS",
58
+ "css",
59
+ "javascript",
60
+ "minifier",
61
+ "minify"
62
+ ]
63
+ },
64
+ {
65
+ "name": "matthiasmullie/path-converter",
66
+ "version": "1.1.3",
67
+ "version_normalized": "1.1.3.0",
68
+ "source": {
69
+ "type": "git",
70
+ "url": "https://github.com/matthiasmullie/path-converter.git",
71
+ "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9"
72
+ },
73
+ "dist": {
74
+ "type": "zip",
75
+ "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9",
76
+ "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9",
77
+ "shasum": ""
78
+ },
79
+ "require": {
80
+ "ext-pcre": "*",
81
+ "php": ">=5.3.0"
82
+ },
83
+ "require-dev": {
84
+ "phpunit/phpunit": "~4.8"
85
+ },
86
+ "time": "2019-02-05T23:41:09+00:00",
87
+ "type": "library",
88
+ "installation-source": "dist",
89
+ "autoload": {
90
+ "psr-4": {
91
+ "MatthiasMullie\\PathConverter\\": "src/"
92
+ }
93
+ },
94
+ "notification-url": "https://packagist.org/downloads/",
95
+ "license": [
96
+ "MIT"
97
+ ],
98
+ "authors": [
99
+ {
100
+ "name": "Matthias Mullie",
101
+ "email": "pathconverter@mullie.eu",
102
+ "homepage": "http://www.mullie.eu",
103
+ "role": "Developer"
104
+ }
105
+ ],
106
+ "description": "Relative path converter",
107
+ "homepage": "http://github.com/matthiasmullie/path-converter",
108
+ "keywords": [
109
+ "converter",
110
+ "path",
111
+ "paths",
112
+ "relative"
113
+ ]
114
+ }
115
+ ]
vendor/matthiasmullie/minify/CONTRIBUTING.md ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # How to contribute
2
+
3
+
4
+ ## Issues
5
+
6
+ When [filing bugs](https://github.com/matthiasmullie/minify/issues/new),
7
+ try to be as thorough as possible:
8
+ * What version did you use?
9
+ * What did you try to do? ***Please post the relevant parts of your code.***
10
+ * What went wrong? ***Please include error messages, if any.***
11
+ * What was the expected result?
12
+
13
+
14
+ ## Pull requests
15
+
16
+ Bug fixes and general improvements to the existing codebase are always welcome.
17
+ New features are also welcome, but will be judged on an individual basis. If
18
+ you'd rather not risk wasting your time implementing a new feature only to see
19
+ it turned down, please start the discussion by
20
+ [opening an issue](https://github.com/matthiasmullie/minify/issues/new).
21
+
22
+ Don't forget to add your changes to the [changelog](CHANGELOG.md).
23
+
24
+
25
+ ### Testing
26
+
27
+ Please include tests for every change or addition to the code.
28
+ To run the complete test suite:
29
+
30
+ ```sh
31
+ vendor/bin/phpunit
32
+ ```
33
+
34
+ When submitting a new pull request, please make sure that that the test suite
35
+ passes (Travis CI will run it & report back on your pull request.)
36
+
37
+ To run the tests on Windows, run `tests/convert_symlinks_to_windows_style.sh`
38
+ from the command line in order to convert Linux-style test symlinks to
39
+ Windows-style.
40
+
41
+
42
+ ### Coding standards
43
+
44
+ All code must follow [PSR-2](http://www.php-fig.org/psr/psr-2/). Just make sure
45
+ to run php-cs-fixer before submitting the code, it'll take care of the
46
+ formatting for you:
47
+
48
+ ```sh
49
+ vendor/bin/php-cs-fixer fix src
50
+ vendor/bin/php-cs-fixer fix tests
51
+ ```
52
+
53
+ Document the code thoroughly!
54
+
55
+
56
+ ## License
57
+
58
+ Note that minify is MIT-licensed, which basically allows anyone to do
59
+ anything they like with it, without restriction.
vendor/matthiasmullie/minify/Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ARG version=cli
2
+ FROM php:$version
3
+
4
+ COPY . /var/www
5
+ WORKDIR /var/www
6
+
7
+ RUN apt-get update
8
+ RUN apt-get install -y zip unzip zlib1g-dev
9
+ RUN docker-php-ext-install zip
10
+ RUN docker-php-ext-install pcntl
11
+ RUN curl -sS https://getcomposer.org/installer | php
12
+ RUN mv composer.phar /usr/local/bin/composer
13
+ RUN composer install
vendor/matthiasmullie/minify/LICENSE ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2012 Matthias Mullie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
vendor/matthiasmullie/minify/bin/minifycss ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env php
2
+ <?php
3
+ use MatthiasMullie\Minify;
4
+
5
+ // command line utility to minify CSS
6
+ if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
+ // if composer install
8
+ require_once __DIR__ . '/../../../autoload.php';
9
+ } else {
10
+ require_once __DIR__ . '/../src/Minify.php';
11
+ require_once __DIR__ . '/../src/CSS.php';
12
+ require_once __DIR__ . '/../src/Exception.php';
13
+ }
14
+
15
+ error_reporting(E_ALL);
16
+ // check PHP setup for cli arguments
17
+ if (!isset($_SERVER['argv']) && !isset($argv)) {
18
+ fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
+ exit(1);
20
+ } elseif (!isset($argv)) {
21
+ $argv = $_SERVER['argv'];
22
+ }
23
+ // check if path to file given
24
+ if (!isset($argv[1])) {
25
+ fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
+ exit(1);
27
+ }
28
+ // check if script run in cli environment
29
+ if ('cli' !== php_sapi_name()) {
30
+ fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
+ exit(1);
32
+ }
33
+ // check if source file exists
34
+ if (!file_exists($argv[1])) {
35
+ fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
+ exit(1);
37
+ }
38
+
39
+ try {
40
+ $minifier = new Minify\CSS($argv[1]);
41
+ echo $minifier->minify();
42
+ } catch (Exception $e) {
43
+ fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
+ exit(1);
45
+ }
vendor/matthiasmullie/minify/bin/minifyjs ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env php
2
+ <?php
3
+ use MatthiasMullie\Minify;
4
+
5
+ // command line utility to minify JS
6
+ if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
+ // if composer install
8
+ require_once __DIR__ . '/../../../autoload.php';
9
+ } else {
10
+ require_once __DIR__ . '/../src/Minify.php';
11
+ require_once __DIR__ . '/../src/JS.php';
12
+ require_once __DIR__ . '/../src/Exception.php';
13
+ }
14
+
15
+ error_reporting(E_ALL);
16
+ // check PHP setup for cli arguments
17
+ if (!isset($_SERVER['argv']) && !isset($argv)) {
18
+ fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
+ exit(1);
20
+ } elseif (!isset($argv)) {
21
+ $argv = $_SERVER['argv'];
22
+ }
23
+ // check if path to file given
24
+ if (!isset($argv[1])) {
25
+ fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
+ exit(1);
27
+ }
28
+ // check if script run in cli environment
29
+ if ('cli' !== php_sapi_name()) {
30
+ fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
+ exit(1);
32
+ }
33
+ // check if source file exists
34
+ if (!file_exists($argv[1])) {
35
+ fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
+ exit(1);
37
+ }
38
+
39
+ try {
40
+ $minifier = new Minify\JS($argv[1]);
41
+ echo $minifier->minify();
42
+ } catch (Exception $e) {
43
+ fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
+ exit(1);
45
+ }
vendor/matthiasmullie/minify/composer.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "matthiasmullie/minify",
3
+ "type": "library",
4
+ "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
5
+ "keywords": ["minify", "minifier", "css", "js", "javascript"],
6
+ "homepage": "http://www.minifier.org",
7
+ "license": "MIT",
8
+ "authors": [
9
+ {
10
+ "name": "Matthias Mullie",
11
+ "homepage": "http://www.mullie.eu",
12
+ "email": "minify@mullie.eu",
13
+ "role": "Developer"
14
+ }
15
+ ],
16
+ "require": {
17
+ "php": ">=5.3.0",
18
+ "ext-pcre": "*",
19
+ "matthiasmullie/path-converter": "~1.1"
20
+ },
21
+ "require-dev": {
22
+ "matthiasmullie/scrapbook": "~1.0",
23
+ "phpunit/phpunit": "~4.8",
24
+ "friendsofphp/php-cs-fixer": "~2.0"
25
+ },
26
+ "suggest": {
27
+ "psr/cache-implementation": "Cache implementation to use with Minify::cache"
28
+ },
29
+ "autoload": {
30
+ "psr-4": {
31
+ "MatthiasMullie\\Minify\\": "src/"
32
+ }
33
+ },
34
+ "bin": [
35
+ "bin/minifycss",
36
+ "bin/minifyjs"
37
+ ]
38
+ }
vendor/matthiasmullie/minify/data/js/keywords_after.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ in
2
+ public
3
+ extends
4
+ private
5
+ protected
6
+ implements
7
+ instanceof
vendor/matthiasmullie/minify/data/js/keywords_before.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ do
2
+ in
3
+ let
4
+ new
5
+ var
6
+ case
7
+ else
8
+ enum
9
+ void
10
+ with
11
+ class
12
+ const
13
+ yield
14
+ delete
15
+ export
16
+ import
17
+ public
18
+ static
19
+ typeof
20
+ extends
21
+ package
22
+ private
23
+ function
24
+ protected
25
+ implements
26
+ instanceof
vendor/matthiasmullie/minify/data/js/keywords_reserved.txt ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ do
2
+ if
3
+ in
4
+ for
5
+ let
6
+ new
7
+ try
8
+ var
9
+ case
10
+ else
11
+ enum
12
+ eval
13
+ null
14
+ this
15
+ true
16
+ void
17
+ with
18
+ break
19
+ catch
20
+ class
21
+ const
22
+ false
23
+ super
24
+ throw
25
+ while
26
+ yield
27
+ delete
28
+ export
29
+ import
30
+ public
31
+ return
32
+ static
33
+ switch
34
+ typeof
35
+ default
36
+ extends
37
+ finally
38
+ package
39
+ private
40
+ continue
41
+ debugger
42
+ function
43
+ arguments
44
+ interface
45
+ protected
46
+ implements
47
+ instanceof
48
+ abstract
49
+ boolean
50
+ byte
51
+ char
52
+ double
53
+ final
54
+ float
55
+ goto
56
+ int
57
+ long
58
+ native
59
+ short
60
+ synchronized
61
+ throws
62
+ transient
63
+ volatile
vendor/matthiasmullie/minify/data/js/operators.txt ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ ~
22
+ <<
23
+ >>
24
+ >>>
25
+ ==
26
+ ===
27
+ !=
28
+ !==
29
+ >
30
+ <
31
+ >=
32
+ <=
33
+ &&
34
+ ||
35
+ !
36
+ .
37
+ [
38
+ ]
39
+ ?
40
+ :
41
+ ,
42
+ ;
43
+ (
44
+ )
45
+ {
46
+ }
vendor/matthiasmullie/minify/data/js/operators_after.txt ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ <<
22
+ >>
23
+ >>>
24
+ ==
25
+ ===
26
+ !=
27
+ !==
28
+ >
29
+ <
30
+ >=
31
+ <=
32
+ &&
33
+ ||
34
+ .
35
+ [
36
+ ]
37
+ ?
38
+ :
39
+ ,
40
+ ;
41
+ (
42
+ )
43
+ }
vendor/matthiasmullie/minify/data/js/operators_before.txt ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ ~
22
+ <<
23
+ >>
24
+ >>>
25
+ ==
26
+ ===
27
+ !=
28
+ !==
29
+ >
30
+ <
31
+ >=
32
+ <=
33
+ &&
34
+ ||
35
+ !
36
+ .
37
+ [
38
+ ?
39
+ :
40
+ ,
41
+ ;
42
+ (
43
+ {
vendor/matthiasmullie/minify/docker-compose.yml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '2.1'
2
+ services:
3
+ php:
4
+ build:
5
+ context: .
6
+ dockerfile: Dockerfile
7
+ volumes:
8
+ - ./src:/var/www/src
9
+ - ./data:/var/www/data
10
+ - ./tests:/var/www/tests
11
+ - ./phpunit.xml.dist:/var/www/phpunit.xml.dist
12
+ '7.2':
13
+ extends: php
14
+ build:
15
+ args:
16
+ version: 7.2-cli
17
+ '7.1':
18
+ extends: php
19
+ build:
20
+ args:
21
+ version: 7.1-cli
22
+ '7.0':
23
+ extends: php
24
+ build:
25
+ args:
26
+ version: 7.0-cli
27
+ '5.6':
28
+ extends: php
29
+ build:
30
+ args:
31
+ version: 5.6-cli
vendor/matthiasmullie/minify/src/CSS.php ADDED
@@ -0,0 +1,752 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ $minifier->setMaxImportSize($this->maxImportSize);
221
+ $minifier->setImportExtensions($this->importExtensions);
222
+ $importContent = $minifier->execute($source, $parents);
223
+
224
+ // check if this is only valid for certain media
225
+ if (!empty($match['media'])) {
226
+ $importContent = '@media '.$match['media'].'{'.$importContent.'}';
227
+ }
228
+
229
+ // add to replacement array
230
+ $search[] = $match[0];
231
+ $replace[] = $importContent;
232
+ }
233
+
234
+ // replace the import statements
235
+ return str_replace($search, $replace, $content);
236
+ }
237
+
238
+ /**
239
+ * Import files into the CSS, base64-ized.
240
+ *
241
+ * @url(image.jpg) images will be loaded and their content merged into the
242
+ * original file, to save HTTP requests.
243
+ *
244
+ * @param string $source The file to import files for
245
+ * @param string $content The CSS content to import files for
246
+ *
247
+ * @return string
248
+ */
249
+ protected function importFiles($source, $content)
250
+ {
251
+ $regex = '/url\((["\']?)(.+?)\\1\)/i';
252
+ if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
253
+ $search = array();
254
+ $replace = array();
255
+
256
+ // loop the matches
257
+ foreach ($matches as $match) {
258
+ $extension = substr(strrchr($match[2], '.'), 1);
259
+ if ($extension && !array_key_exists($extension, $this->importExtensions)) {
260
+ continue;
261
+ }
262
+
263
+ // get the path for the file that will be imported
264
+ $path = $match[2];
265
+ $path = dirname($source).'/'.$path;
266
+
267
+ // only replace the import with the content if we're able to get
268
+ // the content of the file, and it's relatively small
269
+ if ($this->canImportFile($path) && $this->canImportBySize($path)) {
270
+ // grab content && base64-ize
271
+ $importContent = $this->load($path);
272
+ $importContent = base64_encode($importContent);
273
+
274
+ // build replacement
275
+ $search[] = $match[0];
276
+ $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
277
+ }
278
+ }
279
+
280
+ // replace the import statements
281
+ $content = str_replace($search, $replace, $content);
282
+ }
283
+
284
+ return $content;
285
+ }
286
+
287
+ /**
288
+ * Minify the data.
289
+ * Perform CSS optimizations.
290
+ *
291
+ * @param string[optional] $path Path to write the data to
292
+ * @param string[] $parents Parent paths, for circular reference checks
293
+ *
294
+ * @return string The minified data
295
+ */
296
+ public function execute($path = null, $parents = array())
297
+ {
298
+ $content = '';
299
+
300
+ // loop CSS data (raw data and files)
301
+ foreach ($this->data as $source => $css) {
302
+ /*
303
+ * Let's first take out strings & comments, since we can't just
304
+ * remove whitespace anywhere. If whitespace occurs inside a string,
305
+ * we should leave it alone. E.g.:
306
+ * p { content: "a test" }
307
+ */
308
+ $this->extractStrings();
309
+ $this->stripComments();
310
+ $this->extractCalcs();
311
+ $css = $this->replace($css);
312
+
313
+ $css = $this->stripWhitespace($css);
314
+ $css = $this->shortenColors($css);
315
+ $css = $this->shortenZeroes($css);
316
+ $css = $this->shortenFontWeights($css);
317
+ $css = $this->stripEmptyTags($css);
318
+
319
+ // restore the string we've extracted earlier
320
+ $css = $this->restoreExtractedData($css);
321
+
322
+ $source = is_int($source) ? '' : $source;
323
+ $parents = $source ? array_merge($parents, array($source)) : $parents;
324
+ $css = $this->combineImports($source, $css, $parents);
325
+ $css = $this->importFiles($source, $css);
326
+
327
+ /*
328
+ * If we'll save to a new path, we'll have to fix the relative paths
329
+ * to be relative no longer to the source file, but to the new path.
330
+ * If we don't write to a file, fall back to same path so no
331
+ * conversion happens (because we still want it to go through most
332
+ * of the move code, which also addresses url() & @import syntax...)
333
+ */
334
+ $converter = $this->getPathConverter($source, $path ?: $source);
335
+ $css = $this->move($converter, $css);
336
+
337
+ // combine css
338
+ $content .= $css;
339
+ }
340
+
341
+ $content = $this->moveImportsToTop($content);
342
+
343
+ return $content;
344
+ }
345
+
346
+ /**
347
+ * Moving a css file should update all relative urls.
348
+ * Relative references (e.g. ../images/image.gif) in a certain css file,
349
+ * will have to be updated when a file is being saved at another location
350
+ * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
351
+ *
352
+ * @param ConverterInterface $converter Relative path converter
353
+ * @param string $content The CSS content to update relative urls for
354
+ *
355
+ * @return string
356
+ */
357
+ protected function move(ConverterInterface $converter, $content)
358
+ {
359
+ /*
360
+ * Relative path references will usually be enclosed by url(). @import
361
+ * is an exception, where url() is not necessary around the path (but is
362
+ * allowed).
363
+ * This *could* be 1 regular expression, where both regular expressions
364
+ * in this array are on different sides of a |. But we're using named
365
+ * patterns in both regexes, the same name on both regexes. This is only
366
+ * possible with a (?J) modifier, but that only works after a fairly
367
+ * recent PCRE version. That's why I'm doing 2 separate regular
368
+ * expressions & combining the matches after executing of both.
369
+ */
370
+ $relativeRegexes = array(
371
+ // url(xxx)
372
+ '/
373
+ # open url()
374
+ url\(
375
+
376
+ \s*
377
+
378
+ # open path enclosure
379
+ (?P<quotes>["\'])?
380
+
381
+ # fetch path
382
+ (?P<path>.+?)
383
+
384
+ # close path enclosure
385
+ (?(quotes)(?P=quotes))
386
+
387
+ \s*
388
+
389
+ # close url()
390
+ \)
391
+
392
+ /ix',
393
+
394
+ // @import "xxx"
395
+ '/
396
+ # import statement
397
+ @import
398
+
399
+ # whitespace
400
+ \s+
401
+
402
+ # we don\'t have to check for @import url(), because the
403
+ # condition above will already catch these
404
+
405
+ # open path enclosure
406
+ (?P<quotes>["\'])
407
+
408
+ # fetch path
409
+ (?P<path>.+?)
410
+
411
+ # close path enclosure
412
+ (?P=quotes)
413
+
414
+ /ix',
415
+ );
416
+
417
+ // find all relative urls in css
418
+ $matches = array();
419
+ foreach ($relativeRegexes as $relativeRegex) {
420
+ if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
421
+ $matches = array_merge($matches, $regexMatches);
422
+ }
423
+ }
424
+
425
+ $search = array();
426
+ $replace = array();
427
+
428
+ // loop all urls
429
+ foreach ($matches as $match) {
430
+ // determine if it's a url() or an @import match
431
+ $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
432
+
433
+ $url = $match['path'];
434
+ if ($this->canImportByPath($url)) {
435
+ // attempting to interpret GET-params makes no sense, so let's discard them for awhile
436
+ $params = strrchr($url, '?');
437
+ $url = $params ? substr($url, 0, -strlen($params)) : $url;
438
+
439
+ // fix relative url
440
+ $url = $converter->convert($url);
441
+
442
+ // now that the path has been converted, re-apply GET-params
443
+ $url .= $params;
444
+ }
445
+
446
+ /*
447
+ * Urls with control characters above 0x7e should be quoted.
448
+ * According to Mozilla's parser, whitespace is only allowed at the
449
+ * end of unquoted urls.
450
+ * Urls with `)` (as could happen with data: uris) should also be
451
+ * quoted to avoid being confused for the url() closing parentheses.
452
+ * And urls with a # have also been reported to cause issues.
453
+ * Urls with quotes inside should also remain escaped.
454
+ *
455
+ * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
456
+ * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
457
+ * @see https://github.com/matthiasmullie/minify/issues/193
458
+ */
459
+ $url = trim($url);
460
+ if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
461
+ $url = $match['quotes'] . $url . $match['quotes'];
462
+ }
463
+
464
+ // build replacement
465
+ $search[] = $match[0];
466
+ if ($type === 'url') {
467
+ $replace[] = 'url('.$url.')';
468
+ } elseif ($type === 'import') {
469
+ $replace[] = '@import "'.$url.'"';
470
+ }
471
+ }
472
+
473
+ // replace urls
474
+ return str_replace($search, $replace, $content);
475
+ }
476
+
477
+ /**
478
+ * Shorthand hex color codes.
479
+ * #FF0000 -> #F00.
480
+ *
481
+ * @param string $content The CSS content to shorten the hex color codes for
482
+ *
483
+ * @return string
484
+ */
485
+ protected function shortenColors($content)
486
+ {
487
+ $content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?:([0-9a-z])\\4)?(?=[; }])/i', '#$1$2$3$4', $content);
488
+
489
+ // remove alpha channel if it's pointless...
490
+ $content = preg_replace('/(?<=[: ])#([0-9a-z]{6})ff?(?=[; }])/i', '#$1', $content);
491
+ $content = preg_replace('/(?<=[: ])#([0-9a-z]{3})f?(?=[; }])/i', '#$1', $content);
492
+
493
+ $colors = array(
494
+ // we can shorten some even more by replacing them with their color name
495
+ '#F0FFFF' => 'azure',
496
+ '#F5F5DC' => 'beige',
497
+ '#A52A2A' => 'brown',
498
+ '#FF7F50' => 'coral',
499
+ '#FFD700' => 'gold',
500
+ '#808080' => 'gray',
501
+ '#008000' => 'green',
502
+ '#4B0082' => 'indigo',
503
+ '#FFFFF0' => 'ivory',
504
+ '#F0E68C' => 'khaki',
505
+ '#FAF0E6' => 'linen',
506
+ '#800000' => 'maroon',
507
+ '#000080' => 'navy',
508
+ '#808000' => 'olive',
509
+ '#CD853F' => 'peru',
510
+ '#FFC0CB' => 'pink',
511
+ '#DDA0DD' => 'plum',
512
+ '#800080' => 'purple',
513
+ '#F00' => 'red',
514
+ '#FA8072' => 'salmon',
515
+ '#A0522D' => 'sienna',
516
+ '#C0C0C0' => 'silver',
517
+ '#FFFAFA' => 'snow',
518
+ '#D2B48C' => 'tan',
519
+ '#FF6347' => 'tomato',
520
+ '#EE82EE' => 'violet',
521
+ '#F5DEB3' => 'wheat',
522
+ // or the other way around
523
+ 'WHITE' => '#fff',
524
+ 'BLACK' => '#000',
525
+ );
526
+
527
+ return preg_replace_callback(
528
+ '/(?<=[: ])('.implode('|', array_keys($colors)).')(?=[; }])/i',
529
+ function ($match) use ($colors) {
530
+ return $colors[strtoupper($match[0])];
531
+ },
532
+ $content
533
+ );
534
+ }
535
+
536
+ /**
537
+ * Shorten CSS font weights.
538
+ *
539
+ * @param string $content The CSS content to shorten the font weights for
540
+ *
541
+ * @return string
542
+ */
543
+ protected function shortenFontWeights($content)
544
+ {
545
+ $weights = array(
546
+ 'normal' => 400,
547
+ 'bold' => 700,
548
+ );
549
+
550
+ $callback = function ($match) use ($weights) {
551
+ return $match[1].$weights[$match[2]];
552
+ };
553
+
554
+ return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
555
+ }
556
+
557
+ /**
558
+ * Shorthand 0 values to plain 0, instead of e.g. -0em.
559
+ *
560
+ * @param string $content The CSS content to shorten the zero values for
561
+ *
562
+ * @return string
563
+ */
564
+ protected function shortenZeroes($content)
565
+ {
566
+ // we don't want to strip units in `calc()` expressions:
567
+ // `5px - 0px` is valid, but `5px - 0` is not
568
+ // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
569
+ // `10 * 0` is invalid
570
+ // we've extracted calcs earlier, so we don't need to worry about this
571
+
572
+ // reusable bits of code throughout these regexes:
573
+ // before & after are used to make sure we don't match lose unintended
574
+ // 0-like values (e.g. in #000, or in http://url/1.0)
575
+ // units can be stripped from 0 values, or used to recognize non 0
576
+ // values (where wa may be able to strip a .0 suffix)
577
+ $before = '(?<=[:(, ])';
578
+ $after = '(?=[ ,);}])';
579
+ $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
580
+
581
+ // strip units after zeroes (0px -> 0)
582
+ // NOTE: it should be safe to remove all units for a 0 value, but in
583
+ // practice, Webkit (especially Safari) seems to stumble over at least
584
+ // 0%, potentially other units as well. Only stripping 'px' for now.
585
+ // @see https://github.com/matthiasmullie/minify/issues/60
586
+ $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
587
+
588
+ // strip 0-digits (.0 -> 0)
589
+ $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
590
+ // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
591
+ $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
592
+ // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
593
+ $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
594
+ // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
595
+ $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
596
+
597
+ // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
598
+ $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
599
+
600
+ // IE doesn't seem to understand a unitless flex-basis value (correct -
601
+ // it goes against the spec), so let's add it in again (make it `%`,
602
+ // which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
603
+ // @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
604
+ $content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
605
+ $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
606
+
607
+ return $content;
608
+ }
609
+
610
+ /**
611
+ * Strip empty tags from source code.
612
+ *
613
+ * @param string $content
614
+ *
615
+ * @return string
616
+ */
617
+ protected function stripEmptyTags($content)
618
+ {
619
+ $content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content);
620
+ $content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content);
621
+
622
+ return $content;
623
+ }
624
+
625
+ /**
626
+ * Strip comments from source code.
627
+ */
628
+ protected function stripComments()
629
+ {
630
+ // PHP only supports $this inside anonymous functions since 5.4
631
+ $minifier = $this;
632
+ $callback = function ($match) use ($minifier) {
633
+ $count = count($minifier->extracted);
634
+ $placeholder = '/*'.$count.'*/';
635
+ $minifier->extracted[$placeholder] = $match[0];
636
+
637
+ return $placeholder;
638
+ };
639
+ $this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
640
+
641
+ $this->registerPattern('/\/\*.*?\*\//s', '');
642
+ }
643
+
644
+ /**
645
+ * Strip whitespace.
646
+ *
647
+ * @param string $content The CSS content to strip the whitespace for
648
+ *
649
+ * @return string
650
+ */
651
+ protected function stripWhitespace($content)
652
+ {
653
+ // remove leading & trailing whitespace
654
+ $content = preg_replace('/^\s*/m', '', $content);
655
+ $content = preg_replace('/\s*$/m', '', $content);
656
+
657
+ // replace newlines with a single space
658
+ $content = preg_replace('/\s+/', ' ', $content);
659
+
660
+ // remove whitespace around meta characters
661
+ // inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
662
+ $content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
663
+ $content = preg_replace('/([\[(:>\+])\s+/', '$1', $content);
664
+ $content = preg_replace('/\s+([\]\)>\+])/', '$1', $content);
665
+ $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
666
+
667
+ // whitespace around + and - can only be stripped inside some pseudo-
668
+ // classes, like `:nth-child(3+2n)`
669
+ // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
670
+ // selectors like `div.weird- p`
671
+ $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
672
+ $content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
673
+
674
+ // remove semicolon/whitespace followed by closing bracket
675
+ $content = str_replace(';}', '}', $content);
676
+
677
+ return trim($content);
678
+ }
679
+
680
+ /**
681
+ * Replace all `calc()` occurrences.
682
+ */
683
+ protected function extractCalcs()
684
+ {
685
+ // PHP only supports $this inside anonymous functions since 5.4
686
+ $minifier = $this;
687
+ $callback = function ($match) use ($minifier) {
688
+ $length = strlen($match[1]);
689
+ $expr = '';
690
+ $opened = 0;
691
+
692
+ for ($i = 0; $i < $length; $i++) {
693
+ $char = $match[1][$i];
694
+ $expr .= $char;
695
+ if ($char === '(') {
696
+ $opened++;
697
+ } elseif ($char === ')' && --$opened === 0) {
698
+ break;
699
+ }
700
+ }
701
+ $rest = str_replace($expr, '', $match[1]);
702
+ $expr = trim(substr($expr, 1, -1));
703
+
704
+ $count = count($minifier->extracted);
705
+ $placeholder = 'calc('.$count.')';
706
+ $minifier->extracted[$placeholder] = 'calc('.$expr.')';
707
+
708
+ return $placeholder.$rest;
709
+ };
710
+
711
+ $this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/', $callback);
712
+ $this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/m', $callback);
713
+ }
714
+
715
+ /**
716
+ * Check if file is small enough to be imported.
717
+ *
718
+ * @param string $path The path to the file
719
+ *
720
+ * @return bool
721
+ */
722
+ protected function canImportBySize($path)
723
+ {
724
+ return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
725
+ }
726
+
727
+ /**
728
+ * Check if file a file can be imported, going by the path.
729
+ *
730
+ * @param string $path
731
+ *
732
+ * @return bool
733
+ */
734
+ protected function canImportByPath($path)
735
+ {
736
+ return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
737
+ }
738
+
739
+ /**
740
+ * Return a converter to update relative paths to be relative to the new
741
+ * destination.
742
+ *
743
+ * @param string $source
744
+ * @param string $target
745
+ *
746
+ * @return ConverterInterface
747
+ */
748
+ protected function getPathConverter($source, $target)
749
+ {
750
+ return new Converter($source, $target);
751
+ }
752
+ }
vendor/matthiasmullie/minify/src/Exception.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Base Exception
4
+ *
5
+ * @deprecated Use Exceptions\BasicException instead
6
+ *
7
+ * @author Matthias Mullie <minify@mullie.eu>
8
+ */
9
+ namespace 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
+ }
vendor/matthiasmullie/minify/src/Exceptions/BasicException.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }
vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }
vendor/matthiasmullie/minify/src/Exceptions/IOException.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }
vendor/matthiasmullie/minify/src/JS.php ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 = (string) 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
+ // PHP only supports $this inside anonymous functions since 5.4
199
+ $minifier = $this;
200
+ $callback = function ($match) use ($minifier) {
201
+ $count = count($minifier->extracted);
202
+ $placeholder = '/*'.$count.'*/';
203
+ $minifier->extracted[$placeholder] = $match[0];
204
+
205
+ return $placeholder;
206
+ };
207
+ // multi-line comments
208
+ $this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
209
+ $this->registerPattern('/\/\*.*?\*\//s', '');
210
+
211
+ // single-line comments
212
+ $this->registerPattern('/\/\/.*$/m', '');
213
+ }
214
+
215
+ /**
216
+ * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
217
+ *
218
+ * The content inside the regex can contain characters that may be confused
219
+ * for JS code: e.g. it could contain whitespace it needs to match & we
220
+ * don't want to strip whitespace in there.
221
+ *
222
+ * The regex can be pretty simple: we don't have to care about comments,
223
+ * (which also use slashes) because stripComments() will have stripped those
224
+ * already.
225
+ *
226
+ * This method will replace all string content with simple REGEX#
227
+ * placeholder text, so we've rid all regular expressions from characters
228
+ * that may be misinterpreted. Original regex content will be saved in
229
+ * $this->extracted and after doing all other minifying, we can restore the
230
+ * original content via restoreRegex()
231
+ */
232
+ protected function extractRegex()
233
+ {
234
+ // PHP only supports $this inside anonymous functions since 5.4
235
+ $minifier = $this;
236
+ $callback = function ($match) use ($minifier) {
237
+ $count = count($minifier->extracted);
238
+ $placeholder = '"'.$count.'"';
239
+ $minifier->extracted[$placeholder] = $match[0];
240
+
241
+ return $placeholder;
242
+ };
243
+
244
+ // match all chars except `/` and `\`
245
+ // `\` is allowed though, along with whatever char follows (which is the
246
+ // one being escaped)
247
+ // this should allow all chars, except for an unescaped `/` (= the one
248
+ // closing the regex)
249
+ // then also ignore bare `/` inside `[]`, where they don't need to be
250
+ // escaped: anything inside `[]` can be ignored safely
251
+ $pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
252
+
253
+ // a regular expression can only be followed by a few operators or some
254
+ // of the RegExp methods (a `\` followed by a variable or value is
255
+ // likely part of a division, not a regex)
256
+ $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
257
+ $before = '([=:,;\+\-\*\/\}\(\{\[&\|!]|^|'.implode('|', $keywords).')\s*';
258
+ $propertiesAndMethods = array(
259
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
260
+ 'constructor',
261
+ 'flags',
262
+ 'global',
263
+ 'ignoreCase',
264
+ 'multiline',
265
+ 'source',
266
+ 'sticky',
267
+ 'unicode',
268
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
269
+ 'compile(',
270
+ 'exec(',
271
+ 'test(',
272
+ 'toSource(',
273
+ 'toString(',
274
+ );
275
+ $delimiters = array_fill(0, count($propertiesAndMethods), '/');
276
+ $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
277
+ $after = '(?=\s*([\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).')))';
278
+ $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
279
+
280
+ // regular expressions following a `)` are rather annoying to detect...
281
+ // quite often, `/` after `)` is a division operator & if it happens to
282
+ // be followed by another one (or a comment), it is likely to be
283
+ // confused for a regular expression
284
+ // however, it's perfectly possible for a regex to follow a `)`: after
285
+ // a single-line `if()`, `while()`, ... statement, for example
286
+ // since, when they occur like that, they're always the start of a
287
+ // statement, there's only a limited amount of ways they can be useful:
288
+ // by calling the regex methods directly
289
+ // if a regex following `)` is not followed by `.<property or method>`,
290
+ // it's quite likely not a regex
291
+ $before = '\)\s*';
292
+ $after = '(?=\s*\.('.implode('|', $propertiesAndMethods).'))';
293
+ $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
294
+
295
+ // 1 more edge case: a regex can be followed by a lot more operators or
296
+ // keywords if there's a newline (ASI) in between, where the operator
297
+ // actually starts a new statement
298
+ // (https://github.com/matthiasmullie/minify/issues/56)
299
+ $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
300
+ $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
301
+ $after = '(?=\s*\n\s*('.implode('|', $operators).'))';
302
+ $this->registerPattern('/'.$pattern.$after.'/', $callback);
303
+ }
304
+
305
+ /**
306
+ * Strip whitespace.
307
+ *
308
+ * We won't strip *all* whitespace, but as much as possible. The thing that
309
+ * we'll preserve are newlines we're unsure about.
310
+ * JavaScript doesn't require statements to be terminated with a semicolon.
311
+ * It will automatically fix missing semicolons with ASI (automatic semi-
312
+ * colon insertion) at the end of line causing errors (without semicolon.)
313
+ *
314
+ * Because it's sometimes hard to tell if a newline is part of a statement
315
+ * that should be terminated or not, we'll just leave some of them alone.
316
+ *
317
+ * @param string $content The content to strip the whitespace for
318
+ *
319
+ * @return string
320
+ */
321
+ protected function stripWhitespace($content)
322
+ {
323
+ // uniform line endings, make them all line feed
324
+ $content = str_replace(array("\r\n", "\r"), "\n", $content);
325
+
326
+ // collapse all non-line feed whitespace into a single space
327
+ $content = preg_replace('/[^\S\n]+/', ' ', $content);
328
+
329
+ // strip leading & trailing whitespace
330
+ $content = str_replace(array(" \n", "\n "), "\n", $content);
331
+
332
+ // collapse consecutive line feeds into just 1
333
+ $content = preg_replace('/\n+/', "\n", $content);
334
+
335
+ $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
336
+ $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
337
+ $operators = $this->getOperatorsForRegex($this->operators, '/');
338
+ $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
339
+ $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
340
+
341
+ // strip whitespace that ends in (or next line begin with) an operator
342
+ // that allows statements to be broken up over multiple lines
343
+ unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
344
+ $content = preg_replace(
345
+ array(
346
+ '/('.implode('|', $operatorsBefore).')\s+/',
347
+ '/\s+('.implode('|', $operatorsAfter).')/',
348
+ ),
349
+ '\\1',
350
+ $content
351
+ );
352
+
353
+ // make sure + and - can't be mistaken for, or joined into ++ and --
354
+ $content = preg_replace(
355
+ array(
356
+ '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
357
+ '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
358
+ ),
359
+ '\\1',
360
+ $content
361
+ );
362
+
363
+ // collapse whitespace around reserved words into single space
364
+ $content = preg_replace('/(^|[;\}\s])\K('.implode('|', $keywordsBefore).')\s+/', '\\2 ', $content);
365
+ $content = preg_replace('/\s+('.implode('|', $keywordsAfter).')(?=([;\{\s]|$))/', ' \\1', $content);
366
+
367
+ /*
368
+ * We didn't strip whitespace after a couple of operators because they
369
+ * could be used in different contexts and we can't be sure it's ok to
370
+ * strip the newlines. However, we can safely strip any non-line feed
371
+ * whitespace that follows them.
372
+ */
373
+ $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
374
+ $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
375
+ $content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
376
+ $content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
377
+
378
+ /*
379
+ * Whitespace after `return` can be omitted in a few occasions
380
+ * (such as when followed by a string or regex)
381
+ * Same for whitespace in between `)` and `{`, or between `{` and some
382
+ * keywords.
383
+ */
384
+ $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
385
+ $content = preg_replace('/\)\s+\{/', '){', $content);
386
+ $content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
387
+
388
+ /*
389
+ * Get rid of double semicolons, except where they can be used like:
390
+ * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
391
+ * I'll safeguard these double semicolons inside for-loops by
392
+ * temporarily replacing them with an invalid condition: they won't have
393
+ * a double semicolon and will be easy to spot to restore afterwards.
394
+ */
395
+ $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
396
+ $content = preg_replace('/;+/', ';', $content);
397
+ $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
398
+
399
+ /*
400
+ * Next, we'll be removing all semicolons where ASI kicks in.
401
+ * for-loops however, can have an empty body (ending in only a
402
+ * semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
403
+ * Here, nothing happens during the loop; it's just used to keep
404
+ * increasing `i`. With that ; omitted, the next line would be expected
405
+ * to be the for-loop's body... Same goes for while loops.
406
+ * I'm going to double that semicolon (if any) so after the next line,
407
+ * which strips semicolons here & there, we're still left with this one.
408
+ */
409
+ $content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
410
+ $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
411
+ /*
412
+ * Below will also keep `;` after a `do{}while();` along with `while();`
413
+ * While these could be stripped after do-while, detecting this
414
+ * distinction is cumbersome, so I'll play it safe and make sure `;`
415
+ * after any kind of `while` is kept.
416
+ */
417
+ $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
418
+
419
+ /*
420
+ * We also can't strip empty else-statements. Even though they're
421
+ * useless and probably shouldn't be in the code in the first place, we
422
+ * shouldn't be stripping the `;` that follows it as it breaks the code.
423
+ * We can just remove those useless else-statements completely.
424
+ *
425
+ * @see https://github.com/matthiasmullie/minify/issues/91
426
+ */
427
+ $content = preg_replace('/else;/s', '', $content);
428
+
429
+ /*
430
+ * We also don't really want to terminate statements followed by closing
431
+ * curly braces (which we've ignored completely up until now) or end-of-
432
+ * script: ASI will kick in here & we're all about minifying.
433
+ * Semicolons at beginning of the file don't make any sense either.
434
+ */
435
+ $content = preg_replace('/;(\}|$)/s', '\\1', $content);
436
+ $content = ltrim($content, ';');
437
+
438
+ // get rid of remaining whitespace af beginning/end
439
+ return trim($content);
440
+ }
441
+
442
+ /**
443
+ * We'll strip whitespace around certain operators with regular expressions.
444
+ * This will prepare the given array by escaping all characters.
445
+ *
446
+ * @param string[] $operators
447
+ * @param string $delimiter
448
+ *
449
+ * @return string[]
450
+ */
451
+ protected function getOperatorsForRegex(array $operators, $delimiter = '/')
452
+ {
453
+ // escape operators for use in regex
454
+ $delimiters = array_fill(0, count($operators), $delimiter);
455
+ $escaped = array_map('preg_quote', $operators, $delimiters);
456
+
457
+ $operators = array_combine($operators, $escaped);
458
+
459
+ // ignore + & - for now, they'll get special treatment
460
+ unset($operators['+'], $operators['-']);
461
+
462
+ // dot can not just immediately follow a number; it can be confused for
463
+ // decimal point, or calling a method on it, e.g. 42 .toString()
464
+ $operators['.'] = '(?<![0-9]\s)\.';
465
+
466
+ // don't confuse = with other assignment shortcuts (e.g. +=)
467
+ $chars = preg_quote('+-*\=<>%&|', $delimiter);
468
+ $operators['='] = '(?<!['.$chars.'])\=';
469
+
470
+ return $operators;
471
+ }
472
+
473
+ /**
474
+ * We'll strip whitespace around certain keywords with regular expressions.
475
+ * This will prepare the given array by escaping all characters.
476
+ *
477
+ * @param string[] $keywords
478
+ * @param string $delimiter
479
+ *
480
+ * @return string[]
481
+ */
482
+ protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
483
+ {
484
+ // escape keywords for use in regex
485
+ $delimiter = array_fill(0, count($keywords), $delimiter);
486
+ $escaped = array_map('preg_quote', $keywords, $delimiter);
487
+
488
+ // add word boundaries
489
+ array_walk($keywords, function ($value) {
490
+ return '\b'.$value.'\b';
491
+ });
492
+
493
+ $keywords = array_combine($keywords, $escaped);
494
+
495
+ return $keywords;
496
+ }
497
+
498
+ /**
499
+ * Replaces all occurrences of array['key'] by array.key.
500
+ *
501
+ * @param string $content
502
+ *
503
+ * @return string
504
+ */
505
+ protected function propertyNotation($content)
506
+ {
507
+ // PHP only supports $this inside anonymous functions since 5.4
508
+ $minifier = $this;
509
+ $keywords = $this->keywordsReserved;
510
+ $callback = function ($match) use ($minifier, $keywords) {
511
+ $property = trim($minifier->extracted[$match[1]], '\'"');
512
+
513
+ /*
514
+ * Check if the property is a reserved keyword. In this context (as
515
+ * property of an object literal/array) it shouldn't matter, but IE8
516
+ * freaks out with "Expected identifier".
517
+ */
518
+ if (in_array($property, $keywords)) {
519
+ return $match[0];
520
+ }
521
+
522
+ /*
523
+ * See if the property is in a variable-like format (e.g.
524
+ * array['key-here'] can't be replaced by array.key-here since '-'
525
+ * is not a valid character there.
526
+ */
527
+ if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
528
+ return $match[0];
529
+ }
530
+
531
+ return '.'.$property;
532
+ };
533
+
534
+ /*
535
+ * Figure out if previous character is a variable name (of the array
536
+ * we want to use property notation on) - this is to make sure
537
+ * standalone ['value'] arrays aren't confused for keys-of-an-array.
538
+ * We can (and only have to) check the last character, because PHP's
539
+ * regex implementation doesn't allow unfixed-length look-behind
540
+ * assertions.
541
+ */
542
+ preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
543
+ $previousChar = $previousChar[1];
544
+
545
+ /*
546
+ * Make sure word preceding the ['value'] is not a keyword, e.g.
547
+ * return['x']. Because -again- PHP's regex implementation doesn't allow
548
+ * unfixed-length look-behind assertions, I'm just going to do a lot of
549
+ * separate look-behind assertions, one for each keyword.
550
+ */
551
+ $keywords = $this->getKeywordsForRegex($keywords);
552
+ $keywords = '(?<!'.implode(')(?<!', $keywords).')';
553
+
554
+ return preg_replace_callback('/(?<='.$previousChar.'|\])'.$keywords.'\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
555
+ }
556
+
557
+ /**
558
+ * Replaces true & false by !0 and !1.
559
+ *
560
+ * @param string $content
561
+ *
562
+ * @return string
563
+ */
564
+ protected function shortenBools($content)
565
+ {
566
+ /*
567
+ * 'true' or 'false' could be used as property names (which may be
568
+ * followed by whitespace) - we must not replace those!
569
+ * Since PHP doesn't allow variable-length (to account for the
570
+ * whitespace) lookbehind assertions, I need to capture the leading
571
+ * character and check if it's a `.`
572
+ */
573
+ $callback = function ($match) {
574
+ if (trim($match[1]) === '.') {
575
+ return $match[0];
576
+ }
577
+
578
+ return $match[1].($match[2] === 'true' ? '!0' : '!1');
579
+ };
580
+ $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
581
+
582
+ // for(;;) is exactly the same as while(true), but shorter :)
583
+ $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
584
+
585
+ // now make sure we didn't turn any do ... while(true) into do ... for(;;)
586
+ preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
587
+
588
+ // go backward to make sure positional offsets aren't altered when $content changes
589
+ $dos = array_reverse($dos);
590
+ foreach ($dos as $do) {
591
+ $offsetDo = $do[0][1];
592
+
593
+ // find all `while` (now `for`) following `do`: one of those must be
594
+ // associated with the `do` and be turned back into `while`
595
+ preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
596
+ foreach ($whiles as $while) {
597
+ $offsetWhile = $while[0][1];
598
+
599
+ $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
600
+ $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
601
+ if ($open === $close) {
602
+ // only restore `while` if amount of `{` and `}` are the same;
603
+ // otherwise, that `for` isn't associated with this `do`
604
+ $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
605
+ break;
606
+ }
607
+ }
608
+ }
609
+
610
+ return $content;
611
+ }
612
+ }
vendor/matthiasmullie/minify/src/Minify.php ADDED
@@ -0,0 +1,497 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Add a file to be minified.
104
+ *
105
+ * @param string|string[] $data
106
+ *
107
+ * @return static
108
+ *
109
+ * @throws IOException
110
+ */
111
+ public function addFile($data /* $data = null, ... */)
112
+ {
113
+ // bogus "usage" of parameter $data: scrutinizer warns this variable is
114
+ // not used (we're using func_get_args instead to support overloading),
115
+ // but it still needs to be defined because it makes no sense to have
116
+ // this function without argument :)
117
+ $args = array($data) + func_get_args();
118
+
119
+ // this method can be overloaded
120
+ foreach ($args as $path) {
121
+ if (is_array($path)) {
122
+ call_user_func_array(array($this, 'addFile'), $path);
123
+ continue;
124
+ }
125
+
126
+ // redefine var
127
+ $path = (string) $path;
128
+
129
+ // check if we can read the file
130
+ if (!$this->canImportFile($path)) {
131
+ throw new IOException('The file "'.$path.'" could not be opened for reading. Check if PHP has enough permissions.');
132
+ }
133
+
134
+ $this->add($path);
135
+ }
136
+
137
+ return $this;
138
+ }
139
+
140
+ /**
141
+ * Minify the data & (optionally) saves it to a file.
142
+ *
143
+ * @param string[optional] $path Path to write the data to
144
+ *
145
+ * @return string The minified data
146
+ */
147
+ public function minify($path = null)
148
+ {
149
+ $content = $this->execute($path);
150
+
151
+ // save to path
152
+ if ($path !== null) {
153
+ $this->save($content, $path);
154
+ }
155
+
156
+ return $content;
157
+ }
158
+
159
+ /**
160
+ * Minify & gzip the data & (optionally) saves it to a file.
161
+ *
162
+ * @param string[optional] $path Path to write the data to
163
+ * @param int[optional] $level Compression level, from 0 to 9
164
+ *
165
+ * @return string The minified & gzipped data
166
+ */
167
+ public function gzip($path = null, $level = 9)
168
+ {
169
+ $content = $this->execute($path);
170
+ $content = gzencode($content, $level, FORCE_GZIP);
171
+
172
+ // save to path
173
+ if ($path !== null) {
174
+ $this->save($content, $path);
175
+ }
176
+
177
+ return $content;
178
+ }
179
+
180
+ /**
181
+ * Minify the data & write it to a CacheItemInterface object.
182
+ *
183
+ * @param CacheItemInterface $item Cache item to write the data to
184
+ *
185
+ * @return CacheItemInterface Cache item with the minifier data
186
+ */
187
+ public function cache(CacheItemInterface $item)
188
+ {
189
+ $content = $this->execute();
190
+ $item->set($content);
191
+
192
+ return $item;
193
+ }
194
+
195
+ /**
196
+ * Minify the data.
197
+ *
198
+ * @param string[optional] $path Path to write the data to
199
+ *
200
+ * @return string The minified data
201
+ */
202
+ abstract public function execute($path = null);
203
+
204
+ /**
205
+ * Load data.
206
+ *
207
+ * @param string $data Either a path to a file or the content itself
208
+ *
209
+ * @return string
210
+ */
211
+ protected function load($data)
212
+ {
213
+ // check if the data is a file
214
+ if ($this->canImportFile($data)) {
215
+ $data = file_get_contents($data);
216
+
217
+ // strip BOM, if any
218
+ if (substr($data, 0, 3) == "\xef\xbb\xbf") {
219
+ $data = substr($data, 3);
220
+ }
221
+ }
222
+
223
+ return $data;
224
+ }
225
+
226
+ /**
227
+ * Save to file.
228
+ *
229
+ * @param string $content The minified data
230
+ * @param string $path The path to save the minified data to
231
+ *
232
+ * @throws IOException
233
+ */
234
+ protected function save($content, $path)
235
+ {
236
+ $handler = $this->openFileForWriting($path);
237
+
238
+ $this->writeToFile($handler, $content);
239
+
240
+ @fclose($handler);
241
+ }
242
+
243
+ /**
244
+ * Register a pattern to execute against the source content.
245
+ *
246
+ * @param string $pattern PCRE pattern
247
+ * @param string|callable $replacement Replacement value for matched pattern
248
+ */
249
+ protected function registerPattern($pattern, $replacement = '')
250
+ {
251
+ // study the pattern, we'll execute it more than once
252
+ $pattern .= 'S';
253
+
254
+ $this->patterns[] = array($pattern, $replacement);
255
+ }
256
+
257
+ /**
258
+ * We can't "just" run some regular expressions against JavaScript: it's a
259
+ * complex language. E.g. having an occurrence of // xyz would be a comment,
260
+ * unless it's used within a string. Of you could have something that looks
261
+ * like a 'string', but inside a comment.
262
+ * The only way to accurately replace these pieces is to traverse the JS one
263
+ * character at a time and try to find whatever starts first.
264
+ *
265
+ * @param string $content The content to replace patterns in
266
+ *
267
+ * @return string The (manipulated) content
268
+ */
269
+ protected function replace($content)
270
+ {
271
+ $processed = '';
272
+ $positions = array_fill(0, count($this->patterns), -1);
273
+ $matches = array();
274
+
275
+ while ($content) {
276
+ // find first match for all patterns
277
+ foreach ($this->patterns as $i => $pattern) {
278
+ list($pattern, $replacement) = $pattern;
279
+
280
+ // we can safely ignore patterns for positions we've unset earlier,
281
+ // because we know these won't show up anymore
282
+ if (array_key_exists($i, $positions) == false) {
283
+ continue;
284
+ }
285
+
286
+ // no need to re-run matches that are still in the part of the
287
+ // content that hasn't been processed
288
+ if ($positions[$i] >= 0) {
289
+ continue;
290
+ }
291
+
292
+ $match = null;
293
+ if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
294
+ $matches[$i] = $match;
295
+
296
+ // we'll store the match position as well; that way, we
297
+ // don't have to redo all preg_matches after changing only
298
+ // the first (we'll still know where those others are)
299
+ $positions[$i] = $match[0][1];
300
+ } else {
301
+ // if the pattern couldn't be matched, there's no point in
302
+ // executing it again in later runs on this same content;
303
+ // ignore this one until we reach end of content
304
+ unset($matches[$i], $positions[$i]);
305
+ }
306
+ }
307
+
308
+ // no more matches to find: everything's been processed, break out
309
+ if (!$matches) {
310
+ $processed .= $content;
311
+ break;
312
+ }
313
+
314
+ // see which of the patterns actually found the first thing (we'll
315
+ // only want to execute that one, since we're unsure if what the
316
+ // other found was not inside what the first found)
317
+ $discardLength = min($positions);
318
+ $firstPattern = array_search($discardLength, $positions);
319
+ $match = $matches[$firstPattern][0][0];
320
+
321
+ // execute the pattern that matches earliest in the content string
322
+ list($pattern, $replacement) = $this->patterns[$firstPattern];
323
+ $replacement = $this->replacePattern($pattern, $replacement, $content);
324
+
325
+ // figure out which part of the string was unmatched; that's the
326
+ // part we'll execute the patterns on again next
327
+ $content = (string) substr($content, $discardLength);
328
+ $unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
329
+
330
+ // move the replaced part to $processed and prepare $content to
331
+ // again match batch of patterns against
332
+ $processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
333
+ $content = $unmatched;
334
+
335
+ // first match has been replaced & that content is to be left alone,
336
+ // the next matches will start after this replacement, so we should
337
+ // fix their offsets
338
+ foreach ($positions as $i => $position) {
339
+ $positions[$i] -= $discardLength + strlen($match);
340
+ }
341
+ }
342
+
343
+ return $processed;
344
+ }
345
+
346
+ /**
347
+ * This is where a pattern is matched against $content and the matches
348
+ * are replaced by their respective value.
349
+ * This function will be called plenty of times, where $content will always
350
+ * move up 1 character.
351
+ *
352
+ * @param string $pattern Pattern to match
353
+ * @param string|callable $replacement Replacement value
354
+ * @param string $content Content to match pattern against
355
+ *
356
+ * @return string
357
+ */
358
+ protected function replacePattern($pattern, $replacement, $content)
359
+ {
360
+ if (is_callable($replacement)) {
361
+ return preg_replace_callback($pattern, $replacement, $content, 1, $count);
362
+ } else {
363
+ return preg_replace($pattern, $replacement, $content, 1, $count);
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Strings are a pattern we need to match, in order to ignore potential
369
+ * code-like content inside them, but we just want all of the string
370
+ * content to remain untouched.
371
+ *
372
+ * This method will replace all string content with simple STRING#
373
+ * placeholder text, so we've rid all strings from characters that may be
374
+ * misinterpreted. Original string content will be saved in $this->extracted
375
+ * and after doing all other minifying, we can restore the original content
376
+ * via restoreStrings().
377
+ *
378
+ * @param string[optional] $chars
379
+ * @param string[optional] $placeholderPrefix
380
+ */
381
+ protected function extractStrings($chars = '\'"', $placeholderPrefix = '')
382
+ {
383
+ // PHP only supports $this inside anonymous functions since 5.4
384
+ $minifier = $this;
385
+ $callback = function ($match) use ($minifier, $placeholderPrefix) {
386
+ // check the second index here, because the first always contains a quote
387
+ if ($match[2] === '') {
388
+ /*
389
+ * Empty strings need no placeholder; they can't be confused for
390
+ * anything else anyway.
391
+ * But we still needed to match them, for the extraction routine
392
+ * to skip over this particular string.
393
+ */
394
+ return $match[0];
395
+ }
396
+
397
+ $count = count($minifier->extracted);
398
+ $placeholder = $match[1].$placeholderPrefix.$count.$match[1];
399
+ $minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
400
+
401
+ return $placeholder;
402
+ };
403
+
404
+ /*
405
+ * The \\ messiness explained:
406
+ * * Don't count ' or " as end-of-string if it's escaped (has backslash
407
+ * in front of it)
408
+ * * Unless... that backslash itself is escaped (another leading slash),
409
+ * in which case it's no longer escaping the ' or "
410
+ * * So there can be either no backslash, or an even number
411
+ * * multiply all of that times 4, to account for the escaping that has
412
+ * to be done to pass the backslash into the PHP string without it being
413
+ * considered as escape-char (times 2) and to get it in the regex,
414
+ * escaped (times 2)
415
+ */
416
+ $this->registerPattern('/(['.$chars.'])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
417
+ }
418
+
419
+ /**
420
+ * This method will restore all extracted data (strings, regexes) that were
421
+ * replaced with placeholder text in extract*(). The original content was
422
+ * saved in $this->extracted.
423
+ *
424
+ * @param string $content
425
+ *
426
+ * @return string
427
+ */
428
+ protected function restoreExtractedData($content)
429
+ {
430
+ if (!$this->extracted) {
431
+ // nothing was extracted, nothing to restore
432
+ return $content;
433
+ }
434
+
435
+ $content = strtr($content, $this->extracted);
436
+
437
+ $this->extracted = array();
438
+
439
+ return $content;
440
+ }
441
+
442
+ /**
443
+ * Check if the path is a regular file and can be read.
444
+ *
445
+ * @param string $path
446
+ *
447
+ * @return bool
448
+ */
449
+ protected function canImportFile($path)
450
+ {
451
+ $parsed = parse_url($path);
452
+ if (
453
+ // file is elsewhere
454
+ isset($parsed['host']) ||
455
+ // file responds to queries (may change, or need to bypass cache)
456
+ isset($parsed['query'])
457
+ ) {
458
+ return false;
459
+ }
460
+
461
+ return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
462
+ }
463
+
464
+ /**
465
+ * Attempts to open file specified by $path for writing.
466
+ *
467
+ * @param string $path The path to the file
468
+ *
469
+ * @return resource Specifier for the target file
470
+ *
471
+ * @throws IOException
472
+ */
473
+ protected function openFileForWriting($path)
474
+ {
475
+ if (($handler = @fopen($path, 'w')) === false) {
476
+ throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
477
+ }
478
+
479
+ return $handler;
480
+ }
481
+
482
+ /**
483
+ * Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
484
+ *
485
+ * @param resource $handler The resource to write to
486
+ * @param string $content The content to write
487
+ * @param string $path The path to the file (for exception printing only)
488
+ *
489
+ * @throws IOException
490
+ */
491
+ protected function writeToFile($handler, $content, $path = '')
492
+ {
493
+ if (($result = @fwrite($handler, $content)) === false || ($result < strlen($content))) {
494
+ throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
495
+ }
496
+ }
497
+ }
vendor/matthiasmullie/path-converter/LICENSE ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2015 Matthias Mullie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
vendor/matthiasmullie/path-converter/composer.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "matthiasmullie/path-converter",
3
+ "type": "library",
4
+ "description": "Relative path converter",
5
+ "keywords": ["relative", "path", "converter", "paths"],
6
+ "homepage": "http://github.com/matthiasmullie/path-converter",
7
+ "license": "MIT",
8
+ "authors": [
9
+ {
10
+ "name": "Matthias Mullie",
11
+ "homepage": "http://www.mullie.eu",
12
+ "email": "pathconverter@mullie.eu",
13
+ "role": "Developer"
14
+ }
15
+ ],
16
+ "require": {
17
+ "php": ">=5.3.0",
18
+ "ext-pcre": "*"
19
+ },
20
+ "require-dev": {
21
+ "phpunit/phpunit": "~4.8"
22
+ },
23
+ "autoload": {
24
+ "psr-4": {
25
+ "MatthiasMullie\\PathConverter\\": "src/"
26
+ }
27
+ }
28
+ }
vendor/matthiasmullie/path-converter/src/Converter.php ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @param string $root Root directory (defaults to `getcwd`)
35
+ */
36
+ public function __construct($from, $to, $root = '')
37
+ {
38
+ $shared = $this->shared($from, $to);
39
+ if ($shared === '') {
40
+ // when both paths have nothing in common, one of them is probably
41
+ // absolute while the other is relative
42
+ $root = $root ?: getcwd();
43
+ $from = strpos($from, $root) === 0 ? $from : preg_replace('/\/+/', '/', $root.'/'.$from);
44
+ $to = strpos($to, $root) === 0 ? $to : preg_replace('/\/+/', '/', $root.'/'.$to);
45
+
46
+ // or traveling the tree via `..`
47
+ // attempt to resolve path, or assume it's fine if it doesn't exist
48
+ $from = @realpath($from) ?: $from;
49
+ $to = @realpath($to) ?: $to;
50
+ }
51
+
52
+ $from = $this->dirname($from);
53
+ $to = $this->dirname($to);
54
+
55
+ $from = $this->normalize($from);
56
+ $to = $this->normalize($to);
57
+
58
+ $this->from = $from;
59
+ $this->to = $to;
60
+ }
61
+
62
+ /**
63
+ * Normalize path.
64
+ *
65
+ * @param string $path
66
+ *
67
+ * @return string
68
+ */
69
+ protected function normalize($path)
70
+ {
71
+ // deal with different operating systems' directory structure
72
+ $path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
73
+
74
+ // remove leading current directory.
75
+ if (substr($path, 0, 2) === './') {
76
+ $path = substr($path, 2);
77
+ }
78
+
79
+ // remove references to current directory in the path.
80
+ $path = str_replace('/./', '/', $path);
81
+
82
+ /*
83
+ * Example:
84
+ * /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif
85
+ * to
86
+ * /home/forkcms/frontend/core/layout/images/img.gif
87
+ */
88
+ do {
89
+ $path = preg_replace('/[^\/]+(?<!\.\.)\/\.\.\//', '', $path, -1, $count);
90
+ } while ($count);
91
+
92
+ return $path;
93
+ }
94
+
95
+ /**
96
+ * Figure out the shared path of 2 locations.
97
+ *
98
+ * Example:
99
+ * /home/forkcms/frontend/core/layout/images/img.gif
100
+ * and
101
+ * /home/forkcms/frontend/cache/minified_css
102
+ * share
103
+ * /home/forkcms/frontend
104
+ *
105
+ * @param string $path1
106
+ * @param string $path2
107
+ *
108
+ * @return string
109
+ */
110
+ protected function shared($path1, $path2)
111
+ {
112
+ // $path could theoretically be empty (e.g. no path is given), in which
113
+ // case it shouldn't expand to array(''), which would compare to one's
114
+ // root /
115
+ $path1 = $path1 ? explode('/', $path1) : array();
116
+ $path2 = $path2 ? explode('/', $path2) : array();
117
+
118
+ $shared = array();
119
+
120
+ // compare paths & strip identical ancestors
121
+ foreach ($path1 as $i => $chunk) {
122
+ if (isset($path2[$i]) && $path1[$i] == $path2[$i]) {
123
+ $shared[] = $chunk;
124
+ } else {
125
+ break;
126
+ }
127
+ }
128
+
129
+ return implode('/', $shared);
130
+ }
131
+
132
+ /**
133
+ * Convert paths relative from 1 file to another.
134
+ *
135
+ * E.g.
136
+ * ../images/img.gif relative to /home/forkcms/frontend/core/layout/css
137
+ * should become:
138
+ * ../../core/layout/images/img.gif relative to
139
+ * /home/forkcms/frontend/cache/minified_css
140
+ *
141
+ * @param string $path The relative path that needs to be converted
142
+ *
143
+ * @return string The new relative path
144
+ */
145
+ public function convert($path)
146
+ {
147
+ // quit early if conversion makes no sense
148
+ if ($this->from === $this->to) {
149
+ return $path;
150
+ }
151
+
152
+ $path = $this->normalize($path);
153
+ // if we're not dealing with a relative path, just return absolute
154
+ if (strpos($path, '/') === 0) {
155
+ return $path;
156
+ }
157
+
158
+ // normalize paths
159
+ $path = $this->normalize($this->from.'/'.$path);
160
+
161
+ // strip shared ancestor paths
162
+ $shared = $this->shared($path, $this->to);
163
+ $path = mb_substr($path, mb_strlen($shared));
164
+ $to = mb_substr($this->to, mb_strlen($shared));
165
+
166
+ // add .. for every directory that needs to be traversed to new path
167
+ $to = str_repeat('../', count(array_filter(explode('/', $to))));
168
+
169
+ return $to.ltrim($path, '/');
170
+ }
171
+
172
+ /**
173
+ * Attempt to get the directory name from a path.
174
+ *
175
+ * @param string $path
176
+ *
177
+ * @return string
178
+ */
179
+ protected function dirname($path)
180
+ {
181
+ if (@is_file($path)) {
182
+ return dirname($path);
183
+ }
184
+
185
+ if (@is_dir($path)) {
186
+ return rtrim($path, '/');
187
+ }
188
+
189
+ // no known file/dir, start making assumptions
190
+
191
+ // ends in / = dir
192
+ if (mb_substr($path, -1) === '/') {
193
+ return rtrim($path, '/');
194
+ }
195
+
196
+ // has a dot in the name, likely a file
197
+ if (preg_match('/.*\..*$/', basename($path)) !== 0) {
198
+ return dirname($path);
199
+ }
200
+
201
+ // you're on your own here!
202
+ return $path;
203
+ }
204
+ }
vendor/matthiasmullie/path-converter/src/ConverterInterface.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\PathConverter;
4
+
5
+ /**
6
+ * Convert file paths.
7
+ *
8
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
+ *
10
+ * @author Matthias Mullie <pathconverter@mullie.eu>
11
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
+ * @license MIT License
13
+ */
14
+ interface ConverterInterface
15
+ {
16
+ /**
17
+ * Convert file paths.
18
+ *
19
+ * @param string $path The path to be converted
20
+ *
21
+ * @return string The new path
22
+ */
23
+ public function convert($path);
24
+ }
vendor/matthiasmullie/path-converter/src/NoConverter.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\PathConverter;
4
+
5
+ /**
6
+ * Don't convert paths.
7
+ *
8
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
+ *
10
+ * @author Matthias Mullie <pathconverter@mullie.eu>
11
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
+ * @license MIT License
13
+ */
14
+ class NoConverter implements ConverterInterface
15
+ {
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ public function convert($path)
20
+ {
21
+ return $path;
22
+ }
23
+ }