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 | Breeze – WordPress Cache Plugin |
Version | 1.1.8 |
Comparing to | |
See all releases |
Code changes from version 1.1.7 to 1.1.8
- assets/js/breeze-backend.js +1 -1
- breeze.php +16 -11
- composer.json +5 -0
- inc/breeze-configuration.php +14 -14
- inc/cache/config-cache.php +4 -0
- inc/cache/execute-cache.php +17 -10
- inc/cache/purge-per-time.php +9 -3
- inc/functions.php +13 -5
- inc/helpers.php +3 -3
- inc/minification/breeze-js-deferred-loading.php +480 -0
- inc/minification/breeze-minification-scripts.php +40 -36
- inc/minification/breeze-minify-main.php +26 -11
- inc/minification/minify/jsmin-1.1.1.php +0 -291
- inc/minification/minify/minify-2.1.7-html.php +0 -257
- inc/minification/minify/minify-2.1.7-jsmin.php +0 -447
- inc/minification/minify/yui-php-cssmin-2.4.8-4.php +0 -777
- readme.txt +10 -3
- vendor/autoload.php +7 -0
- vendor/bin/minifycss +1 -0
- vendor/bin/minifyjs +1 -0
- vendor/composer/ClassLoader.php +445 -0
- vendor/composer/LICENSE +21 -0
- vendor/composer/autoload_classmap.php +9 -0
- vendor/composer/autoload_namespaces.php +9 -0
- vendor/composer/autoload_psr4.php +11 -0
- vendor/composer/autoload_real.php +52 -0
- vendor/composer/autoload_static.php +36 -0
- vendor/composer/installed.json +115 -0
- vendor/matthiasmullie/minify/CONTRIBUTING.md +59 -0
- vendor/matthiasmullie/minify/Dockerfile +13 -0
- vendor/matthiasmullie/minify/LICENSE +18 -0
- vendor/matthiasmullie/minify/bin/minifycss +45 -0
- vendor/matthiasmullie/minify/bin/minifyjs +45 -0
- vendor/matthiasmullie/minify/composer.json +38 -0
- vendor/matthiasmullie/minify/data/js/keywords_after.txt +7 -0
- vendor/matthiasmullie/minify/data/js/keywords_before.txt +26 -0
- vendor/matthiasmullie/minify/data/js/keywords_reserved.txt +63 -0
- vendor/matthiasmullie/minify/data/js/operators.txt +46 -0
- vendor/matthiasmullie/minify/data/js/operators_after.txt +43 -0
- vendor/matthiasmullie/minify/data/js/operators_before.txt +43 -0
- vendor/matthiasmullie/minify/docker-compose.yml +31 -0
- vendor/matthiasmullie/minify/src/CSS.php +752 -0
- vendor/matthiasmullie/minify/src/Exception.php +20 -0
- vendor/matthiasmullie/minify/src/Exceptions/BasicException.php +23 -0
- vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php +21 -0
- vendor/matthiasmullie/minify/src/Exceptions/IOException.php +21 -0
- vendor/matthiasmullie/minify/src/JS.php +612 -0
- vendor/matthiasmullie/minify/src/Minify.php +497 -0
- vendor/matthiasmullie/path-converter/LICENSE +18 -0
- vendor/matthiasmullie/path-converter/composer.json +28 -0
- vendor/matthiasmullie/path-converter/src/Converter.php +204 -0
- vendor/matthiasmullie/path-converter/src/ConverterInterface.php +24 -0
- 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
|
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.
|
6 |
* Text Domain: breeze
|
7 |
* Domain Path: /languages
|
8 |
* Author: Cloudways
|
@@ -12,7 +12,7 @@
|
|
12 |
*/
|
13 |
|
14 |
/**
|
15 |
-
*
|
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.
|
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
|
99 |
-
$basic_conf
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
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 ( !
|
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(
|
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( '|',
|
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 |
-
|
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 |
-
|
446 |
)
|
447 |
);
|
448 |
|
449 |
-
if ( ! empty(
|
450 |
-
$regex_string = '%{THE_REQUEST} =~ m#^GET (' . implode( '|',
|
451 |
}
|
452 |
|
453 |
// Remove main site URL from disabled sites array.
|
454 |
-
$network_site_url_index = array_search( $network_site_url,
|
455 |
if ( false !== $network_site_url_index ) {
|
456 |
-
unset(
|
457 |
}
|
458 |
// Remove host part from URLs.
|
459 |
-
|
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 |
-
|
466 |
)
|
467 |
);
|
468 |
-
if ( ! empty(
|
469 |
-
if ( ! empty(
|
470 |
$regex_string .= ' && ';
|
471 |
}
|
472 |
-
$regex_string .= '%{THE_REQUEST} !~ m#^GET (' . implode( '|',
|
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
|
40 |
-
$user_logged
|
41 |
|
42 |
if ( substr_count( $url_path, '?' ) > 0 ) {
|
43 |
-
$filename
|
|
|
44 |
} else {
|
45 |
-
$filename
|
|
|
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 |
-
$
|
|
|
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 |
-
$
|
|
|
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 |
-
$
|
301 |
-
|
302 |
-
|
|
|
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 |
-
|
|
|
|
|
30 |
|
31 |
-
|
|
|
|
|
32 |
|
33 |
-
|
|
|
|
|
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 (
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
413 |
-
|
|
|
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 (
|
453 |
-
|
|
|
|
|
454 |
|
455 |
-
|
456 |
-
|
457 |
-
|
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 (
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
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 (
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
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 |
-
|
524 |
-
|
525 |
-
|
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 |
-
|
553 |
-
|
554 |
-
|
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 |
-
|
|
|
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
|
|
|
|
|
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 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
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.
|
6 |
-
Stable tag: 1.1.
|
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
|
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 |
+
}
|