mirror of
https://github.com/moodle/moodle.git
synced 2025-08-09 02:46:40 +02:00
MDL-65744 lib: Update minify minify lib
This commit is contained in:
parent
f7e108438f
commit
0918e156a0
9 changed files with 282 additions and 137 deletions
18
lib/minify/matthiasmullie-minify/LICENSE
Executable file
18
lib/minify/matthiasmullie-minify/LICENSE
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
Copyright (c) 2012 Matthias Mullie
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,4 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* CSS Minifier
|
||||||
|
*
|
||||||
|
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
||||||
|
*
|
||||||
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
|
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
||||||
|
* @license MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
namespace MatthiasMullie\Minify;
|
namespace MatthiasMullie\Minify;
|
||||||
|
|
||||||
|
@ -7,10 +16,11 @@ use MatthiasMullie\PathConverter\ConverterInterface;
|
||||||
use MatthiasMullie\PathConverter\Converter;
|
use MatthiasMullie\PathConverter\Converter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS minifier.
|
* CSS minifier
|
||||||
*
|
*
|
||||||
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
||||||
*
|
*
|
||||||
|
* @package Minify
|
||||||
* @author Matthias Mullie <minify@mullie.eu>
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
* @author Tijs Verkoyen <minify@verkoyen.eu>
|
* @author Tijs Verkoyen <minify@verkoyen.eu>
|
||||||
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
||||||
|
@ -19,12 +29,12 @@ use MatthiasMullie\PathConverter\Converter;
|
||||||
class CSS extends Minify
|
class CSS extends Minify
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int maximum inport size in kB
|
||||||
*/
|
*/
|
||||||
protected $maxImportSize = 5;
|
protected $maxImportSize = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string[]
|
* @var string[] valid import extensions
|
||||||
*/
|
*/
|
||||||
protected $importExtensions = array(
|
protected $importExtensions = array(
|
||||||
'gif' => 'data:image/gif',
|
'gif' => 'data:image/gif',
|
||||||
|
@ -76,14 +86,14 @@ class CSS extends Minify
|
||||||
*/
|
*/
|
||||||
protected function moveImportsToTop($content)
|
protected function moveImportsToTop($content)
|
||||||
{
|
{
|
||||||
if (preg_match_all('/@import[^;]+;/', $content, $matches)) {
|
if (preg_match_all('/(;?)(@import (?<url>url\()?(?P<quotes>["\']?).+?(?P=quotes)(?(url)\)));?/', $content, $matches)) {
|
||||||
// remove from content
|
// remove from content
|
||||||
foreach ($matches[0] as $import) {
|
foreach ($matches[0] as $import) {
|
||||||
$content = str_replace($import, '', $content);
|
$content = str_replace($import, '', $content);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to top
|
// add to top
|
||||||
$content = implode('', $matches[0]).$content;
|
$content = implode(';', $matches[2]).';'.trim($content, ';');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
|
@ -207,6 +217,8 @@ class CSS extends Minify
|
||||||
// grab referenced file & minify it (which may include importing
|
// grab referenced file & minify it (which may include importing
|
||||||
// yet other @import statements recursively)
|
// yet other @import statements recursively)
|
||||||
$minifier = new static($importPath);
|
$minifier = new static($importPath);
|
||||||
|
$minifier->setMaxImportSize($this->maxImportSize);
|
||||||
|
$minifier->setImportExtensions($this->importExtensions);
|
||||||
$importContent = $minifier->execute($source, $parents);
|
$importContent = $minifier->execute($source, $parents);
|
||||||
|
|
||||||
// check if this is only valid for certain media
|
// check if this is only valid for certain media
|
||||||
|
@ -295,10 +307,11 @@ class CSS extends Minify
|
||||||
*/
|
*/
|
||||||
$this->extractStrings();
|
$this->extractStrings();
|
||||||
$this->stripComments();
|
$this->stripComments();
|
||||||
|
$this->extractCalcs();
|
||||||
$css = $this->replace($css);
|
$css = $this->replace($css);
|
||||||
|
|
||||||
$css = $this->stripWhitespace($css);
|
$css = $this->stripWhitespace($css);
|
||||||
$css = $this->shortenHex($css);
|
$css = $this->shortenColors($css);
|
||||||
$css = $this->shortenZeroes($css);
|
$css = $this->shortenZeroes($css);
|
||||||
$css = $this->shortenFontWeights($css);
|
$css = $this->shortenFontWeights($css);
|
||||||
$css = $this->stripEmptyTags($css);
|
$css = $this->stripEmptyTags($css);
|
||||||
|
@ -469,12 +482,16 @@ class CSS extends Minify
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function shortenHex($content)
|
protected function shortenColors($content)
|
||||||
{
|
{
|
||||||
$content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?=[; }])/i', '#$1$2$3', $content);
|
$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);
|
||||||
|
|
||||||
|
// remove alpha channel if it's pointless...
|
||||||
|
$content = preg_replace('/(?<=[: ])#([0-9a-z]{6})ff?(?=[; }])/i', '#$1', $content);
|
||||||
|
$content = preg_replace('/(?<=[: ])#([0-9a-z]{3})f?(?=[; }])/i', '#$1', $content);
|
||||||
|
|
||||||
// we can shorten some even more by replacing them with their color name
|
|
||||||
$colors = array(
|
$colors = array(
|
||||||
|
// we can shorten some even more by replacing them with their color name
|
||||||
'#F0FFFF' => 'azure',
|
'#F0FFFF' => 'azure',
|
||||||
'#F5F5DC' => 'beige',
|
'#F5F5DC' => 'beige',
|
||||||
'#A52A2A' => 'brown',
|
'#A52A2A' => 'brown',
|
||||||
|
@ -502,6 +519,9 @@ class CSS extends Minify
|
||||||
'#FF6347' => 'tomato',
|
'#FF6347' => 'tomato',
|
||||||
'#EE82EE' => 'violet',
|
'#EE82EE' => 'violet',
|
||||||
'#F5DEB3' => 'wheat',
|
'#F5DEB3' => 'wheat',
|
||||||
|
// or the other way around
|
||||||
|
'WHITE' => '#fff',
|
||||||
|
'BLACK' => '#000',
|
||||||
);
|
);
|
||||||
|
|
||||||
return preg_replace_callback(
|
return preg_replace_callback(
|
||||||
|
@ -543,6 +563,12 @@ class CSS extends Minify
|
||||||
*/
|
*/
|
||||||
protected function shortenZeroes($content)
|
protected function shortenZeroes($content)
|
||||||
{
|
{
|
||||||
|
// we don't want to strip units in `calc()` expressions:
|
||||||
|
// `5px - 0px` is valid, but `5px - 0` is not
|
||||||
|
// `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
|
||||||
|
// `10 * 0` is invalid
|
||||||
|
// we've extracted calcs earlier, so we don't need to worry about this
|
||||||
|
|
||||||
// reusable bits of code throughout these regexes:
|
// reusable bits of code throughout these regexes:
|
||||||
// before & after are used to make sure we don't match lose unintended
|
// before & after are used to make sure we don't match lose unintended
|
||||||
// 0-like values (e.g. in #000, or in http://url/1.0)
|
// 0-like values (e.g. in #000, or in http://url/1.0)
|
||||||
|
@ -571,28 +597,11 @@ class CSS extends Minify
|
||||||
// strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
|
// strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
|
||||||
$content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
|
$content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
|
||||||
|
|
||||||
// remove zeroes where they make no sense in calc: e.g. calc(100px - 0)
|
// IE doesn't seem to understand a unitless flex-basis value (correct -
|
||||||
// the 0 doesn't have any effect, and this isn't even valid without unit
|
// it goes against the spec), so let's add it in again (make it `%`,
|
||||||
// strip all `+ 0` or `- 0` occurrences: calc(10% + 0) -> calc(10%)
|
// which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
|
||||||
// looped because there may be multiple 0s inside 1 group of parentheses
|
// @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
|
||||||
do {
|
$content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
|
||||||
$previous = $content;
|
|
||||||
$content = preg_replace('/\(([^\(\)]+) [\+\-] 0( [^\(\)]+)?\)/', '(\\1\\2)', $content);
|
|
||||||
} while ($content !== $previous);
|
|
||||||
// strip all `0 +` occurrences: calc(0 + 10%) -> calc(10%)
|
|
||||||
$content = preg_replace('/\(0 \+ ([^\(\)]+)\)/', '(\\1)', $content);
|
|
||||||
// strip all `0 -` occurrences: calc(0 - 10%) -> calc(-10%)
|
|
||||||
$content = preg_replace('/\(0 \- ([^\(\)]+)\)/', '(-\\1)', $content);
|
|
||||||
// I'm not going to attempt to optimize away `x * 0` instances:
|
|
||||||
// it's dumb enough code already that it likely won't occur, and it's
|
|
||||||
// too complex to do right (order of operations would have to be
|
|
||||||
// respected etc)
|
|
||||||
// what I cared about most here was fixing incorrectly truncated units
|
|
||||||
|
|
||||||
// IE doesn't seem to understand a unitless flex-basis value, so let's
|
|
||||||
// add it in again (make it `%`, which is only 1 char: 0%, 0px, 0
|
|
||||||
// anything, it's all just the same)
|
|
||||||
$content = preg_replace('/flex:([^ ]+ [^ ]+ )0([;\}])/', 'flex:${1}0%${2}', $content);
|
|
||||||
$content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
|
$content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
|
@ -607,7 +616,10 @@ class CSS extends Minify
|
||||||
*/
|
*/
|
||||||
protected function stripEmptyTags($content)
|
protected function stripEmptyTags($content)
|
||||||
{
|
{
|
||||||
return preg_replace('/(^|\}|;)[^\{\};]+\{\s*\}/', '\\1', $content);
|
$content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content);
|
||||||
|
$content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content);
|
||||||
|
|
||||||
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -615,6 +627,17 @@ class CSS extends Minify
|
||||||
*/
|
*/
|
||||||
protected function stripComments()
|
protected function stripComments()
|
||||||
{
|
{
|
||||||
|
// PHP only supports $this inside anonymous functions since 5.4
|
||||||
|
$minifier = $this;
|
||||||
|
$callback = function ($match) use ($minifier) {
|
||||||
|
$count = count($minifier->extracted);
|
||||||
|
$placeholder = '/*'.$count.'*/';
|
||||||
|
$minifier->extracted[$placeholder] = $match[0];
|
||||||
|
|
||||||
|
return $placeholder;
|
||||||
|
};
|
||||||
|
$this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
|
||||||
|
|
||||||
$this->registerPattern('/\/\*.*?\*\//s', '');
|
$this->registerPattern('/\/\*.*?\*\//s', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,8 +660,8 @@ class CSS extends Minify
|
||||||
// remove whitespace around meta characters
|
// remove whitespace around meta characters
|
||||||
// inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
|
// inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
|
||||||
$content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
|
$content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
|
||||||
$content = preg_replace('/([\[(:])\s+/', '$1', $content);
|
$content = preg_replace('/([\[(:>\+])\s+/', '$1', $content);
|
||||||
$content = preg_replace('/\s+([\]\)])/', '$1', $content);
|
$content = preg_replace('/\s+([\]\)>\+])/', '$1', $content);
|
||||||
$content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
|
$content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
|
||||||
|
|
||||||
// whitespace around + and - can only be stripped inside some pseudo-
|
// whitespace around + and - can only be stripped inside some pseudo-
|
||||||
|
@ -654,6 +677,40 @@ class CSS extends Minify
|
||||||
return trim($content);
|
return trim($content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace all `calc()` occurrences.
|
||||||
|
*/
|
||||||
|
protected function extractCalcs()
|
||||||
|
{
|
||||||
|
// PHP only supports $this inside anonymous functions since 5.4
|
||||||
|
$minifier = $this;
|
||||||
|
$callback = function ($match) use ($minifier) {
|
||||||
|
$length = strlen($match[1]);
|
||||||
|
$expr = '';
|
||||||
|
$opened = 0;
|
||||||
|
|
||||||
|
for ($i = 0; $i < $length; $i++) {
|
||||||
|
$char = $match[1][$i];
|
||||||
|
$expr .= $char;
|
||||||
|
if ($char === '(') {
|
||||||
|
$opened++;
|
||||||
|
} elseif ($char === ')' && --$opened === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rest = str_replace($expr, '', $match[1]);
|
||||||
|
$expr = trim(substr($expr, 1, -1));
|
||||||
|
|
||||||
|
$count = count($minifier->extracted);
|
||||||
|
$placeholder = 'calc('.$count.')';
|
||||||
|
$minifier->extracted[$placeholder] = 'calc('.$expr.')';
|
||||||
|
|
||||||
|
return $placeholder.$rest;
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->registerPattern('/calc(\(.+?)(?=$|;|calc\()/', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if file is small enough to be imported.
|
* Check if file is small enough to be imported.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Base Exception
|
||||||
|
*
|
||||||
|
* @deprecated Use Exceptions\BasicException instead
|
||||||
|
*
|
||||||
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
|
*/
|
||||||
namespace MatthiasMullie\Minify;
|
namespace MatthiasMullie\Minify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Base Exception Class
|
||||||
* @deprecated Use Exceptions\BasicException instead
|
* @deprecated Use Exceptions\BasicException instead
|
||||||
*
|
*
|
||||||
|
* @package Minify
|
||||||
* @author Matthias Mullie <minify@mullie.eu>
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
*/
|
*/
|
||||||
abstract class Exception extends \Exception
|
abstract class Exception extends \Exception
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Basic exception
|
||||||
|
*
|
||||||
|
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
||||||
|
*
|
||||||
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
|
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
||||||
|
* @license MIT License
|
||||||
|
*/
|
||||||
namespace MatthiasMullie\Minify\Exceptions;
|
namespace MatthiasMullie\Minify\Exceptions;
|
||||||
|
|
||||||
use MatthiasMullie\Minify\Exception;
|
use MatthiasMullie\Minify\Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Basic Exception Class
|
||||||
|
*
|
||||||
|
* @package Minify\Exception
|
||||||
* @author Matthias Mullie <minify@mullie.eu>
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
*/
|
*/
|
||||||
abstract class BasicException extends Exception
|
abstract class BasicException extends Exception
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* File Import Exception
|
||||||
|
*
|
||||||
|
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
||||||
|
*
|
||||||
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
|
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
||||||
|
* @license MIT License
|
||||||
|
*/
|
||||||
namespace MatthiasMullie\Minify\Exceptions;
|
namespace MatthiasMullie\Minify\Exceptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* File Import Exception Class
|
||||||
|
*
|
||||||
|
* @package Minify\Exception
|
||||||
* @author Matthias Mullie <minify@mullie.eu>
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
*/
|
*/
|
||||||
class FileImportException extends BasicException
|
class FileImportException extends BasicException
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* IO Exception
|
||||||
|
*
|
||||||
|
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
||||||
|
*
|
||||||
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
|
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
||||||
|
* @license MIT License
|
||||||
|
*/
|
||||||
namespace MatthiasMullie\Minify\Exceptions;
|
namespace MatthiasMullie\Minify\Exceptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* IO Exception Class
|
||||||
|
*
|
||||||
|
* @package Minify\Exception
|
||||||
* @author Matthias Mullie <minify@mullie.eu>
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
*/
|
*/
|
||||||
class IOException extends BasicException
|
class IOException extends BasicException
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace MatthiasMullie\Minify;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JavaScript minifier.
|
* JavaScript minifier
|
||||||
*
|
*
|
||||||
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
||||||
*
|
*
|
||||||
* @author Matthias Mullie <minify@mullie.eu>
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
|
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
||||||
|
* @license MIT License
|
||||||
|
*/
|
||||||
|
namespace MatthiasMullie\Minify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JavaScript Minifier Class
|
||||||
|
*
|
||||||
|
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
||||||
|
*
|
||||||
|
* @package Minify
|
||||||
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
* @author Tijs Verkoyen <minify@verkoyen.eu>
|
* @author Tijs Verkoyen <minify@verkoyen.eu>
|
||||||
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
||||||
* @license MIT License
|
* @license MIT License
|
||||||
|
@ -110,11 +119,6 @@ class JS extends Minify
|
||||||
*/
|
*/
|
||||||
protected $operatorsAfter = array();
|
protected $operatorsAfter = array();
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $nestedExtracted = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -144,19 +148,6 @@ class JS extends Minify
|
||||||
{
|
{
|
||||||
$content = '';
|
$content = '';
|
||||||
|
|
||||||
// loop files
|
|
||||||
foreach ($this->data as $source => $js) {
|
|
||||||
/*
|
|
||||||
* Combine js: separating the scripts by a ;
|
|
||||||
* I'm also adding a newline: it will be eaten when whitespace is
|
|
||||||
* stripped, but we need to make sure we're not just appending
|
|
||||||
* a new script right after a previous script that ended with a
|
|
||||||
* singe-line comment on the last line (in which case it would also
|
|
||||||
* be seen as part of that comment)
|
|
||||||
*/
|
|
||||||
$content .= $js."\n;";
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Let's first take out strings, comments and regular expressions.
|
* Let's first take out strings, comments and regular expressions.
|
||||||
* All of these can contain JS code-like characters, and we should make
|
* All of these can contain JS code-like characters, and we should make
|
||||||
|
@ -171,11 +162,24 @@ class JS extends Minify
|
||||||
$this->extractStrings('\'"`');
|
$this->extractStrings('\'"`');
|
||||||
$this->stripComments();
|
$this->stripComments();
|
||||||
$this->extractRegex();
|
$this->extractRegex();
|
||||||
$content = $this->replace($content);
|
|
||||||
|
|
||||||
$content = $this->propertyNotation($content);
|
// loop files
|
||||||
$content = $this->shortenBools($content);
|
foreach ($this->data as $source => $js) {
|
||||||
$content = $this->stripWhitespace($content);
|
// take out strings, comments & regex (for which we've registered
|
||||||
|
// the regexes just a few lines earlier)
|
||||||
|
$js = $this->replace($js);
|
||||||
|
|
||||||
|
$js = $this->propertyNotation($js);
|
||||||
|
$js = $this->shortenBools($js);
|
||||||
|
$js = $this->stripWhitespace($js);
|
||||||
|
|
||||||
|
// combine js: separating the scripts by a ;
|
||||||
|
$content .= $js.";";
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up leftover `;`s from the combination of multiple scripts
|
||||||
|
$content = ltrim($content, ';');
|
||||||
|
$content = (string) substr($content, 0, -1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Earlier, we extracted strings & regular expressions and replaced them
|
* Earlier, we extracted strings & regular expressions and replaced them
|
||||||
|
@ -191,11 +195,21 @@ class JS extends Minify
|
||||||
*/
|
*/
|
||||||
protected function stripComments()
|
protected function stripComments()
|
||||||
{
|
{
|
||||||
|
// PHP only supports $this inside anonymous functions since 5.4
|
||||||
|
$minifier = $this;
|
||||||
|
$callback = function ($match) use ($minifier) {
|
||||||
|
$count = count($minifier->extracted);
|
||||||
|
$placeholder = '/*'.$count.'*/';
|
||||||
|
$minifier->extracted[$placeholder] = $match[0];
|
||||||
|
|
||||||
|
return $placeholder;
|
||||||
|
};
|
||||||
|
// multi-line comments
|
||||||
|
$this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
|
||||||
|
$this->registerPattern('/\/\*.*?\*\//s', '');
|
||||||
|
|
||||||
// single-line comments
|
// single-line comments
|
||||||
$this->registerPattern('/\/\/.*$/m', '');
|
$this->registerPattern('/\/\/.*$/m', '');
|
||||||
|
|
||||||
// multi-line comments
|
|
||||||
$this->registerPattern('/\/\*.*?\*\//s', '');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -222,38 +236,26 @@ class JS extends Minify
|
||||||
$callback = function ($match) use ($minifier) {
|
$callback = function ($match) use ($minifier) {
|
||||||
$count = count($minifier->extracted);
|
$count = count($minifier->extracted);
|
||||||
$placeholder = '"'.$count.'"';
|
$placeholder = '"'.$count.'"';
|
||||||
$minifier->extracted[$placeholder] = $match['regex'];
|
$minifier->extracted[$placeholder] = $match[0];
|
||||||
|
|
||||||
// because we're also trying to find regular expressions that follow
|
return $placeholder;
|
||||||
// if/when/for statements, we should also make sure that the content
|
|
||||||
// within these statements is also minified...
|
|
||||||
// e.g. `if("some string"/* or comment */)` should become
|
|
||||||
// `if("some string")`
|
|
||||||
if (isset($match['before'])) {
|
|
||||||
$other = new static();
|
|
||||||
$other->extractStrings('\'"`', "$count-");
|
|
||||||
$other->stripComments();
|
|
||||||
$match['before'] = $other->replace($match['before']);
|
|
||||||
$this->nestedExtracted += $other->extracted;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (isset($match['before']) ? $match['before'] : '').
|
|
||||||
$placeholder.
|
|
||||||
(isset($match['after']) ? $match['after'] : '');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$pattern = '(?P<regex>\/.+?((?<!\\\\)\\\\\\\\)*\/[gimy]*)(?![0-9a-zA-Z\/])';
|
// match all chars except `/` and `\`
|
||||||
|
// `\` is allowed though, along with whatever char follows (which is the
|
||||||
|
// one being escaped)
|
||||||
|
// this should allow all chars, except for an unescaped `/` (= the one
|
||||||
|
// closing the regex)
|
||||||
|
// then also ignore bare `/` inside `[]`, where they don't need to be
|
||||||
|
// escaped: anything inside `[]` can be ignored safely
|
||||||
|
$pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
|
||||||
|
|
||||||
// a regular expression can only be followed by a few operators or some
|
// a regular expression can only be followed by a few operators or some
|
||||||
// of the RegExp methods (a `\` followed by a variable or value is
|
// of the RegExp methods (a `\` followed by a variable or value is
|
||||||
// likely part of a division, not a regex)
|
// likely part of a division, not a regex)
|
||||||
$keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
|
$keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
|
||||||
$before = '(?P<before>[=:,;\}\(\{&\|!]|^|'.implode('|', $keywords).')';
|
$before = '([=:,;\+\-\*\/\}\(\{\[&\|!]|^|'.implode('|', $keywords).')\s*';
|
||||||
$propertiesAndMethods = array(
|
$propertiesAndMethods = array(
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties
|
|
||||||
'prototype',
|
|
||||||
'length',
|
|
||||||
'lastIndex',
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
|
||||||
'constructor',
|
'constructor',
|
||||||
'flags',
|
'flags',
|
||||||
|
@ -267,29 +269,28 @@ class JS extends Minify
|
||||||
'compile(',
|
'compile(',
|
||||||
'exec(',
|
'exec(',
|
||||||
'test(',
|
'test(',
|
||||||
'match',
|
|
||||||
'replace(',
|
|
||||||
'search(',
|
|
||||||
'split(',
|
|
||||||
'toSource(',
|
'toSource(',
|
||||||
'toString(',
|
'toString(',
|
||||||
);
|
);
|
||||||
$delimiters = array_fill(0, count($propertiesAndMethods), '/');
|
$delimiters = array_fill(0, count($propertiesAndMethods), '/');
|
||||||
$propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
|
$propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
|
||||||
$after = '(?P<after>[\.,;\)\}&\|+]|$|\.('.implode('|', $propertiesAndMethods).'))';
|
$after = '(?=\s*([\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).')))';
|
||||||
$this->registerPattern('/'.$before.'\s*'.$pattern.'\s*'.$after.'/', $callback);
|
$this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
|
||||||
|
|
||||||
// we didn't check for regular expressions after `)`, because that is
|
// regular expressions following a `)` are rather annoying to detect...
|
||||||
// more often than not not a character where a regex can follow (e.g.
|
// quite often, `/` after `)` is a division operator & if it happens to
|
||||||
// (1+2)/3/4 -> /3/ could be considered a regex, but it's not)
|
// be followed by another one (or a comment), it is likely to be
|
||||||
// however, after single-line if/while/for, there could very well be a
|
// confused for a regular expression
|
||||||
// regex after `)` (e.g. if(true)/regex/)
|
// however, it's perfectly possible for a regex to follow a `)`: after
|
||||||
// there is one problem, though: it's (near) impossible to check for
|
// a single-line `if()`, `while()`, ... statement, for example
|
||||||
// when the if/while/for statement is closed (same amount of closing
|
// since, when they occur like that, they're always the start of a
|
||||||
// brackets as there were opened), so I'll ignore single-line statements
|
// statement, there's only a limited amount of ways they can be useful:
|
||||||
// with nested brackets followed by a regex for now...
|
// by calling the regex methods directly
|
||||||
$before = '(?P<before>\b(if|while|for)\s*\((?P<code>[^\(]+?)\))';
|
// if a regex following `)` is not followed by `.<property or method>`,
|
||||||
$this->registerPattern('/'.$before.'\s*'.$pattern.'\s*'.$after.'/', $callback);
|
// it's quite likely not a regex
|
||||||
|
$before = '\)\s*';
|
||||||
|
$after = '(?=\s*\.('.implode('|', $propertiesAndMethods).'))';
|
||||||
|
$this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
|
||||||
|
|
||||||
// 1 more edge case: a regex can be followed by a lot more operators or
|
// 1 more edge case: a regex can be followed by a lot more operators or
|
||||||
// keywords if there's a newline (ASI) in between, where the operator
|
// keywords if there's a newline (ASI) in between, where the operator
|
||||||
|
@ -297,25 +298,8 @@ class JS extends Minify
|
||||||
// (https://github.com/matthiasmullie/minify/issues/56)
|
// (https://github.com/matthiasmullie/minify/issues/56)
|
||||||
$operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
|
$operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
|
||||||
$operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
|
$operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
|
||||||
$after = '(?P<after>\n\s*('.implode('|', $operators).'))';
|
$after = '(?=\s*\n\s*('.implode('|', $operators).'))';
|
||||||
$this->registerPattern('/'.$pattern.'\s*'.$after.'/', $callback);
|
$this->registerPattern('/'.$pattern.$after.'/', $callback);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In addition to the regular restore routine, we also need to restore a few
|
|
||||||
* more things that have been extracted as part of the regex extraction...
|
|
||||||
*
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected function restoreExtractedData($content)
|
|
||||||
{
|
|
||||||
// restore regular extracted stuff
|
|
||||||
$content = parent::restoreExtractedData($content);
|
|
||||||
|
|
||||||
// restore nested stuff from within regex extraction
|
|
||||||
$content = strtr($content, $this->nestedExtracted);
|
|
||||||
|
|
||||||
return $content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -361,7 +345,9 @@ class JS extends Minify
|
||||||
array(
|
array(
|
||||||
'/('.implode('|', $operatorsBefore).')\s+/',
|
'/('.implode('|', $operatorsBefore).')\s+/',
|
||||||
'/\s+('.implode('|', $operatorsAfter).')/',
|
'/\s+('.implode('|', $operatorsAfter).')/',
|
||||||
), '\\1', $content
|
),
|
||||||
|
'\\1',
|
||||||
|
$content
|
||||||
);
|
);
|
||||||
|
|
||||||
// make sure + and - can't be mistaken for, or joined into ++ and --
|
// make sure + and - can't be mistaken for, or joined into ++ and --
|
||||||
|
@ -369,7 +355,9 @@ class JS extends Minify
|
||||||
array(
|
array(
|
||||||
'/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
|
'/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
|
||||||
'/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
|
'/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
|
||||||
), '\\1', $content
|
),
|
||||||
|
'\\1',
|
||||||
|
$content
|
||||||
);
|
);
|
||||||
|
|
||||||
// collapse whitespace around reserved words into single space
|
// collapse whitespace around reserved words into single space
|
||||||
|
@ -387,6 +375,16 @@ class JS extends Minify
|
||||||
$content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
|
$content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
|
||||||
$content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
|
$content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Whitespace after `return` can be omitted in a few occasions
|
||||||
|
* (such as when followed by a string or regex)
|
||||||
|
* Same for whitespace in between `)` and `{`, or between `{` and some
|
||||||
|
* keywords.
|
||||||
|
*/
|
||||||
|
$content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
|
||||||
|
$content = preg_replace('/\)\s+\{/', '){', $content);
|
||||||
|
$content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get rid of double semicolons, except where they can be used like:
|
* Get rid of double semicolons, except where they can be used like:
|
||||||
* "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
|
* "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
|
||||||
|
@ -404,12 +402,19 @@ class JS extends Minify
|
||||||
* semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
|
* semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
|
||||||
* Here, nothing happens during the loop; it's just used to keep
|
* Here, nothing happens during the loop; it's just used to keep
|
||||||
* increasing `i`. With that ; omitted, the next line would be expected
|
* increasing `i`. With that ; omitted, the next line would be expected
|
||||||
* to be the for-loop's body...
|
* to be the for-loop's body... Same goes for while loops.
|
||||||
* I'm going to double that semicolon (if any) so after the next line,
|
* I'm going to double that semicolon (if any) so after the next line,
|
||||||
* which strips semicolons here & there, we're still left with this one.
|
* which strips semicolons here & there, we're still left with this one.
|
||||||
*/
|
*/
|
||||||
$content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
|
$content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
|
||||||
$content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
|
$content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
|
||||||
|
/*
|
||||||
|
* Below will also keep `;` after a `do{}while();` along with `while();`
|
||||||
|
* While these could be stripped after do-while, detecting this
|
||||||
|
* distinction is cumbersome, so I'll play it safe and make sure `;`
|
||||||
|
* after any kind of `while` is kept.
|
||||||
|
*/
|
||||||
|
$content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We also can't strip empty else-statements. Even though they're
|
* We also can't strip empty else-statements. Even though they're
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Abstract minifier class
|
||||||
|
*
|
||||||
|
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
||||||
|
*
|
||||||
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
|
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
||||||
|
* @license MIT License
|
||||||
|
*/
|
||||||
namespace MatthiasMullie\Minify;
|
namespace MatthiasMullie\Minify;
|
||||||
|
|
||||||
use MatthiasMullie\Minify\Exceptions\IOException;
|
use MatthiasMullie\Minify\Exceptions\IOException;
|
||||||
|
@ -10,6 +18,7 @@ use Psr\Cache\CacheItemInterface;
|
||||||
*
|
*
|
||||||
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
* Please report bugs on https://github.com/matthiasmullie/minify/issues
|
||||||
*
|
*
|
||||||
|
* @package Minify
|
||||||
* @author Matthias Mullie <minify@mullie.eu>
|
* @author Matthias Mullie <minify@mullie.eu>
|
||||||
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
|
||||||
* @license MIT License
|
* @license MIT License
|
||||||
|
@ -230,6 +239,12 @@ abstract class Minify
|
||||||
foreach ($this->patterns as $i => $pattern) {
|
foreach ($this->patterns as $i => $pattern) {
|
||||||
list($pattern, $replacement) = $pattern;
|
list($pattern, $replacement) = $pattern;
|
||||||
|
|
||||||
|
// we can safely ignore patterns for positions we've unset earlier,
|
||||||
|
// because we know these won't show up anymore
|
||||||
|
if (array_key_exists($i, $positions) == false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// no need to re-run matches that are still in the part of the
|
// no need to re-run matches that are still in the part of the
|
||||||
// content that hasn't been processed
|
// content that hasn't been processed
|
||||||
if ($positions[$i] >= 0) {
|
if ($positions[$i] >= 0) {
|
||||||
|
@ -237,19 +252,18 @@ abstract class Minify
|
||||||
}
|
}
|
||||||
|
|
||||||
$match = null;
|
$match = null;
|
||||||
if (preg_match($pattern, $content, $match)) {
|
if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
|
||||||
$matches[$i] = $match;
|
$matches[$i] = $match;
|
||||||
|
|
||||||
// we'll store the match position as well; that way, we
|
// we'll store the match position as well; that way, we
|
||||||
// don't have to redo all preg_matches after changing only
|
// don't have to redo all preg_matches after changing only
|
||||||
// the first (we'll still know where those others are)
|
// the first (we'll still know where those others are)
|
||||||
$positions[$i] = strpos($content, $match[0]);
|
$positions[$i] = $match[0][1];
|
||||||
} else {
|
} else {
|
||||||
// if the pattern couldn't be matched, there's no point in
|
// if the pattern couldn't be matched, there's no point in
|
||||||
// executing it again in later runs on this same content;
|
// executing it again in later runs on this same content;
|
||||||
// ignore this one until we reach end of content
|
// ignore this one until we reach end of content
|
||||||
unset($matches[$i]);
|
unset($matches[$i], $positions[$i]);
|
||||||
$positions[$i] = strlen($content);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +278,7 @@ abstract class Minify
|
||||||
// other found was not inside what the first found)
|
// other found was not inside what the first found)
|
||||||
$discardLength = min($positions);
|
$discardLength = min($positions);
|
||||||
$firstPattern = array_search($discardLength, $positions);
|
$firstPattern = array_search($discardLength, $positions);
|
||||||
$match = $matches[$firstPattern][0];
|
$match = $matches[$firstPattern][0][0];
|
||||||
|
|
||||||
// execute the pattern that matches earliest in the content string
|
// execute the pattern that matches earliest in the content string
|
||||||
list($pattern, $replacement) = $this->patterns[$firstPattern];
|
list($pattern, $replacement) = $this->patterns[$firstPattern];
|
||||||
|
@ -272,7 +286,7 @@ abstract class Minify
|
||||||
|
|
||||||
// figure out which part of the string was unmatched; that's the
|
// figure out which part of the string was unmatched; that's the
|
||||||
// part we'll execute the patterns on again next
|
// part we'll execute the patterns on again next
|
||||||
$content = substr($content, $discardLength);
|
$content = (string) substr($content, $discardLength);
|
||||||
$unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
|
$unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
|
||||||
|
|
||||||
// move the replaced part to $processed and prepare $content to
|
// move the replaced part to $processed and prepare $content to
|
||||||
|
@ -396,6 +410,16 @@ abstract class Minify
|
||||||
*/
|
*/
|
||||||
protected function canImportFile($path)
|
protected function canImportFile($path)
|
||||||
{
|
{
|
||||||
|
$parsed = parse_url($path);
|
||||||
|
if (
|
||||||
|
// file is elsewhere
|
||||||
|
isset($parsed['host']) ||
|
||||||
|
// file responds to queries (may change, or need to bypass cache)
|
||||||
|
isset($parsed['query'])
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
|
return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
<location>minify/matthiasmullie-minify</location>
|
<location>minify/matthiasmullie-minify</location>
|
||||||
<name>MatthiasMullie\Minify</name>
|
<name>MatthiasMullie\Minify</name>
|
||||||
<license>MIT</license>
|
<license>MIT</license>
|
||||||
<version>1.3.51</version>
|
<version>1.3.61</version>
|
||||||
<licenseversion></licenseversion>
|
<licenseversion></licenseversion>
|
||||||
</library>
|
</library>
|
||||||
<library>
|
<library>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue