1
0
mirror of https://github.com/mrclay/minify.git synced 2025-08-19 12:21:20 +02:00

Merge branch 'master' into static

This commit is contained in:
Steve Clay
2017-01-18 21:09:38 -05:00
committed by GitHub
75 changed files with 1125 additions and 472 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@
/vendor /vendor
/.php_cs.cache /.php_cs.cache
/static/[0-9]* /static/[0-9]*
/tests/compiler.jar

View File

@@ -5,7 +5,7 @@ $finder = Symfony\CS\Finder\DefaultFinder::create()
; ;
return Symfony\CS\Config\Config::create() return Symfony\CS\Config\Config::create()
->level(Symfony\CS\FixerInterface::NONE_LEVEL) ->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
->setUsingCache(true) ->setUsingCache(true)
->fixers(array( ->fixers(array(
'linefeed', 'linefeed',
@@ -21,6 +21,7 @@ return Symfony\CS\Config\Config::create()
'controls_spaces', 'controls_spaces',
'elseif', 'elseif',
'-eof_ending', '-eof_ending',
'-method_argument_space',
)) ))
->finder($finder) ->finder($finder)
; ;

View File

@@ -6,7 +6,6 @@ php:
- 5.6 - 5.6
- 5.5 - 5.5
- 5.4 - 5.4
- 5.3
- hhvm - hhvm
matrix: matrix:
@@ -25,8 +24,14 @@ cache:
install: install:
- composer update --no-interaction --prefer-source - composer update --no-interaction --prefer-source
before_script:
- wget -c https://dl.google.com/closure-compiler/compiler-latest.zip -O vendor/compiler-latest.zip
- unzip -od vendor/closure-compiler vendor/compiler-latest.zip
- ln -sfn ../$(echo vendor/closure-compiler/closure-compiler-*.jar) tests/compiler.jar
- java -jar tests/compiler.jar --version
script: script:
- composer validate - composer validate
- phpunit - phpunit --verbose
# vim:ts=2:sw=2:et # vim:ts=2:sw=2:et

View File

@@ -23,21 +23,24 @@
"classmap": ["tests/TestCase.php"] "classmap": ["tests/TestCase.php"]
}, },
"require": { "require": {
"php": ">=5.3.0",
"ext-pcre": "*", "ext-pcre": "*",
"firephp/firephp-core": "~0.4.0",
"intervention/httpauth": "~2.0", "intervention/httpauth": "~2.0",
"monolog/monolog": "~1.1", "monolog/monolog": "~1.1",
"mrclay/jsmin-php": "~2", "mrclay/jsmin-php": "~2",
"mrclay/props-dic": "^2.2", "mrclay/props-dic": "^2.2",
"php": "^5.3.0 || ^7.0",
"tubalmartin/cssmin": "~2.4.8" "tubalmartin/cssmin": "~2.4.8"
}, },
"require-dev": { "require-dev": {
"firephp/firephp-core": "~0.4.0",
"leafo/lessphp": "~0.4.0", "leafo/lessphp": "~0.4.0",
"leafo/scssphp": "~0.6.6",
"meenie/javascript-packer": "~1.1", "meenie/javascript-packer": "~1.1",
"phpunit/phpunit": "4.8.*" "phpunit/phpunit": "4.8.*",
"tedivm/jshrink": "~1.1.0"
}, },
"suggest": { "suggest": {
"firephp/firephp-core": "Use FirePHP for Log messages",
"leafo/lessphp": "LESS support", "leafo/lessphp": "LESS support",
"meenie/javascript-packer": "Keep track of the Packer PHP port using Composer" "meenie/javascript-packer": "Keep track of the Packer PHP port using Composer"
}, },

View File

@@ -10,7 +10,7 @@ In other words, the "f" argument is set to the file path from root without the i
To combine multiple files, separate the paths given to "f" with commas. To combine multiple files, separate the paths given to "f" with commas.
Let's say you have CSS files at these URLs: Let's say you have JS files at these URLs:
* http://example.com/scripts/library-1.5.js * http://example.com/scripts/library-1.5.js
* http://example.com/scripts/site.js * http://example.com/scripts/site.js

View File

@@ -60,7 +60,8 @@
* @subpackage HTTP * @subpackage HTTP
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class HTTP_ConditionalGet { class HTTP_ConditionalGet
{
/** /**
* Does the client have a valid copy of the requested resource? * Does the client have a valid copy of the requested resource?
@@ -340,7 +341,8 @@ class HTTP_ConditionalGet {
* *
* @return string * @return string
*/ */
protected function normalizeEtag($etag) { protected function normalizeEtag($etag)
{
$etag = trim($etag); $etag = trim($etag);
return $this->_stripEtag return $this->_stripEtag

View File

@@ -43,7 +43,8 @@
* @subpackage HTTP * @subpackage HTTP
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class HTTP_Encoder { class HTTP_Encoder
{
/** /**
* Should the encoder allow HTTP encoding to IE6? * Should the encoder allow HTTP encoding to IE6?
@@ -97,8 +98,7 @@ class HTTP_Encoder {
$this->_headers['Content-Type'] = $spec['type']; $this->_headers['Content-Type'] = $spec['type'];
} }
if (isset($spec['method']) if (isset($spec['method'])
&& in_array($spec['method'], array('gzip', 'deflate', 'compress', ''))) && in_array($spec['method'], array('gzip', 'deflate', 'compress', ''))) {
{
$this->_encodeMethod = array($spec['method'], $spec['method']); $this->_encodeMethod = array($spec['method'], $spec['method']);
} else { } else {
$this->_encodeMethod = self::getAcceptedEncoding(); $this->_encodeMethod = self::getAcceptedEncoding();
@@ -192,8 +192,7 @@ class HTTP_Encoder {
// @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
if (! isset($_SERVER['HTTP_ACCEPT_ENCODING']) if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
|| self::isBuggyIe()) || self::isBuggyIe()) {
{
return array('', ''); return array('', '');
} }
$ae = $_SERVER['HTTP_ACCEPT_ENCODING']; $ae = $_SERVER['HTTP_ACCEPT_ENCODING'];
@@ -259,8 +258,7 @@ class HTTP_Encoder {
} }
if ('' === $this->_encodeMethod[0] if ('' === $this->_encodeMethod[0]
|| ($compressionLevel == 0) || ($compressionLevel == 0)
|| !extension_loaded('zlib')) || !extension_loaded('zlib')) {
{
return false; return false;
} }
if ($this->_encodeMethod[0] === 'deflate') { if ($this->_encodeMethod[0] === 'deflate') {

View File

@@ -24,7 +24,8 @@ use Psr\Log\LoggerInterface;
* @license http://opensource.org/licenses/bsd-license.php New BSD License * @license http://opensource.org/licenses/bsd-license.php New BSD License
* @link https://github.com/mrclay/minify * @link https://github.com/mrclay/minify
*/ */
class Minify { class Minify
{
/** /**
* API version * API version
@@ -87,7 +88,8 @@ class Minify {
* @param Minify_CacheInterface $cache * @param Minify_CacheInterface $cache
* @param LoggerInterface $logger * @param LoggerInterface $logger
*/ */
public function __construct(Minify_CacheInterface $cache, LoggerInterface $logger = null) { public function __construct(Minify_CacheInterface $cache, LoggerInterface $logger = null)
{
$this->cache = $cache; $this->cache = $cache;
$this->logger = $logger; $this->logger = $logger;
} }
@@ -272,7 +274,8 @@ class Minify {
// depending on what the client accepts, $contentEncoding may be // depending on what the client accepts, $contentEncoding may be
// 'x-gzip' while our internal encodeMethod is 'gzip'. Calling // 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
// getAcceptedEncoding(false, false) leaves out compress and deflate as options. // getAcceptedEncoding(false, false) leaves out compress and deflate as options.
list($this->options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false); $list = HTTP_Encoder::getAcceptedEncoding(false, false);
list($this->options['encodeMethod'], $contentEncoding) = $list;
$sendVary = ! HTTP_Encoder::isBuggyIe(); $sendVary = ! HTTP_Encoder::isBuggyIe();
} }
} else { } else {
@@ -492,11 +495,13 @@ class Minify {
* @param string $content * @param string $content
* @return string * @return string
*/ */
public static function nullMinifier($content) { public static function nullMinifier($content)
{
if (isset($content[0]) && $content[0] === "\xef") { if (isset($content[0]) && $content[0] === "\xef") {
$content = substr($content, 3); $content = substr($content, 3);
} }
$content = str_replace("\r\n", "\n", $content); $content = str_replace("\r\n", "\n", $content);
return trim($content); return trim($content);
} }
@@ -505,14 +510,14 @@ class Minify {
*/ */
protected function setupUriRewrites() protected function setupUriRewrites()
{ {
foreach($this->sources as $key => $source) { foreach ($this->sources as $key => $source) {
$file = $this->env->normalizePath($source->getFilePath()); $file = $this->env->normalizePath($source->getFilePath());
$minifyOptions = $source->getMinifierOptions(); $minifyOptions = $source->getMinifierOptions();
if ($file if ($file
&& !isset($minifyOptions['currentDir']) && !isset($minifyOptions['currentDir'])
&& !isset($minifyOptions['prependRelativePath']) && !isset($minifyOptions['prependRelativePath'])) {
) {
$minifyOptions['currentDir'] = dirname($file); $minifyOptions['currentDir'] = dirname($file);
$source->setMinifierOptions($minifyOptions); $source->setMinifierOptions($minifyOptions);
} }
@@ -592,9 +597,7 @@ class Minify {
! $source // yes, we ran out of sources ! $source // yes, we ran out of sources
|| $type === self::TYPE_CSS // yes, to process CSS individually (avoiding PCRE bugs/limits) || $type === self::TYPE_CSS // yes, to process CSS individually (avoiding PCRE bugs/limits)
|| $minifier !== $lastMinifier // yes, minifier changed || $minifier !== $lastMinifier // yes, minifier changed
|| $options !== $lastOptions) // yes, options changed || $options !== $lastOptions)) { // yes, options changed
)
{
// minify previous sources with last settings // minify previous sources with last settings
$imploded = implode($implodeSeparator, $groupToProcessTogether); $imploded = implode($implodeSeparator, $groupToProcessTogether);
$groupToProcessTogether = array(); $groupToProcessTogether = array();
@@ -717,10 +720,10 @@ class Minify {
if (!empty($options['contentType'])) { if (!empty($options['contentType'])) {
// just verify sources have null content type or match the options // just verify sources have null content type or match the options
if ($sourceType !== null && $sourceType !== $options['contentType']) { if ($sourceType !== null && $sourceType !== $options['contentType']) {
$this->logger && $this->logger->warning("ContentType mismatch: '{$sourceType}' != '{$options['contentType']}'");
$this->logger && $this->logger->warning('ContentType mismatch');
$this->sources = array(); $this->sources = array();
return $options; return $options;
} }
@@ -730,10 +733,10 @@ class Minify {
if ($type === null) { if ($type === null) {
$type = $sourceType; $type = $sourceType;
} elseif ($sourceType !== $type) { } elseif ($sourceType !== $type) {
$this->logger && $this->logger->warning("ContentType mismatch: '{$sourceType}' != '{$type}'");
$this->logger && $this->logger->warning('ContentType mismatch');
$this->sources = array(); $this->sources = array();
return $options; return $options;
} }
} }

View File

@@ -3,7 +3,6 @@
namespace Minify; namespace Minify;
use Props\Container; use Props\Container;
use Psr\Log\LoggerInterface;
/** /**
* @property \Minify_CacheInterface $cache * @property \Minify_CacheInterface $cache
@@ -22,7 +21,8 @@ use Psr\Log\LoggerInterface;
* @property \Minify_Source_Factory $sourceFactory * @property \Minify_Source_Factory $sourceFactory
* @property array $sourceFactoryOptions * @property array $sourceFactoryOptions
*/ */
class App extends Container { class App extends Container
{
/** /**
* Constructor * Constructor
@@ -64,9 +64,10 @@ class App extends Container {
$propNames = array_keys(get_object_vars($config)); $propNames = array_keys(get_object_vars($config));
$varNames = array_map(function ($name) { $prefixer = function ($name) {
return "min_$name"; return "min_$name";
}, $propNames); };
$varNames = array_map($prefixer, $propNames);
$vars = compact($varNames); $vars = compact($varNames);
@@ -104,12 +105,14 @@ class App extends Container {
if (empty($config->documentRoot)) { if (empty($config->documentRoot)) {
return $app->env->getDocRoot(); return $app->env->getDocRoot();
} }
return $app->env->normalizePath($config->documentRoot); return $app->env->normalizePath($config->documentRoot);
}; };
$this->env = function (App $app) { $this->env = function (App $app) {
$config = $app->config; $config = $app->config;
$envArgs = empty($config->envArgs) ? array() : $config->envArgs; $envArgs = empty($config->envArgs) ? array() : $config->envArgs;
return new \Minify_Env($envArgs); return new \Minify_Env($envArgs);
}; };
@@ -117,6 +120,7 @@ class App extends Container {
$format = "%channel%.%level_name%: %message% %context% %extra%"; $format = "%channel%.%level_name%: %message% %context% %extra%";
$handler = new \Monolog\Handler\ErrorLogHandler(); $handler = new \Monolog\Handler\ErrorLogHandler();
$handler->setFormatter(new \Monolog\Formatter\LineFormatter($format)); $handler->setFormatter(new \Monolog\Formatter\LineFormatter($format));
return $handler; return $handler;
}; };
@@ -142,11 +146,13 @@ class App extends Container {
if ($value === true || $value instanceof \FirePHP) { if ($value === true || $value instanceof \FirePHP) {
$logger->pushHandler($app->errorLogHandler); $logger->pushHandler($app->errorLogHandler);
$logger->pushHandler(new \Monolog\Handler\FirePHPHandler()); $logger->pushHandler(new \Monolog\Handler\FirePHPHandler());
return $logger; return $logger;
} }
if ($value instanceof \Monolog\Handler\HandlerInterface) { if ($value instanceof \Monolog\Handler\HandlerInterface) {
$logger->pushHandler($value); $logger->pushHandler($value);
return $logger; return $logger;
} }
@@ -154,6 +160,7 @@ class App extends Container {
if (is_object($value) && is_callable(array($value, 'log'))) { if (is_object($value) && is_callable(array($value, 'log'))) {
$handler = new \Minify\Logger\LegacyHandler($value); $handler = new \Minify\Logger\LegacyHandler($value);
$logger->pushHandler($handler); $logger->pushHandler($handler);
return $logger; return $logger;
} }
@@ -232,6 +239,10 @@ class App extends Container {
$ret['allowDirs'] = $serveOptions['minApp']['allowDirs']; $ret['allowDirs'] = $serveOptions['minApp']['allowDirs'];
} }
if (isset($serveOptions['checkAllowDirs'])) {
$ret['checkAllowDirs'] = $serveOptions['checkAllowDirs'];
}
if (is_numeric($app->config->uploaderHoursBehind)) { if (is_numeric($app->config->uploaderHoursBehind)) {
$ret['uploaderHoursBehind'] = $app->config->uploaderHoursBehind; $ret['uploaderHoursBehind'] = $app->config->uploaderHoursBehind;
} }
@@ -240,7 +251,8 @@ class App extends Container {
}; };
} }
public function runServer() { public function runServer()
{
if (!$this->env->get('f') && $this->env->get('g') === null) { if (!$this->env->get('f') && $this->env->get('g') === null) {
// no spec given // no spec given
$msg = '<p>No "f" or "g" parameters were detected.</p>'; $msg = '<p>No "f" or "g" parameters were detected.</p>';
@@ -256,8 +268,10 @@ class App extends Container {
* @param mixed $var * @param mixed $var
* @return string * @return string
*/ */
private function typeOf($var) { private function typeOf($var)
{
$type = gettype($var); $type = gettype($var);
return $type === 'object' ? get_class($var) : $type; return $type === 'object' ? get_class($var) : $type;
} }
} }

View File

@@ -34,7 +34,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_Build { class Minify_Build
{
/** /**
* Last modification time of all files in the build * Last modification time of all files in the build
@@ -67,15 +68,14 @@ class Minify_Build {
* append the timestamp to the URI. * append the timestamp to the URI.
* @return string * @return string
*/ */
public function uri($uri, $forceAmpersand = false) { public function uri($uri, $forceAmpersand = false)
$sep = ($forceAmpersand || strpos($uri, '?') !== false) {
? self::$ampersand $sep = ($forceAmpersand || strpos($uri, '?') !== false) ? self::$ampersand : '?';
: '?';
return "{$uri}{$sep}{$this->lastModified}"; return "{$uri}{$sep}{$this->lastModified}";
} }
/** /**
* Create a build object * Create a build object
* *
* @param array $sources array of Minify_Source objects and/or file paths * @param array $sources array of Minify_Source objects and/or file paths

View File

@@ -16,7 +16,8 @@
* *
* @deprecated Use Minify_CSSmin * @deprecated Use Minify_CSSmin
*/ */
class Minify_CSS { class Minify_CSS
{
/** /**
* Minify a CSS string * Minify a CSS string
@@ -70,32 +71,27 @@ class Minify_CSS {
if ($options['removeCharsets']) { if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css); $css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
} }
if ($options['compress']) { if ($options['compress']) {
if (! $options['preserveComments']) { if (! $options['preserveComments']) {
$css = Minify_CSS_Compressor::process($css, $options); $css = Minify_CSS_Compressor::process($css, $options);
} else { } else {
$css = Minify_CommentPreserver::process( $processor = array('Minify_CSS_Compressor', 'process');
$css $css = Minify_CommentPreserver::process($css, $processor, array($options));
,array('Minify_CSS_Compressor', 'process')
,array($options)
);
} }
} }
if (! $options['currentDir'] && ! $options['prependRelativePath']) { if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css; return $css;
} }
if ($options['currentDir']) { if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite( $currentDir = $options['currentDir'];
$css $docRoot = $options['docRoot'];
,$options['currentDir'] $symlinks = $options['symlinks'];
,$options['docRoot'] return Minify_CSS_UriRewriter::rewrite($css, $currentDir, $docRoot, $symlinks);
,$options['symlinks']
);
} else { } else {
return Minify_CSS_UriRewriter::prepend( return Minify_CSS_UriRewriter::prepend($css, $options['prependRelativePath']);
$css
,$options['prependRelativePath']
);
} }
} }
} }

View File

@@ -26,7 +26,8 @@
* *
* @deprecated Use CSSmin (tubalmartin/cssmin) * @deprecated Use CSSmin (tubalmartin/cssmin)
*/ */
class Minify_CSS_Compressor { class Minify_CSS_Compressor
{
/** /**
* Minify a CSS string * Minify a CSS string
@@ -61,7 +62,8 @@ class Minify_CSS_Compressor {
* *
* @param array $options (currently ignored) * @param array $options (currently ignored)
*/ */
private function __construct($options) { private function __construct($options)
{
$this->_options = $options; $this->_options = $options;
} }
@@ -86,8 +88,8 @@ class Minify_CSS_Compressor {
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css); $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
// apply callback to all valid comments (and strip out surrounding ws // apply callback to all valid comments (and strip out surrounding ws
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@' $pattern = '@\\s*/\\*([\\s\\S]*?)\\*/\\s*@';
,array($this, '_commentCB'), $css); $css = preg_replace_callback($pattern, array($this, '_commentCB'), $css);
// remove ws around { } and last semicolon in declaration block // remove ws around { } and last semicolon in declaration block
$css = preg_replace('/\\s*{\\s*/', '{', $css); $css = preg_replace('/\\s*{\\s*/', '{', $css);
@@ -97,16 +99,17 @@ class Minify_CSS_Compressor {
$css = preg_replace('/\\s*;\\s*/', ';', $css); $css = preg_replace('/\\s*;\\s*/', ';', $css);
// remove ws around urls // remove ws around urls
$css = preg_replace('/ $pattern = '/
url\\( # url( url\\( # url(
\\s* \\s*
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis) ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
\\s* \\s*
\\) # ) \\) # )
/x', 'url($1)', $css); /x';
$css = preg_replace($pattern, 'url($1)', $css);
// remove ws between rules and colons // remove ws between rules and colons
$css = preg_replace('/ $pattern = '/
\\s* \\s*
([{;]) # 1 = beginning of block or rule separator ([{;]) # 1 = beginning of block or rule separator
\\s* \\s*
@@ -115,10 +118,11 @@ class Minify_CSS_Compressor {
: :
\\s* \\s*
(\\b|[#\'"-]) # 3 = first character of a value (\\b|[#\'"-]) # 3 = first character of a value
/x', '$1$2:$3', $css); /x';
$css = preg_replace($pattern, '$1$2:$3', $css);
// remove ws in selectors // remove ws in selectors
$css = preg_replace_callback('/ $pattern = '/
(?: # non-capture (?: # non-capture
\\s* \\s*
[^~>+,\\s]+ # selector part [^~>+,\\s]+ # selector part
@@ -128,16 +132,16 @@ class Minify_CSS_Compressor {
\\s* \\s*
[^~>+,\\s]+ # selector part [^~>+,\\s]+ # selector part
{ # open declaration block { # open declaration block
/x' /x';
,array($this, '_selectorsCB'), $css); $css = preg_replace_callback($pattern, array($this, '_selectorsCB'), $css);
// minimize hex colors // minimize hex colors
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i' $pattern = '/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i';
, '$1#$2$3$4$5', $css); $css = preg_replace($pattern, '$1#$2$3$4$5', $css);
// remove spaces between font families // remove spaces between font families
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/' $pattern = '/font-family:([^;}]+)([;}])/';
,array($this, '_fontFamilyCB'), $css); $css = preg_replace_callback($pattern, array($this, '_fontFamilyCB'), $css);
$css = preg_replace('/@import\\s+url/', '@import url', $css); $css = preg_replace('/@import\\s+url/', '@import url', $css);
@@ -145,14 +149,15 @@ class Minify_CSS_Compressor {
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css); $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
// separate common descendent selectors w/ newlines (to limit line lengths) // separate common descendent selectors w/ newlines (to limit line lengths)
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css); $pattern = '/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/';
$css = preg_replace($pattern, "$1\n$2{", $css);
// Use newline after 1st numeric value (to limit line lengths). // Use newline after 1st numeric value (to limit line lengths).
$css = preg_replace('/ $pattern = '/
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
\\s+ \\s+
/x' /x';
,"$1\n", $css); $css = preg_replace($pattern, "$1\n", $css);
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
@@ -189,52 +194,54 @@ class Minify_CSS_Compressor {
if ($m === 'keep') { if ($m === 'keep') {
return '/**/'; return '/**/';
} }
if ($m === '" "') { if ($m === '" "') {
// component of http://tantek.com/CSS/Examples/midpass.html // component of http://tantek.com/CSS/Examples/midpass.html
return '/*" "*/'; return '/*" "*/';
} }
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) { if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
// component of http://tantek.com/CSS/Examples/midpass.html // component of http://tantek.com/CSS/Examples/midpass.html
return '/*";}}/* */'; return '/*";}}/* */';
} }
if ($this->_inHack) { if ($this->_inHack) {
// inversion: feeding only to one browser // inversion: feeding only to one browser
if (preg_match('@ $pattern = '@
^/ # comment started like /*/ ^/ # comment started like /*/
\\s* \\s*
(\\S[\\s\\S]+?) # has at least some non-ws content (\\S[\\s\\S]+?) # has at least some non-ws content
\\s* \\s*
/\\* # ends like /*/ or /**/ /\\* # ends like /*/ or /**/
@x', $m, $n)) { @x';
if (preg_match($pattern, $m, $n)) {
// end hack mode after this comment, but preserve the hack and comment content // end hack mode after this comment, but preserve the hack and comment content
$this->_inHack = false; $this->_inHack = false;
return "/*/{$n[1]}/**/"; return "/*/{$n[1]}/**/";
} }
} }
if (substr($m, -1) === '\\') { // comment ends like \*/ if (substr($m, -1) === '\\') { // comment ends like \*/
// begin hack mode and preserve hack // begin hack mode and preserve hack
$this->_inHack = true; $this->_inHack = true;
return '/*\\*/'; return '/*\\*/';
} }
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */ if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
// begin hack mode and preserve hack // begin hack mode and preserve hack
$this->_inHack = true; $this->_inHack = true;
return '/*/*/'; return '/*/*/';
} }
if ($this->_inHack) { if ($this->_inHack) {
// a regular comment ends hack mode but should be preserved // a regular comment ends hack mode but should be preserved
$this->_inHack = false; $this->_inHack = false;
return '/**/'; return '/**/';
} }
// Issue 107: if there's any surrounding whitespace, it may be important, so // Issue 107: if there's any surrounding whitespace, it may be important, so
// replace the comment with a single space // replace the comment with a single space
return $hasSurroundingWs // remove all other comments return $hasSurroundingWs ? ' ' : ''; // remove all other comments
? ' '
: '';
} }
/** /**
@@ -247,8 +254,10 @@ class Minify_CSS_Compressor {
protected function _fontFamilyCB($m) protected function _fontFamilyCB($m)
{ {
// Issue 210: must not eliminate WS between words in unquoted families // Issue 210: must not eliminate WS between words in unquoted families
$pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $flags = PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY;
$pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, $flags);
$out = 'font-family:'; $out = 'font-family:';
while (null !== ($piece = array_shift($pieces))) { while (null !== ($piece = array_shift($pieces))) {
if ($piece[0] !== '"' && $piece[0] !== "'") { if ($piece[0] !== '"' && $piece[0] !== "'") {
$piece = preg_replace('/\\s+/', ' ', $piece); $piece = preg_replace('/\\s+/', ' ', $piece);

View File

@@ -10,7 +10,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_CSS_UriRewriter { class Minify_CSS_UriRewriter
{
/** /**
* rewrite() and rewriteRelative() append debugging information here * rewrite() and rewriteRelative() append debugging information here
@@ -51,10 +52,10 @@ class Minify_CSS_UriRewriter {
// normalize symlinks in order to map to link // normalize symlinks in order to map to link
foreach ($symlinks as $link => $target) { foreach ($symlinks as $link => $target) {
$link = ($link === '//')
? self::$_docRoot $link = ($link === '//') ? self::$_docRoot : str_replace('//', self::$_docRoot . '/', $link);
: str_replace('//', self::$_docRoot . '/', $link);
$link = strtr($link, '/', DIRECTORY_SEPARATOR); $link = strtr($link, '/', DIRECTORY_SEPARATOR);
self::$_symlinks[$link] = self::_realpath($target); self::$_symlinks[$link] = self::_realpath($target);
} }
@@ -70,10 +71,11 @@ class Minify_CSS_UriRewriter {
$css = self::_owlifySvgPaths($css); $css = self::_owlifySvgPaths($css);
// rewrite // rewrite
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' $pattern = '/@import\\s+([\'"])(.*?)[\'"]/';
,array(self::$className, '_processUriCB'), $css); $css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css); $pattern = '/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/';
$css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css);
$css = self::_unOwlify($css); $css = self::_unOwlify($css);
@@ -98,10 +100,11 @@ class Minify_CSS_UriRewriter {
$css = self::_owlifySvgPaths($css); $css = self::_owlifySvgPaths($css);
// append // append
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' $pattern = '/@import\\s+([\'"])(.*?)[\'"]/';
,array(self::$className, '_processUriCB'), $css); $css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css); $pattern = '/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/';
$css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css);
$css = self::_unOwlify($css); $css = self::_unOwlify($css);
@@ -151,8 +154,8 @@ class Minify_CSS_UriRewriter {
public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array()) public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
{ {
// prepend path with current dir separator (OS-independent) // prepend path with current dir separator (OS-independent)
$path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR) $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR);
. DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR); $path .= DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
self::$debugText .= "file-relative URI : {$uri}\n" self::$debugText .= "file-relative URI : {$uri}\n"
. "path prepended : {$path}\n"; . "path prepended : {$path}\n";
@@ -262,13 +265,14 @@ class Minify_CSS_UriRewriter {
*/ */
private static function _trimUrls($css) private static function _trimUrls($css)
{ {
return preg_replace('/ $pattern = '/
url\\( # url( url\\( # url(
\\s* \\s*
([^\\)]+?) # 1 = URI (assuming does not contain ")") ([^\\)]+?) # 1 = URI (assuming does not contain ")")
\\s* \\s*
\\) # ) \\) # )
/x', 'url($1)', $css); /x';
return preg_replace($pattern, 'url($1)', $css);
} }
/** /**
@@ -286,13 +290,15 @@ class Minify_CSS_UriRewriter {
$uri = $m[2]; $uri = $m[2];
} else { } else {
// $m[1] is either quoted or not // $m[1] is either quoted or not
$quoteChar = ($m[1][0] === "'" || $m[1][0] === '"') $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"') ? $m[1][0] : '';
? $m[1][0]
: ''; $uri = ($quoteChar === '') ? $m[1] : substr($m[1], 1, strlen($m[1]) - 2);
$uri = ($quoteChar === '')
? $m[1]
: substr($m[1], 1, strlen($m[1]) - 2);
} }
if ($uri === '') {
return $m[0];
}
// if not root/scheme relative and not starts with scheme // if not root/scheme relative and not starts with scheme
if (!preg_match('~^(/|[a-z]+\:)~', $uri)) { if (!preg_match('~^(/|[a-z]+\:)~', $uri)) {
// URI is file-relative: rewrite depending on options // URI is file-relative: rewrite depending on options
@@ -312,9 +318,11 @@ class Minify_CSS_UriRewriter {
} }
} }
return $isImport if ($isImport) {
? "@import {$quoteChar}{$uri}{$quoteChar}" return "@import {$quoteChar}{$uri}{$quoteChar}";
: "url({$quoteChar}{$uri}{$quoteChar})"; } else {
return "url({$quoteChar}{$uri}{$quoteChar})";
}
} }
/** /**
@@ -326,8 +334,10 @@ class Minify_CSS_UriRewriter {
* @param string $css * @param string $css
* @return string * @return string
*/ */
private static function _owlifySvgPaths($css) { private static function _owlifySvgPaths($css)
return preg_replace('~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)url(\(\s*#\w+\s*\))~', '$1owl$2', $css); {
$pattern = '~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)url(\(\s*#\w+\s*\))~';
return preg_replace($pattern, '$1owl$2', $css);
} }
/** /**
@@ -338,7 +348,9 @@ class Minify_CSS_UriRewriter {
* @param string $css * @param string $css
* @return string * @return string
*/ */
private static function _unOwlify($css) { private static function _unOwlify($css)
return preg_replace('~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)owl~', '$1url', $css); {
$pattern = '~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)owl~';
return preg_replace($pattern, '$1url', $css);
} }
} }

View File

@@ -12,7 +12,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_CSSmin { class Minify_CSSmin
{
/** /**
* Minify a CSS string * Minify a CSS string

View File

@@ -14,7 +14,8 @@
* @package Minify * @package Minify
* @author Chris Edwards * @author Chris Edwards
**/ **/
class Minify_Cache_APC implements Minify_CacheInterface { class Minify_Cache_APC implements Minify_CacheInterface
{
/** /**
* Create a Minify_Cache_APC object, to be passed to * Create a Minify_Cache_APC object, to be passed to
@@ -58,9 +59,11 @@ class Minify_Cache_APC implements Minify_CacheInterface {
return false; return false;
} }
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
? mb_strlen($this->_data, '8bit') return mb_strlen($this->_data, '8bit');
: strlen($this->_data); } else {
return strlen($this->_data);;
}
} }
/** /**
@@ -84,9 +87,7 @@ class Minify_Cache_APC implements Minify_CacheInterface {
*/ */
public function display($id) public function display($id)
{ {
echo $this->_fetch($id) echo $this->_fetch($id) ? $this->_data : '';
? $this->_data
: '';
} }
/** /**
@@ -98,9 +99,7 @@ class Minify_Cache_APC implements Minify_CacheInterface {
*/ */
public function fetch($id) public function fetch($id)
{ {
return $this->_fetch($id) return $this->_fetch($id) ? $this->_data : '';
? $this->_data
: '';
} }
private $_exp = null; private $_exp = null;
@@ -125,9 +124,9 @@ class Minify_Cache_APC implements Minify_CacheInterface {
$ret = apc_fetch($id); $ret = apc_fetch($id);
if (false === $ret) { if (false === $ret) {
$this->_id = null; $this->_id = null;
return false; return false;
} }
list($this->_lm, $this->_data) = explode('|', $ret, 2); list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id; $this->_id = $id;

View File

@@ -6,7 +6,8 @@
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
class Minify_Cache_File implements Minify_CacheInterface { class Minify_Cache_File implements Minify_CacheInterface
{
/** /**
* @var string * @var string
@@ -53,17 +54,18 @@ class Minify_Cache_File implements Minify_CacheInterface {
*/ */
public function store($id, $data) public function store($id, $data)
{ {
$flag = $this->locking $flag = $this->locking ? LOCK_EX : null;
? LOCK_EX
: null;
$file = $this->path . '/' . $id; $file = $this->path . '/' . $id;
if (! @file_put_contents($file, $data, $flag)) { if (! @file_put_contents($file, $data, $flag)) {
$this->logger->warning("Minify_Cache_File: Write failed to '$file'"); $this->logger->warning("Minify_Cache_File: Write failed to '$file'");
} }
// write control // write control
if ($data !== $this->fetch($id)) { if ($data !== $this->fetch($id)) {
@unlink($file); @unlink($file);
$this->logger->warning("Minify_Cache_File: Post-write read failed for '$file'"); $this->logger->warning("Minify_Cache_File: Post-write read failed for '$file'");
return false; return false;
} }
@@ -105,18 +107,19 @@ class Minify_Cache_File implements Minify_CacheInterface {
*/ */
public function display($id) public function display($id)
{ {
if ($this->locking) { if (!$this->locking) {
$fp = fopen($this->path . '/' . $id, 'rb');
flock($fp, LOCK_SH);
fpassthru($fp);
flock($fp, LOCK_UN);
fclose($fp);
} else {
readfile($this->path . '/' . $id); readfile($this->path . '/' . $id);
return;
} }
$fp = fopen($this->path . '/' . $id, 'rb');
flock($fp, LOCK_SH);
fpassthru($fp);
flock($fp, LOCK_UN);
fclose($fp);
} }
/** /**
* Fetch the cached content * Fetch the cached content
* *
* @param string $id cache id (e.g. a filename) * @param string $id cache id (e.g. a filename)
@@ -125,20 +128,21 @@ class Minify_Cache_File implements Minify_CacheInterface {
*/ */
public function fetch($id) public function fetch($id)
{ {
if ($this->locking) { if (!$this->locking) {
$fp = fopen($this->path . '/' . $id, 'rb');
if (!$fp) {
return false;
}
flock($fp, LOCK_SH);
$ret = stream_get_contents($fp);
flock($fp, LOCK_UN);
fclose($fp);
return $ret;
} else {
return file_get_contents($this->path . '/' . $id); return file_get_contents($this->path . '/' . $id);
} }
$fp = fopen($this->path . '/' . $id, 'rb');
if (!$fp) {
return false;
}
flock($fp, LOCK_SH);
$ret = stream_get_contents($fp);
flock($fp, LOCK_UN);
fclose($fp);
return $ret;
} }
/** /**
@@ -160,6 +164,7 @@ class Minify_Cache_File implements Minify_CacheInterface {
public static function tmp() public static function tmp()
{ {
trigger_error(__METHOD__ . ' is deprecated in Minfy 3.0', E_USER_DEPRECATED); trigger_error(__METHOD__ . ' is deprecated in Minfy 3.0', E_USER_DEPRECATED);
return sys_get_temp_dir(); return sys_get_temp_dir();
} }

View File

@@ -17,7 +17,8 @@
* } * }
* </code> * </code>
**/ **/
class Minify_Cache_Memcache implements Minify_CacheInterface { class Minify_Cache_Memcache implements Minify_CacheInterface
{
/** /**
* Create a Minify_Cache_Memcache object, to be passed to * Create a Minify_Cache_Memcache object, to be passed to
@@ -61,9 +62,11 @@ class Minify_Cache_Memcache implements Minify_CacheInterface {
return false; return false;
} }
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
? mb_strlen($this->_data, '8bit') return mb_strlen($this->_data, '8bit');
: strlen($this->_data); } else {
return strlen($this->_data);
}
} }
/** /**
@@ -87,12 +90,10 @@ class Minify_Cache_Memcache implements Minify_CacheInterface {
*/ */
public function display($id) public function display($id)
{ {
echo $this->_fetch($id) echo $this->_fetch($id) ? $this->_data : '';
? $this->_data
: '';
} }
/** /**
* Fetch the cached content * Fetch the cached content
* *
* @param string $id cache id * @param string $id cache id
@@ -101,9 +102,7 @@ class Minify_Cache_Memcache implements Minify_CacheInterface {
*/ */
public function fetch($id) public function fetch($id)
{ {
return $this->_fetch($id) return $this->_fetch($id) ? $this->_data : '';
? $this->_data
: '';
} }
private $_mc = null; private $_mc = null;
@@ -114,7 +113,7 @@ class Minify_Cache_Memcache implements Minify_CacheInterface {
private $_data = null; private $_data = null;
private $_id = null; private $_id = null;
/** /**
* Fetch data and timestamp from memcache, store in instance * Fetch data and timestamp from memcache, store in instance
* *
* @param string $id * @param string $id
@@ -126,12 +125,14 @@ class Minify_Cache_Memcache implements Minify_CacheInterface {
if ($this->_id === $id) { if ($this->_id === $id) {
return true; return true;
} }
$ret = $this->_mc->get($id); $ret = $this->_mc->get($id);
if (false === $ret) { if (false === $ret) {
$this->_id = null; $this->_id = null;
return false; return false;
} }
list($this->_lm, $this->_data) = explode('|', $ret, 2); list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id; $this->_id = $id;

View File

@@ -8,7 +8,8 @@
* *
* @package Minify * @package Minify
*/ */
class Minify_Cache_Null implements Minify_CacheInterface { class Minify_Cache_Null implements Minify_CacheInterface
{
/** /**
* Write data to cache. * Write data to cache.
* *

View File

@@ -60,7 +60,11 @@ class Minify_Cache_WinCache implements Minify_CacheInterface
return false; return false;
} }
return (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload') & 2)) ? mb_strlen($this->_data, '8bit') : strlen($this->_data); if (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload') & 2)) {
return mb_strlen($this->_data, '8bit');
} else {
return strlen($this->_data);
}
} }
/** /**
@@ -99,12 +103,12 @@ class Minify_Cache_WinCache implements Minify_CacheInterface
return $this->_fetch($id) ? $this->_data : ''; return $this->_fetch($id) ? $this->_data : '';
} }
private $_exp = NULL; private $_exp = null;
// cache of most recently fetched id // cache of most recently fetched id
private $_lm = NULL; private $_lm = null;
private $_data = NULL; private $_data = null;
private $_id = NULL; private $_id = null;
/** /**
* Fetch data and timestamp from WinCache, store in instance * Fetch data and timestamp from WinCache, store in instance
@@ -118,13 +122,15 @@ class Minify_Cache_WinCache implements Minify_CacheInterface
if ($this->_id === $id) { if ($this->_id === $id) {
return true; return true;
} }
$suc = false; $suc = false;
$ret = wincache_ucache_get($id, $suc); $ret = wincache_ucache_get($id, $suc);
if (!$suc) { if (!$suc) {
$this->_id = NULL; $this->_id = null;
return false; return false;
} }
list($this->_lm, $this->_data) = explode('|', $ret, 2); list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id; $this->_id = $id;

View File

@@ -17,7 +17,8 @@
* @package Minify * @package Minify
* @author Elan Ruusamäe <glen@delfi.ee> * @author Elan Ruusamäe <glen@delfi.ee>
**/ **/
class Minify_Cache_XCache implements Minify_CacheInterface { class Minify_Cache_XCache implements Minify_CacheInterface
{
/** /**
* Create a Minify_Cache_XCache object, to be passed to * Create a Minify_Cache_XCache object, to be passed to
@@ -55,9 +56,11 @@ class Minify_Cache_XCache implements Minify_CacheInterface {
return false; return false;
} }
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
? mb_strlen($this->_data, '8bit') return mb_strlen($this->_data, '8bit');
: strlen($this->_data); } else {
return strlen($this->_data);
}
} }
/** /**
@@ -79,9 +82,7 @@ class Minify_Cache_XCache implements Minify_CacheInterface {
*/ */
public function display($id) public function display($id)
{ {
echo $this->_fetch($id) echo $this->_fetch($id) ? $this->_data : '';
? $this->_data
: '';
} }
/** /**
@@ -92,9 +93,7 @@ class Minify_Cache_XCache implements Minify_CacheInterface {
*/ */
public function fetch($id) public function fetch($id)
{ {
return $this->_fetch($id) return $this->_fetch($id) ? $this->_data : '';
? $this->_data
: '';
} }
private $_exp = null; private $_exp = null;
@@ -115,12 +114,14 @@ class Minify_Cache_XCache implements Minify_CacheInterface {
if ($this->_id === $id) { if ($this->_id === $id) {
return true; return true;
} }
$ret = xcache_get($id); $ret = xcache_get($id);
if (false === $ret) { if (false === $ret) {
$this->_id = null; $this->_id = null;
return false; return false;
} }
list($this->_lm, $this->_data) = explode('|', $ret, 2); list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id; $this->_id = $id;

View File

@@ -16,7 +16,8 @@
* @package Minify * @package Minify
* @author Patrick van Dissel * @author Patrick van Dissel
*/ */
class Minify_Cache_ZendPlatform implements Minify_CacheInterface { class Minify_Cache_ZendPlatform implements Minify_CacheInterface
{
/** /**
* Create a Minify_Cache_ZendPlatform object, to be passed to * Create a Minify_Cache_ZendPlatform object, to be passed to
@@ -54,9 +55,7 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface {
*/ */
public function getSize($id) public function getSize($id)
{ {
return $this->_fetch($id) return $this->_fetch($id) ? strlen($this->_data) : false;
? strlen($this->_data)
: false;
} }
/** /**
@@ -70,9 +69,7 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface {
*/ */
public function isValid($id, $srcMtime) public function isValid($id, $srcMtime)
{ {
$ret = ($this->_fetch($id) && ($this->_lm >= $srcMtime)); return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
return $ret;
} }
/** /**
@@ -82,9 +79,7 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface {
*/ */
public function display($id) public function display($id)
{ {
echo $this->_fetch($id) echo $this->_fetch($id) ? $this->_data : '';
? $this->_data
: '';
} }
/** /**
@@ -96,9 +91,7 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface {
*/ */
public function fetch($id) public function fetch($id)
{ {
return $this->_fetch($id) return $this->_fetch($id) ? $this->_data : '';
? $this->_data
: '';
} }
private $_exp = null; private $_exp = null;
@@ -120,12 +113,14 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface {
if ($this->_id === $id) { if ($this->_id === $id) {
return true; return true;
} }
$ret = output_cache_get($id, $this->_exp); $ret = output_cache_get($id, $this->_exp);
if (false === $ret) { if (false === $ret) {
$this->_id = null; $this->_id = null;
return false; return false;
} }
list($this->_lm, $this->_data) = explode('|', $ret, 2); list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id; $this->_id = $id;

View File

@@ -9,7 +9,8 @@
* *
* @package Minify * @package Minify
*/ */
interface Minify_CacheInterface { interface Minify_CacheInterface
{
/** /**
* Write data to cache. * Write data to cache.
* *

View File

@@ -24,19 +24,12 @@
* *
* </code> * </code>
* *
* @todo unit tests, $options docs
* @todo more options support (or should just passthru them all?)
*
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
* @author Elan Ruusamäe <glen@delfi.ee> * @author Elan Ruusamäe <glen@delfi.ee>
*/ */
class Minify_ClosureCompiler class Minify_ClosureCompiler
{ {
const OPTION_CHARSET = 'charset';
const OPTION_COMPILATION_LEVEL = 'compilation_level';
const OPTION_WARNING_LEVEL = 'warning_level';
public static $isDebug = false; public static $isDebug = false;
/** /**
@@ -61,6 +54,17 @@ class Minify_ClosureCompiler
*/ */
public static $javaExecutable = 'java'; public static $javaExecutable = 'java';
/**
* Default command line options passed to closure-compiler
*
* @var array
*/
public static $defaultOptions = array(
'charset' => 'utf-8',
'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
'warning_level' => 'QUIET',
);
/** /**
* Minify a Javascript string * Minify a Javascript string
* *
@@ -137,7 +141,8 @@ class Minify_ClosureCompiler
$this->checkJar(self::$jarFile); $this->checkJar(self::$jarFile);
$server = array( $server = array(
self::$javaExecutable, self::$javaExecutable,
'-jar', escapeshellarg(self::$jarFile) '-jar',
escapeshellarg(self::$jarFile)
); );
return $server; return $server;
@@ -151,24 +156,13 @@ class Minify_ClosureCompiler
{ {
$args = array(); $args = array();
$o = array_merge( $options = array_merge(
array( static::$defaultOptions,
self::OPTION_CHARSET => 'utf-8',
self::OPTION_COMPILATION_LEVEL => 'SIMPLE_OPTIMIZATIONS',
self::OPTION_WARNING_LEVEL => 'QUIET',
),
$userOptions $userOptions
); );
$charsetOption = $o[self::OPTION_CHARSET]; foreach ($options as $key => $value) {
if (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $charsetOption)) { $args[] = "--{$key} " . escapeshellarg($value);
$args[] = "--charset {$charsetOption}";
}
foreach (array(self::OPTION_COMPILATION_LEVEL, self::OPTION_WARNING_LEVEL) as $opt) {
if ($o[$opt]) {
$args[] = "--{$opt} " . escapeshellarg($o[$opt]);
}
} }
return $args; return $args;

View File

@@ -10,7 +10,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_CommentPreserver { class Minify_CommentPreserver
{
/** /**
* String to be prepended to each preserved comment * String to be prepended to each preserved comment
@@ -71,21 +72,16 @@ class Minify_CommentPreserver {
*/ */
private static function _nextComment($in) private static function _nextComment($in)
{ {
if ( if (false === ($start = strpos($in, '/*!')) || false === ($end = strpos($in, '*/', $start + 3))) {
false === ($start = strpos($in, '/*!'))
|| false === ($end = strpos($in, '*/', $start + 3))
) {
return array($in, false, false); return array($in, false, false);
} }
$ret = array(
substr($in, 0, $start)
,self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append
);
$endChars = (strlen($in) - $end - 2);
$ret[] = (0 === $endChars)
? ''
: substr($in, -$endChars);
return $ret; $beforeComment = substr($in, 0, $start);
$comment = self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append;
$endChars = (strlen($in) - $end - 2);
$afterComment = (0 === $endChars) ? '' : substr($in, -$endChars);
return array($beforeComment, $comment, $afterComment);
} }
} }

View File

@@ -15,7 +15,8 @@ use Monolog\Logger;
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
abstract class Minify_Controller_Base implements Minify_ControllerInterface { abstract class Minify_Controller_Base implements Minify_ControllerInterface
{
/** /**
* @var Minify_Env * @var Minify_Env

View File

@@ -4,7 +4,6 @@
* @package Minify * @package Minify
*/ */
use Psr\Log\LoggerInterface;
use Monolog\Logger; use Monolog\Logger;
/** /**
@@ -29,7 +28,8 @@ use Monolog\Logger;
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_Controller_Files extends Minify_Controller_Base { class Minify_Controller_Files extends Minify_Controller_Base
{
/** /**
* Set up file sources * Set up file sources
@@ -41,7 +41,8 @@ class Minify_Controller_Files extends Minify_Controller_Base {
* *
* 'files': (required) array of complete file paths, or a single path * 'files': (required) array of complete file paths, or a single path
*/ */
public function createConfiguration(array $options) { public function createConfiguration(array $options)
{
// strip controller options // strip controller options
$files = $options['files']; $files = $options['files'];

View File

@@ -23,7 +23,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_Controller_Groups extends Minify_Controller_Files { class Minify_Controller_Groups extends Minify_Controller_Files
{
/** /**
* Set up groups of files as sources * Set up groups of files as sources
@@ -35,7 +36,8 @@ class Minify_Controller_Groups extends Minify_Controller_Files {
* *
* @return array Minify options * @return array Minify options
*/ */
public function createConfiguration(array $options) { public function createConfiguration(array $options)
{
// strip controller options // strip controller options
$groups = $options['groups']; $groups = $options['groups'];
unset($options['groups']); unset($options['groups']);
@@ -43,12 +45,14 @@ class Minify_Controller_Groups extends Minify_Controller_Files {
$server = $this->env->server(); $server = $this->env->server();
// mod_fcgid places PATH_INFO in ORIG_PATH_INFO // mod_fcgid places PATH_INFO in ORIG_PATH_INFO
$pathInfo = isset($server['ORIG_PATH_INFO']) if (isset($server['ORIG_PATH_INFO'])) {
? substr($server['ORIG_PATH_INFO'], 1) $pathInfo = substr($server['ORIG_PATH_INFO'], 1);
: (isset($server['PATH_INFO']) } elseif (isset($server['PATH_INFO'])) {
? substr($server['PATH_INFO'], 1) $pathInfo = substr($server['PATH_INFO'], 1)
: false } else {
); $pathInfo = false;
}
if (false === $pathInfo || ! isset($groups[$pathInfo])) { if (false === $pathInfo || ! isset($groups[$pathInfo])) {
// no PATH_INFO or not a valid group // no PATH_INFO or not a valid group
$this->logger->info("Missing PATH_INFO or no group set for \"$pathInfo\""); $this->logger->info("Missing PATH_INFO or no group set for \"$pathInfo\"");

View File

@@ -10,7 +10,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_Controller_MinApp extends Minify_Controller_Base { class Minify_Controller_MinApp extends Minify_Controller_Base
{
/** /**
* Set up groups of files as sources * Set up groups of files as sources
@@ -19,7 +20,8 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
* *
* @return array Minify options * @return array Minify options
*/ */
public function createConfiguration(array $options) { public function createConfiguration(array $options)
{
// PHP insecure by default: realpath() and other FS functions can't handle null bytes. // PHP insecure by default: realpath() and other FS functions can't handle null bytes.
$get = $this->env->get(); $get = $this->env->get();
foreach (array('g', 'b', 'f') as $key) { foreach (array('g', 'b', 'f') as $key) {
@@ -29,14 +31,14 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
} }
// filter controller options // filter controller options
$localOptions = array_merge( $defaults = array(
array( 'groupsOnly' => false,
'groupsOnly' => false, 'groups' => array(),
'groups' => array(), 'symlinks' => array(),
'symlinks' => array(),
)
,(isset($options['minApp']) ? $options['minApp'] : array())
); );
$minApp = isset($options['minApp']) ? $options['minApp'] : array();
$localOptions = array_merge($defaults, $minApp);
unset($options['minApp']); unset($options['minApp']);
// normalize $symlinks in order to map to target // normalize $symlinks in order to map to target
@@ -51,7 +53,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
$sources = array(); $sources = array();
$selectionId = ''; $selectionId = '';
$firstMissingResource = null; $firstMissing = null;
if (isset($get['g'])) { if (isset($get['g'])) {
// add group(s) // add group(s)
@@ -81,12 +83,12 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
$sources[] = $source; $sources[] = $source;
} catch (Minify_Source_FactoryException $e) { } catch (Minify_Source_FactoryException $e) {
$this->logger->error($e->getMessage()); $this->logger->error($e->getMessage());
if (null === $firstMissingResource) { if (null === $firstMissing) {
$firstMissingResource = basename($file); $firstMissing = basename($file);
continue; continue;
} else { } else {
$secondMissingResource = basename($file); $secondMissing = basename($file);
$this->logger->info("More than one file was missing: '$firstMissingResource', '$secondMissingResource'"); $this->logger->info("More than one file was missing: '$firstMissing', '$secondMissing'");
return new Minify_ServeConfiguration($options); return new Minify_ServeConfiguration($options);
} }
@@ -98,18 +100,18 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
// try user files // try user files
// The following restrictions are to limit the URLs that minify will // The following restrictions are to limit the URLs that minify will
// respond to. // respond to.
if (// verify at least one file, files are single comma separated,
// and are all same extension // verify at least one file, files are single comma separated, and are all same extension
! preg_match('/^[^,]+\\.(css|less|js)(?:,[^,]+\\.\\1)*$/', $get['f'], $m) $validPattern = preg_match('/^[^,]+\\.(css|less|scss|js)(?:,[^,]+\\.\\1)*$/', $get['f'], $m);
// no "//" $hasComment = strpos($get['f'], '//') !== false;
|| strpos($get['f'], '//') !== false $hasEscape = strpos($get['f'], '\\') !== false;
// no "\"
|| strpos($get['f'], '\\') !== false if (!$validPattern || $hasComment || $hasEscape) {
) {
$this->logger->info("GET param 'f' was invalid"); $this->logger->info("GET param 'f' was invalid");
return new Minify_ServeConfiguration($options); return new Minify_ServeConfiguration($options);
} }
$ext = ".{$m[1]}"; $ext = ".{$m[1]}";
$files = explode(',', $get['f']); $files = explode(',', $get['f']);
if ($files != array_unique($files)) { if ($files != array_unique($files)) {
@@ -117,11 +119,14 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
return new Minify_ServeConfiguration($options); return new Minify_ServeConfiguration($options);
} }
if (isset($get['b'])) { if (isset($get['b'])) {
// check for validity // check for validity
if (preg_match('@^[^/]+(?:/[^/]+)*$@', $get['b']) $isValidBase = preg_match('@^[^/]+(?:/[^/]+)*$@', $get['b']);
&& false === strpos($get['b'], '..') $hasDots = false !== strpos($get['b'], '..');
&& $get['b'] !== '.') { $isDot = $get['b'] === '.';
if ($isValidBase && !$hasDots && !$isDot) {
// valid base // valid base
$base = "/{$get['b']}/"; $base = "/{$get['b']}/";
} else { } else {
@@ -152,12 +157,12 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
$basenames[] = basename($path, $ext); $basenames[] = basename($path, $ext);
} catch (Minify_Source_FactoryException $e) { } catch (Minify_Source_FactoryException $e) {
$this->logger->error($e->getMessage()); $this->logger->error($e->getMessage());
if (null === $firstMissingResource) { if (null === $firstMissing) {
$firstMissingResource = $uri; $firstMissing = $uri;
continue; continue;
} else { } else {
$secondMissingResource = $uri; $secondMissing = $uri;
$this->logger->info("More than one file was missing: '$firstMissingResource', '$secondMissingResource`'"); $this->logger->info("More than one file was missing: '$firstMissing', '$secondMissing`'");
return new Minify_ServeConfiguration($options); return new Minify_ServeConfiguration($options);
} }
@@ -175,14 +180,14 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
return new Minify_ServeConfiguration($options); return new Minify_ServeConfiguration($options);
} }
if (null !== $firstMissingResource) { if (null !== $firstMissing) {
array_unshift($sources, new Minify_Source(array( array_unshift($sources, new Minify_Source(array(
'id' => 'missingFile' 'id' => 'missingFile',
// should not cause cache invalidation // should not cause cache invalidation
,'lastModified' => 0 'lastModified' => 0,
// due to caching, filename is unreliable. // due to caching, filename is unreliable.
,'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n" 'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n",
,'minifier' => 'Minify::nullMinifier' 'minifier' => 'Minify::nullMinifier',
))); )));
} }

View File

@@ -11,7 +11,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_Controller_Page extends Minify_Controller_Base { class Minify_Controller_Page extends Minify_Controller_Base
{
/** /**
* Set up source of HTML content * Set up source of HTML content
@@ -31,7 +32,8 @@ class Minify_Controller_Page extends Minify_Controller_Base {
* 'minifyAll': should all CSS and Javascript blocks be individually * 'minifyAll': should all CSS and Javascript blocks be individually
* minified? (default false) * minified? (default false)
*/ */
public function createConfiguration(array $options) { public function createConfiguration(array $options)
{
if (isset($options['file'])) { if (isset($options['file'])) {
$sourceSpec = array( $sourceSpec = array(
'filepath' => $options['file'] 'filepath' => $options['file']
@@ -40,8 +42,8 @@ class Minify_Controller_Page extends Minify_Controller_Base {
} else { } else {
// strip controller options // strip controller options
$sourceSpec = array( $sourceSpec = array(
'content' => $options['content'] 'content' => $options['content'],
,'id' => $options['id'] 'id' => $options['id'],
); );
$f = $options['id']; $f = $options['id'];
unset($options['content'], $options['id']); unset($options['content'], $options['id']);
@@ -52,8 +54,8 @@ class Minify_Controller_Page extends Minify_Controller_Base {
if (isset($options['minifyAll'])) { if (isset($options['minifyAll'])) {
// this will be the 2nd argument passed to Minify_HTML::minify() // this will be the 2nd argument passed to Minify_HTML::minify()
$sourceSpec['minifyOptions'] = array( $sourceSpec['minifyOptions'] = array(
'cssMinifier' => array('Minify_CSSmin', 'minify') 'cssMinifier' => array('Minify_CSSmin', 'minify'),
,'jsMinifier' => array('JSMin\\JSMin', 'minify') 'jsMinifier' => array('JSMin\\JSMin', 'minify'),
); );
unset($options['minifyAll']); unset($options['minifyAll']);
} }

View File

@@ -1,8 +1,8 @@
<?php <?php
use Psr\Log\LoggerInterface;
interface Minify_ControllerInterface { interface Minify_ControllerInterface
{
/** /**
* Create controller sources and options for Minify::serve() * Create controller sources and options for Minify::serve()

View File

@@ -6,12 +6,14 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_DebugDetector { class Minify_DebugDetector
{
public static function shouldDebugRequest(Minify_Env $env) public static function shouldDebugRequest(Minify_Env $env)
{ {
if ($env->get('debug') !== null) { if ($env->get('debug') !== null) {
return true; return true;
} }
$cookieValue = $env->cookie('minifyDebug'); $cookieValue = $env->cookie('minifyDebug');
if ($cookieValue) { if ($cookieValue) {
foreach (preg_split('/\\s+/', $cookieValue) as $debugUri) { foreach (preg_split('/\\s+/', $cookieValue) as $debugUri) {

View File

@@ -1,6 +1,7 @@
<?php <?php
class Minify_Env { class Minify_Env
{
/** /**
* @return string * @return string
@@ -33,6 +34,7 @@ class Minify_Env {
} else { } else {
$this->server['DOCUMENT_ROOT'] = rtrim($this->server['DOCUMENT_ROOT'], '/\\'); $this->server['DOCUMENT_ROOT'] = rtrim($this->server['DOCUMENT_ROOT'], '/\\');
} }
$this->server['DOCUMENT_ROOT'] = $this->normalizePath($this->server['DOCUMENT_ROOT']); $this->server['DOCUMENT_ROOT'] = $this->normalizePath($this->server['DOCUMENT_ROOT']);
$this->get = $options['get']; $this->get = $options['get'];
$this->post = $options['post']; $this->post = $options['post'];
@@ -45,9 +47,7 @@ class Minify_Env {
return $this->server; return $this->server;
} }
return isset($this->server[$key]) return isset($this->server[$key]) ? $this->server[$key] : null;
? $this->server[$key]
: null;
} }
public function cookie($key = null, $default = null) public function cookie($key = null, $default = null)
@@ -92,11 +92,13 @@ class Minify_Env {
if ($realpath) { if ($realpath) {
$path = $realpath; $path = $realpath;
} }
$path = str_replace('\\', '/', $path); $path = str_replace('\\', '/', $path);
$path = rtrim($path, '/'); $path = rtrim($path, '/');
if (substr($path, 1, 1) === ':') { if (substr($path, 1, 1) === ':') {
$path = lcfirst($path); $path = lcfirst($path);
} }
return $path; return $path;
} }
@@ -116,11 +118,9 @@ class Minify_Env {
if (isset($server['SERVER_SOFTWARE']) && 0 !== strpos($server['SERVER_SOFTWARE'], 'Microsoft-IIS/')) { if (isset($server['SERVER_SOFTWARE']) && 0 !== strpos($server['SERVER_SOFTWARE'], 'Microsoft-IIS/')) {
throw new InvalidArgumentException('DOCUMENT_ROOT is not provided and could not be computed'); throw new InvalidArgumentException('DOCUMENT_ROOT is not provided and could not be computed');
} }
$docRoot = substr(
$server['SCRIPT_FILENAME'] $substrLength = strlen($server['SCRIPT_FILENAME']) - strlen($server['SCRIPT_NAME']);
,0 $docRoot = substr($server['SCRIPT_FILENAME'], 0, $substrLength);
,strlen($server['SCRIPT_FILENAME']) - strlen($server['SCRIPT_NAME'])
);
return rtrim($docRoot, '\\'); return rtrim($docRoot, '\\');
} }

View File

@@ -16,7 +16,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_HTML { class Minify_HTML
{
/** /**
* @var boolean * @var boolean
*/ */
@@ -40,7 +41,8 @@ class Minify_HTML {
* *
* @return string * @return string
*/ */
public static function minify($html, $options = array()) { public static function minify($html, $options = array())
{
$min = new self($html, $options); $min = new self($html, $options);
return $min->process(); return $min->process();

View File

@@ -10,7 +10,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_HTML_Helper { class Minify_HTML_Helper
{
public $rewriteWorks = true; public $rewriteWorks = true;
public $minAppUri = '/min'; public $minAppUri = '/min';
public $groupsConfigFile = ''; public $groupsConfigFile = '';
@@ -98,8 +99,7 @@ class Minify_HTML_Helper {
foreach ($files as $k => $file) { foreach ($files as $k => $file) {
if (0 === strpos($file, '//')) { if (0 === strpos($file, '//')) {
$file = substr($file, 2); $file = substr($file, 2);
} elseif (0 === strpos($file, '/') } elseif (0 === strpos($file, '/') || 1 === strpos($file, ':\\')) {
|| 1 === strpos($file, ':\\')) {
$file = substr($file, strlen(self::app()->env->getDocRoot()) + 1); $file = substr($file, strlen(self::app()->env->getDocRoot()) + 1);
} }
$file = strtr($file, '\\', '/'); $file = strtr($file, '\\', '/');
@@ -168,11 +168,13 @@ class Minify_HTML_Helper {
static $cached; static $cached;
if ($app) { if ($app) {
$cached = $app; $cached = $app;
return $app; return $app;
} }
if ($cached === null) { if ($cached === null) {
$cached = (require __DIR__ . '/../../../bootstrap.php'); $cached = (require __DIR__ . '/../../../bootstrap.php');
} }
return $cached; return $cached;
} }
@@ -188,7 +190,8 @@ class Minify_HTML_Helper {
* @param int $pos index to check * @param int $pos index to check
* @return mixed a common char or '' if any do not match * @return mixed a common char or '' if any do not match
*/ */
protected static function _getCommonCharAtPos($arr, $pos) { protected static function _getCommonCharAtPos($arr, $pos)
{
if (!isset($arr[0][$pos])) { if (!isset($arr[0][$pos])) {
return ''; return '';
} }
@@ -213,7 +216,8 @@ class Minify_HTML_Helper {
* @param string $minRoot root-relative URI of the "min" application * @param string $minRoot root-relative URI of the "min" application
* @return string * @return string
*/ */
protected static function _getShortestUri($paths, $minRoot = '/min/') { protected static function _getShortestUri($paths, $minRoot = '/min/')
{
$pos = 0; $pos = 0;
$base = ''; $base = '';
while (true) { while (true) {
@@ -238,9 +242,7 @@ class Minify_HTML_Helper {
$base = substr($base, 0, strlen($base) - 1); $base = substr($base, 0, strlen($base) - 1);
$bUri = $minRoot . 'b=' . $base . '&f=' . implode(',', $basedPaths); $bUri = $minRoot . 'b=' . $base . '&f=' . implode(',', $basedPaths);
$uri = strlen($uri) < strlen($bUri) $uri = strlen($uri) < strlen($bUri) ? $uri : $bUri;
? $uri
: $bUri;
} }
return $uri; return $uri;

View File

@@ -18,8 +18,8 @@
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
* @author Simon Schick <simonsimcity@gmail.com> * @author Simon Schick <simonsimcity@gmail.com>
*/ */
class Minify_ImportProcessor { class Minify_ImportProcessor
{
public static $filesIncluded = array(); public static $filesIncluded = array();
public static function process($file) public static function process($file)
@@ -57,8 +57,7 @@ class Minify_ImportProcessor {
$file = realpath($file); $file = realpath($file);
if (! $file if (! $file
|| in_array($file, self::$filesIncluded) || in_array($file, self::$filesIncluded)
|| false === ($content = @file_get_contents($file)) || false === ($content = @file_get_contents($file))) {
) {
// file missing, already included, or failed read // file missing, already included, or failed read
return ''; return '';
} }
@@ -73,8 +72,7 @@ class Minify_ImportProcessor {
$content = str_replace("\r\n", "\n", $content); $content = str_replace("\r\n", "\n", $content);
// process @imports // process @imports
$content = preg_replace_callback( $pattern = '/
'/
@import\\s+ @import\\s+
(?:url\\(\\s*)? # maybe url( (?:url\\(\\s*)? # maybe url(
[\'"]? # maybe quote [\'"]? # maybe quote
@@ -83,19 +81,14 @@ class Minify_ImportProcessor {
(?:\\s*\\))? # maybe ) (?:\\s*\\))? # maybe )
([a-zA-Z,\\s]*)? # 2 = media list ([a-zA-Z,\\s]*)? # 2 = media list
; # end token ; # end token
/x' /x';
,array($this, '_importCB') $content = preg_replace_callback($pattern, array($this, '_importCB'), $content);
,$content
);
// You only need to rework the import-path if the script is imported // You only need to rework the import-path if the script is imported
if (self::$_isCss && $is_imported) { if (self::$_isCss && $is_imported) {
// rewrite remaining relative URIs // rewrite remaining relative URIs
$content = preg_replace_callback( $pattern = '/url\\(\\s*([^\\)\\s]+)\\s*\\)/';
'/url\\(\\s*([^\\)\\s]+)\\s*\\)/' $content = preg_replace_callback($pattern, array($this, '_urlCB'), $content);
,array($this, '_urlCB')
,$content
);
} }
return $this->_importedContent . $content; return $this->_importedContent . $content;
@@ -139,12 +132,10 @@ class Minify_ImportProcessor {
private function _urlCB($m) private function _urlCB($m)
{ {
// $m[1] is either quoted or not // $m[1] is either quoted or not
$quote = ($m[1][0] === "'" || $m[1][0] === '"') $quote = ($m[1][0] === "'" || $m[1][0] === '"') ? $m[1][0] : '';
? $m[1][0]
: ''; $url = ($quote === '') ? $m[1] : substr($m[1], 1, strlen($m[1]) - 2);
$url = ($quote === '')
? $m[1]
: substr($m[1], 1, strlen($m[1]) - 2);
if ('/' !== $url[0]) { if ('/' !== $url[0]) {
if (strpos($url, '//') > 0) { if (strpos($url, '//') > 0) {
// probably starts with protocol, do not alter // probably starts with protocol, do not alter
@@ -173,8 +164,7 @@ class Minify_ImportProcessor {
$arFrom = explode($ps, rtrim($realFrom, $ps)); $arFrom = explode($ps, rtrim($realFrom, $ps));
$arTo = explode($ps, rtrim($realTo, $ps)); $arTo = explode($ps, rtrim($realTo, $ps));
while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) {
{
array_shift($arFrom); array_shift($arFrom);
array_shift($arTo); array_shift($arTo);
} }
@@ -191,28 +181,33 @@ class Minify_ImportProcessor {
private function truepath($path) private function truepath($path)
{ {
// whether $path is unix or not // whether $path is unix or not
$unipath = strlen($path) == 0 || $path{0} != '/'; $unipath = (strlen($path) == 0) || ($path{0} != '/');
// attempts to detect if path is relative in which case, add cwd // attempts to detect if path is relative in which case, add cwd
if (strpos($path, ':') === false && $unipath) if (strpos($path, ':') === false && $unipath) {
$path = $this->_currentDir . DIRECTORY_SEPARATOR . $path; $path = $this->_currentDir . DIRECTORY_SEPARATOR . $path;
}
// resolve path parts (single dot, double dot and double delimiters) // resolve path parts (single dot, double dot and double delimiters)
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = array(); $absolutes = array();
foreach ($parts as $part) { foreach ($parts as $part) {
if ('.' == $part) if ('.' == $part) {
continue; continue;
}
if ('..' == $part) { if ('..' == $part) {
array_pop($absolutes); array_pop($absolutes);
} else { } else {
$absolutes[] = $part; $absolutes[] = $part;
} }
} }
$path = implode(DIRECTORY_SEPARATOR, $absolutes); $path = implode(DIRECTORY_SEPARATOR, $absolutes);
// resolve any symlinks // resolve any symlinks
if (file_exists($path) && linkinfo($path) > 0) if (file_exists($path) && linkinfo($path) > 0) {
$path = readlink($path); $path = readlink($path);
}
// put initial separator that could have been lost // put initial separator that could have been lost
$path = !$unipath ? '/' . $path : $path; $path = !$unipath ? '/' . $path : $path;

View File

@@ -13,7 +13,8 @@
* *
* @todo can use a stream wrapper to unit test this? * @todo can use a stream wrapper to unit test this?
*/ */
class Minify_JS_ClosureCompiler { class Minify_JS_ClosureCompiler
{
/** /**
* @var string The option key for the maximum POST byte size * @var string The option key for the maximum POST byte size
@@ -228,4 +229,6 @@ class Minify_JS_ClosureCompiler {
} }
} }
class Minify_JS_ClosureCompiler_Exception extends Exception {} class Minify_JS_ClosureCompiler_Exception extends Exception
{
}

48
lib/Minify/JS/JShrink.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
/**
* Class Minify\JS\JShrink
*
* @package Minify
*/
namespace Minify\JS;
/**
* Wrapper to Javascript Minifier built in PHP http://www.tedivm.com
*
* @package Minify
* @author Elan Ruusamäe <glen@pld-linux.org>
* @link https://github.com/tedious/JShrink
*
*/
class JShrink
{
/**
* Contains the default options for minification. This array is merged with
* the one passed in by the user to create the request specific set of
* options (stored in the $options attribute).
*
* @var string[]
*/
protected static $defaultOptions = array('flaggedComments' => true);
/**
* Takes a string containing javascript and removes unneeded characters in
* order to shrink the code without altering it's functionality.
*
* @param string $js The raw javascript to be minified
* @param array $options Various runtime options in an associative array
*
* @see JShrink\Minifier::minify()
* @return string
*/
public static function minify($js, array $options = array())
{
$options = array_merge(
self::$defaultOptions,
$options
);
return \JShrink\Minifier::minify($js, $options);
}
}

View File

@@ -1,6 +1,7 @@
<?php <?php
class Minify_LessCssSource extends Minify_Source { class Minify_LessCssSource extends Minify_Source
{
/** /**
* @var Minify_CacheInterface * @var Minify_CacheInterface
*/ */
@@ -16,7 +17,8 @@ class Minify_LessCssSource extends Minify_Source {
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function __construct(array $spec, Minify_CacheInterface $cache) { public function __construct(array $spec, Minify_CacheInterface $cache)
{
parent::__construct($spec); parent::__construct($spec);
$this->cache = $cache; $this->cache = $cache;
@@ -27,16 +29,11 @@ class Minify_LessCssSource extends Minify_Source {
* *
* @return int * @return int
*/ */
public function getLastModified() { public function getLastModified()
{
$cache = $this->getCache(); $cache = $this->getCache();
$lastModified = 0; return $cache['lastModified'];
foreach ($cache['files'] as $mtime) {
$lastModified = max($lastModified, $mtime);
}
return $lastModified;
} }
/** /**
@@ -44,7 +41,8 @@ class Minify_LessCssSource extends Minify_Source {
* *
* @return string * @return string
*/ */
public function getContent() { public function getContent()
{
$cache = $this->getCache(); $cache = $this->getCache();
return $cache['compiled']; return $cache['compiled'];
@@ -55,7 +53,8 @@ class Minify_LessCssSource extends Minify_Source {
* *
* @return array * @return array
*/ */
private function getCache() { private function getCache()
{
// cache for single run // cache for single run
// so that getLastModified and getContent in single request do not add additional cache roundtrips (i.e memcache) // so that getLastModified and getContent in single request do not add additional cache roundtrips (i.e memcache)
if (isset($this->parsed)) { if (isset($this->parsed)) {
@@ -76,12 +75,29 @@ class Minify_LessCssSource extends Minify_Source {
$cache = $less->cachedCompile($input); $cache = $less->cachedCompile($input);
if (!is_array($input) || $cache['updated'] > $input['updated']) { if (!is_array($input) || $cache['updated'] > $input['updated']) {
$cache['lastModified'] = $this->getMaxLastModified($cache);
$this->cache->store($cacheId, serialize($cache)); $this->cache->store($cacheId, serialize($cache));
} }
return $this->parsed = $cache; return $this->parsed = $cache;
} }
/**
* Calculate maximum last modified of all files,
* as the 'updated' timestamp in cache is not the same as file last modified timestamp:
* @link https://github.com/leafo/lessphp/blob/v0.4.0/lessc.inc.php#L1904
* @return int
*/
private function getMaxLastModified($cache)
{
$lastModified = 0;
foreach ($cache['files'] as $mtime) {
$lastModified = max($lastModified, $mtime);
}
return $lastModified;
}
/** /**
* Make a unique cache id for for this source. * Make a unique cache id for for this source.
* *
@@ -89,10 +105,11 @@ class Minify_LessCssSource extends Minify_Source {
* *
* @return string * @return string
*/ */
private function getCacheId($prefix = 'minify') { private function getCacheId($prefix = 'minify')
{
$md5 = md5($this->filepath); $md5 = md5($this->filepath);
return "{$prefix}_less_{$md5}"; return "{$prefix}_less2_{$md5}";
} }
/** /**
@@ -100,7 +117,8 @@ class Minify_LessCssSource extends Minify_Source {
* *
* @return lessc * @return lessc
*/ */
private function getCompiler() { private function getCompiler()
{
$less = new lessc(); $less = new lessc();
// do not spend CPU time letting less doing minify // do not spend CPU time letting less doing minify
$less->setPreserveComments(true); $less->setPreserveComments(true);

View File

@@ -11,7 +11,8 @@
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
* @author Adam Pedersen (Issue 55 fix) * @author Adam Pedersen (Issue 55 fix)
*/ */
class Minify_Lines { class Minify_Lines
{
/** /**
* Add line numbers in C-style comments * Add line numbers in C-style comments
@@ -36,16 +37,9 @@ class Minify_Lines {
*/ */
public static function minify($content, $options = array()) public static function minify($content, $options = array())
{ {
$id = (isset($options['id']) && $options['id']) $id = (isset($options['id']) && $options['id']) ? $options['id'] : '';
? $options['id']
: '';
$content = str_replace("\r\n", "\n", $content); $content = str_replace("\r\n", "\n", $content);
// Hackily rewrite strings with XPath expressions that are
// likely to throw off our dumb parser (for Prototype 1.6.1).
$content = str_replace('"/*"', '"/"+"*"', $content);
$content = preg_replace('@([\'"])(\\.?//?)\\*@', '$1$2$1+$1*', $content);
$lines = explode("\n", $content); $lines = explode("\n", $content);
$numLines = count($lines); $numLines = count($lines);
// determine left padding // determine left padding
@@ -53,6 +47,7 @@ class Minify_Lines {
$inComment = false; $inComment = false;
$i = 0; $i = 0;
$newLines = array(); $newLines = array();
while (null !== ($line = array_shift($lines))) { while (null !== ($line = array_shift($lines))) {
if (('' !== $id) && (0 == $i % 50)) { if (('' !== $id) && (0 == $i % 50)) {
if ($inComment) { if ($inComment) {
@@ -61,24 +56,25 @@ class Minify_Lines {
array_push($newLines, '', "/* {$id} */", ''); array_push($newLines, '', "/* {$id} */", '');
} }
} }
++$i; ++$i;
$newLines[] = self::_addNote($line, $i, $inComment, $padTo); $newLines[] = self::_addNote($line, $i, $inComment, $padTo);
$inComment = self::_eolInComment($line, $inComment); $inComment = self::_eolInComment($line, $inComment);
} }
$content = implode("\n", $newLines) . "\n"; $content = implode("\n", $newLines) . "\n";
// check for desired URI rewriting // check for desired URI rewriting
if (isset($options['currentDir'])) { if (isset($options['currentDir'])) {
Minify_CSS_UriRewriter::$debugText = ''; Minify_CSS_UriRewriter::$debugText = '';
$content = Minify_CSS_UriRewriter::rewrite( $docRoot = isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'];
$content $symlinks = isset($options['symlinks']) ? $options['symlinks'] : array();
,$options['currentDir']
,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'] $content = Minify_CSS_UriRewriter::rewrite($content, $options['currentDir'], $docRoot, $symlinks);
,isset($options['symlinks']) ? $options['symlinks'] : array()
);
$content = "/* Minify_CSS_UriRewriter::\$debugText\n\n" $content = "/* Minify_CSS_UriRewriter::\$debugText\n\n"
. Minify_CSS_UriRewriter::$debugText . "*/\n" . Minify_CSS_UriRewriter::$debugText . "*/\n"
. $content; . $content;
} }
return $content; return $content;
@@ -89,33 +85,43 @@ class Minify_Lines {
* *
* @param string $line current line of code * @param string $line current line of code
* *
* @param bool $inComment was the parser in a comment at the * @param bool $inComment was the parser in a C-style comment at the
* beginning of the line? * beginning of the previous line?
* *
* @return bool * @return bool
*/ */
private static function _eolInComment($line, $inComment) private static function _eolInComment($line, $inComment)
{ {
// crude way to avoid things like // */
$line = preg_replace('~//.*?(\\*/|/\\*).*~', '', $line);
while (strlen($line)) { while (strlen($line)) {
$search = $inComment if ($inComment) {
? '*/' // only "*/" can end the comment
: '/*'; $index = self::_find($line, '*/');
$pos = strpos($line, $search); if ($index === false) {
if (false === $pos) { return true;
return $inComment;
} else {
if ($pos == 0
|| ($inComment
? substr($line, $pos, 3)
: substr($line, $pos-1, 3)) != '*/*')
{
$inComment = ! $inComment;
} }
$line = substr($line, $pos + 2);
// stop comment and keep walking line
$inComment = false;
@$line = (string)substr($line, $index + 2);
continue;
} }
// look for "//" and "/*"
$single = self::_find($line, '//');
$multi = self::_find($line, '/*');
if ($multi === false) {
return false;
}
if ($single === false || $multi < $single) {
// start comment and keep walking line
$inComment = true;
@$line = (string)substr($line, $multi + 2);
continue;
}
// a single-line comment preceeded it
return false;
} }
return $inComment; return $inComment;
@@ -137,8 +143,67 @@ class Minify_Lines {
*/ */
private static function _addNote($line, $note, $inComment, $padTo) private static function _addNote($line, $note, $inComment, $padTo)
{ {
return $inComment if ($inComment) {
? '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line $line = '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line;
: '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line; } else {
$line = '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line;
}
return rtrim($line);
}
/**
* Find a token trying to avoid false positives
*
* @param string $str String containing the token
* @param string $token Token being checked
* @return bool
*/
private static function _find($str, $token)
{
switch ($token) {
case '//':
$fakes = array(
'://' => 1,
'"//' => 1,
'\'//' => 1,
'".//' => 2,
'\'.//' => 2,
);
break;
case '/*':
$fakes = array(
'"/*' => 1,
'\'/*' => 1,
'"//*' => 2,
'\'//*' => 2,
'".//*' => 3,
'\'.//*' => 3,
'*/*' => 1,
'\\/*' => 1,
);
break;
default:
$fakes = array();
}
$index = strpos($str, $token);
$offset = 0;
while ($index !== false) {
foreach ($fakes as $fake => $skip) {
$check = substr($str, $index - $skip, strlen($fake));
if ($check === $fake) {
// move offset and scan again
$offset += $index + strlen($token);
$index = strpos($str, $token, $offset);
break;
}
}
// legitimate find
return $index;
}
return $index;
} }
} }

View File

@@ -2,7 +2,6 @@
namespace Minify\Logger; namespace Minify\Logger;
use Monolog\Logger;
use Monolog\Handler\AbstractProcessingHandler; use Monolog\Handler\AbstractProcessingHandler;
class LegacyHandler extends AbstractProcessingHandler class LegacyHandler extends AbstractProcessingHandler

View File

@@ -19,7 +19,8 @@
* *
* @package Minify * @package Minify
*/ */
class Minify_Packer { class Minify_Packer
{
public static function minify($code, $options = array()) public static function minify($code, $options = array())
{ {
// @todo: set encoding options based on $options :) // @todo: set encoding options based on $options :)

View File

@@ -0,0 +1,176 @@
<?php
use Leafo\ScssPhp\Compiler;
use Leafo\ScssPhp\Server;
use Leafo\ScssPhp\Version;
/**
* Class for using SCSS files
*
* @link https://github.com/leafo/scssphp/
*/
class Minify_ScssCssSource extends Minify_Source
{
/**
* @var Minify_CacheInterface
*/
private $cache;
/**
* Parsed SCSS cache object
*
* @var array
*/
private $parsed;
/**
* @inheritdoc
*/
public function __construct(array $spec, Minify_CacheInterface $cache)
{
parent::__construct($spec);
$this->cache = $cache;
}
/**
* Get last modified of all parsed files
*
* @return int
*/
public function getLastModified()
{
$cache = $this->getCache();
return $cache['updated'];
}
/**
* Get content
*
* @return string
*/
public function getContent()
{
$cache = $this->getCache();
return $cache['content'];
}
/**
* Make a unique cache id for for this source.
*
* @param string $prefix
*
* @return string
*/
private function getCacheId($prefix = 'minify')
{
$md5 = md5($this->filepath);
return "{$prefix}_scss_{$md5}";
}
/**
* Get SCSS cache object
*
* Runs the compilation if needed
*
* Implements Leafo\ScssPhp\Server logic because we need to get parsed files without parsing actual content
*
* @return array
*/
private function getCache()
{
// cache for single run
// so that getLastModified and getContent in single request do not add additional cache roundtrips (i.e memcache)
if (isset($this->parsed)) {
return $this->parsed;
}
// check from cache first
$cache = null;
$cacheId = $this->getCacheId();
if ($this->cache->isValid($cacheId, 0)) {
if ($cache = $this->cache->fetch($cacheId)) {
$cache = unserialize($cache);
}
}
$input = $cache ? $cache : $this->filepath;
if ($this->cacheIsStale($cache)) {
$cache = $this->compile($this->filepath);
}
if (!is_array($input) || $cache['updated'] > $input['updated']) {
$this->cache->store($cacheId, serialize($cache));
}
return $this->parsed = $cache;
}
/**
* Determine whether .scss file needs to be re-compiled.
*
* @param array $cache Cache object
*
* @return boolean True if compile required.
*/
private function cacheIsStale($cache)
{
if (!$cache) {
return true;
}
$updated = $cache['updated'];
foreach ($cache['files'] as $import => $mtime) {
$filemtime = filemtime($import);
if ($filemtime !== $mtime || $filemtime > $updated) {
return true;
}
}
return false;
}
/**
* Compile .scss file
*
* @param string $filename Input path (.scss)
*
* @see Server::compile()
* @return array meta data result of the compile
*/
private function compile($filename)
{
$start = microtime(true);
$scss = new Compiler();
// set import path directory the input filename resides
// otherwise @import statements will not find the files
// and will treat the @import line as css import
$scss->setImportPaths(dirname($filename));
$css = $scss->compile(file_get_contents($filename), $filename);
$elapsed = round((microtime(true) - $start), 4);
$v = Version::VERSION;
$ts = date('r', $start);
$css = "/* compiled by scssphp $v on $ts (${elapsed}s) */\n\n" . $css;
$imports = $scss->getParsedFiles();
$updated = 0;
foreach ($imports as $mtime) {
$updated = max($updated, $mtime);
}
return array(
'elapsed' => $elapsed, // statistic, can be dropped
'updated' => $updated,
'content' => $css,
'files' => $imports,
);
}
}

View File

@@ -9,7 +9,8 @@
* *
* @package Minify * @package Minify
*/ */
class Minify_ServeConfiguration { class Minify_ServeConfiguration
{
/** /**
* @var Minify_SourceInterface[] * @var Minify_SourceInterface[]

View File

@@ -13,7 +13,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_Source implements Minify_SourceInterface { class Minify_Source implements Minify_SourceInterface
{
/** /**
* @var int time of last modification * @var int time of last modification
@@ -70,14 +71,15 @@ class Minify_Source implements Minify_SourceInterface {
if (isset($spec['filepath'])) { if (isset($spec['filepath'])) {
$ext = pathinfo($spec['filepath'], PATHINFO_EXTENSION); $ext = pathinfo($spec['filepath'], PATHINFO_EXTENSION);
switch ($ext) { switch ($ext) {
case 'js' : $this->contentType = Minify::TYPE_JS; case 'js': $this->contentType = Minify::TYPE_JS;
break; break;
case 'less' : // fallthrough case 'less': // fallthrough
case 'css' : $this->contentType = Minify::TYPE_CSS; case 'scss': // fallthrough
break; case 'css': $this->contentType = Minify::TYPE_CSS;
case 'htm' : // fallthrough break;
case 'html' : $this->contentType = Minify::TYPE_HTML; case 'htm': // fallthrough
break; case 'html': $this->contentType = Minify::TYPE_HTML;
break;
} }
$this->filepath = $spec['filepath']; $this->filepath = $spec['filepath'];
$this->id = $spec['filepath']; $this->id = $spec['filepath'];
@@ -96,9 +98,7 @@ class Minify_Source implements Minify_SourceInterface {
} else { } else {
$this->getContentFunc = $spec['getContentFunc']; $this->getContentFunc = $spec['getContentFunc'];
} }
$this->lastModified = isset($spec['lastModified']) $this->lastModified = isset($spec['lastModified']) ? $spec['lastModified'] : time();
? $spec['lastModified']
: time();
} }
if (isset($spec['contentType'])) { if (isset($spec['contentType'])) {
$this->contentType = $spec['contentType']; $this->contentType = $spec['contentType'];
@@ -203,11 +203,12 @@ class Minify_Source implements Minify_SourceInterface {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setupUriRewrites() { public function setupUriRewrites()
{
if ($this->filepath if ($this->filepath
&& !isset($this->minifyOptions['currentDir']) && !isset($this->minifyOptions['currentDir'])
&& !isset($this->minifyOptions['prependRelativePath']) && !isset($this->minifyOptions['prependRelativePath'])) {
) {
$this->minifyOptions['currentDir'] = dirname($this->filepath); $this->minifyOptions['currentDir'] = dirname($this->filepath);
} }
} }

View File

@@ -1,6 +1,7 @@
<?php <?php
class Minify_Source_Factory { class Minify_Source_Factory
{
/** /**
* @var array * @var array
@@ -67,9 +68,13 @@ class Minify_Source_Factory {
throw new InvalidArgumentException("fileChecker option is not callable"); throw new InvalidArgumentException("fileChecker option is not callable");
} }
$this->setHandler('~\.less$~i', function ($spec) use ($cache) { $this->setHandler('~\.less$~i', function ($spec) use ($cache) {
return new Minify_LessCssSource($spec, $cache); return new Minify_LessCssSource($spec, $cache);
}); });
$this->setHandler('~\.scss~i', function ($spec) use ($cache) {
return new Minify_ScssCssSource($spec, $cache);
});
$this->setHandler('~\.(js|css)$~i', function ($spec) { $this->setHandler('~\.(js|css)$~i', function ($spec) {
return new Minify_Source($spec); return new Minify_Source($spec);

View File

@@ -1,3 +1,5 @@
<?php <?php
class Minify_Source_FactoryException extends Exception {} class Minify_Source_FactoryException extends Exception
{
}

View File

@@ -12,7 +12,8 @@
* *
* @package Minify * @package Minify
*/ */
interface Minify_SourceInterface { interface Minify_SourceInterface
{
/** /**
* Get the minifier * Get the minifier

View File

@@ -7,7 +7,8 @@
/** /**
* @package Minify * @package Minify
*/ */
class Minify_SourceSet { class Minify_SourceSet
{
/** /**
* Get unique string for a set of sources * Get unique string for a set of sources

View File

@@ -29,7 +29,8 @@
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_YUICompressor { class Minify_YUICompressor
{
/** /**
* Filepath of the YUI Compressor jar file. This must be set before * Filepath of the YUI Compressor jar file. This must be set before
@@ -92,6 +93,7 @@ class Minify_YUICompressor {
if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) { if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) {
throw new Exception('Minify_YUICompressor : could not create temp file in "'.self::$tempDir.'".'); throw new Exception('Minify_YUICompressor : could not create temp file in "'.self::$tempDir.'".');
} }
file_put_contents($tmpFile, $content); file_put_contents($tmpFile, $content);
exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code); exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code);
unlink($tmpFile); unlink($tmpFile);
@@ -104,23 +106,20 @@ class Minify_YUICompressor {
private static function _getCmd($userOptions, $type, $tmpFile) private static function _getCmd($userOptions, $type, $tmpFile)
{ {
$o = array_merge( $defaults = array(
array( 'charset' => '',
'charset' => '' 'line-break' => 5000,
,'line-break' => 5000 'type' => $type,
,'type' => $type 'nomunge' => false,
,'nomunge' => false 'preserve-semi' => false,
,'preserve-semi' => false 'disable-optimizations' => false,
,'disable-optimizations' => false 'stack-size' => '',
,'stack-size' => ''
)
,$userOptions
); );
$o = array_merge($defaults, $userOptions);
$cmd = self::$javaExecutable $cmd = self::$javaExecutable
. (!empty($o['stack-size']) . (!empty($o['stack-size']) ? ' -Xss' . $o['stack-size'] : '')
? ' -Xss' . $o['stack-size'] . ' -jar ' . escapeshellarg(self::$jarFile)
: '')
. ' -jar ' . escapeshellarg(self::$jarFile)
. " --type {$type}" . " --type {$type}"
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset']) . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
? " --charset {$o['charset']}" ? " --charset {$o['charset']}"

View File

@@ -18,7 +18,8 @@ use InvalidArgumentException;
* @author Steve Clay <steve@mrclay.org> * @author Steve Clay <steve@mrclay.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
class Cli { class Cli
{
/** /**
* @var array validation errors * @var array validation errors

View File

@@ -43,7 +43,8 @@ use BadMethodCallException;
* @author Steve Clay <steve@mrclay.org> * @author Steve Clay <steve@mrclay.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
class Arg { class Arg
{
/** /**
* @return array * @return array
*/ */

View File

@@ -55,7 +55,7 @@ if ($env->post('method') === 'Minify and serve') {
} }
$tpl = array(); $tpl = array();
$tpl['classes'] = array('Minify_HTML', 'JSMin\\JSMin', 'Minify_CSS'); $tpl['classes'] = array('Minify_HTML', 'JSMin\\JSMin', 'Minify_CSS', 'Minify_Lines');
if (in_array($env->post('method'), $tpl['classes'])) { if (in_array($env->post('method'), $tpl['classes'])) {

View File

@@ -5,7 +5,7 @@
* *
* Change to true to expose this info. * Change to true to expose this info.
*/ */
$enabled = true; $enabled = false;
/////////////////////// ///////////////////////

View File

@@ -24,9 +24,7 @@ class LessSourceTest extends TestCase
touch($includedLess); touch($includedLess);
$mtime1 = filemtime($mainLess); $mtime1 = filemtime($mainLess);
var_dump($mtime1);
$mtime2 = filemtime($includedLess); $mtime2 = filemtime($includedLess);
var_dump($mtime2);
$max = max($mtime1, $mtime2); $max = max($mtime1, $mtime2);

View File

@@ -63,7 +63,7 @@ class MinifyClosureCompilerTest extends TestCase
$src = "function unused() {};"; $src = "function unused() {};";
$minExpected = ''; $minExpected = '';
$options = array( $options = array(
Minify_ClosureCompiler::OPTION_COMPILATION_LEVEL => 'ADVANCED_OPTIMIZATIONS' 'compilation_level' => 'ADVANCED_OPTIMIZATIONS'
); );
$minOutput = Minify_ClosureCompiler::minify($src, $options); $minOutput = Minify_ClosureCompiler::minify($src, $options);
$this->assertSame($minExpected, $minOutput, 'advanced optimizations'); $this->assertSame($minExpected, $minOutput, 'advanced optimizations');
@@ -80,12 +80,36 @@ class MinifyClosureCompilerTest extends TestCase
{ {
$this->assertHasJar(); $this->assertHasJar();
$src = file_get_contents(self::$test_files . '/bug-513.js'); $src = $this->getDataFile('bug-513.js');
$minExpected = 'var a=4;'; $minExpected = 'var a=4;';
$minOutput = Minify_ClosureCompiler::minify($src); $minOutput = Minify_ClosureCompiler::minify($src);
$this->assertSame($minExpected, $minOutput, 'advanced optimizations'); $this->assertSame($minExpected, $minOutput, 'advanced optimizations');
} }
/**
* Test that language_in parameter has effect.
*/
public function testLanguageOptions()
{
$this->assertHasJar();
$src = $this->getDataFile('js/jscomp.polyfill.js');
$exp = $this->getDataFile('js/jscomp.polyfill.min.js');
$options = array(
'language_in' => 'ECMASCRIPT3',
);
$res = Minify_ClosureCompiler::minify($src, $options);
$this->assertSame($exp, $res);
$options = array(
'language_in' => 'ECMASCRIPT6',
);
$exp = $this->getDataFile('js/jscomp.polyfilled.min.js');
$res = Minify_ClosureCompiler::minify($src, $options);
$this->assertSame($exp, $res);
}
protected function assertHasJar() protected function assertHasJar()
{ {
$this->assertNotEmpty(Minify_ClosureCompiler::$jarFile); $this->assertNotEmpty(Minify_ClosureCompiler::$jarFile);

View File

@@ -4,8 +4,6 @@ class MinifyLinesTest extends TestCase
{ {
public function test_lines() public function test_lines()
{ {
$exp = file_get_contents(self::$test_files . "/minify/lines_output.js");
$env = new Minify_Env(array( $env = new Minify_Env(array(
'server' => array( 'server' => array(
'DOCUMENT_ROOT' => dirname(__DIR__), 'DOCUMENT_ROOT' => dirname(__DIR__),
@@ -15,15 +13,27 @@ class MinifyLinesTest extends TestCase
$controller = new Minify_Controller_Files($env, $sourceFactory); $controller = new Minify_Controller_Files($env, $sourceFactory);
$minify = new Minify(new Minify_Cache_Null()); $minify = new Minify(new Minify_Cache_Null());
$ret = $minify->serve($controller, array( $files = glob(self::$test_files . "/lines/*.in.js");
'debug' => true
,'quiet' => true
,'encodeOutput' => false
,'files' => array(
self::$test_files . "/js/before.js"
)
));
$this->assertEquals($exp, $ret['content']); // uncomment to debug one
//$files = array(self::$test_files . "/lines/basic.in.js");
foreach ($files as $file) {
$ret = $minify->serve($controller, array(
'debug' => true,
'quiet' => true,
'encodeOutput' => false,
'files' => array($file),
));
$outFile = str_replace('.in.js', '.out.js', $file);
$exp = file_get_contents($outFile);
// uncomment to set up expected output
//file_put_contents($outFile, $ret['content']);
$this->assertEquals($exp, $ret['content'], "Did not match: " . basename($outFile));
}
} }
} }

View File

@@ -20,7 +20,7 @@ class MinifyNailgunClosureCompilerTest extends TestCase
/** /**
* Test minimisation with the minimum necessary settings * Test minimisation with the minimum necessary settings
*/ */
public function test2() public function test1()
{ {
$this->assertHasJar(); $this->assertHasJar();
$src = " $src = "
@@ -40,8 +40,10 @@ class MinifyNailgunClosureCompilerTest extends TestCase
protected function assertHasJar() protected function assertHasJar()
{ {
$this->assertNotEmpty(Minify_ClosureCompiler::$jarFile); $this->assertNotEmpty(Minify_ClosureCompiler::$jarFile);
$this->assertNotEmpty(Minify_NailgunClosureCompiler::$ngJarFile);
try { try {
$this->assertFileExists(Minify_ClosureCompiler::$jarFile, "Have closure compiler compiler.jar"); $this->assertFileExists(Minify_ClosureCompiler::$jarFile, "Have closure compiler compiler.jar");
$this->assertFileExists(Minify_NailgunClosureCompiler::$ngJarFile, "Have nailgun.jar");
} catch (Exception $e) { } catch (Exception $e) {
$this->markTestSkipped($e->getMessage()); $this->markTestSkipped($e->getMessage());
} }

42
tests/ScssSourceTest.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
class ScssSourceTest extends TestCase
{
public function setUp()
{
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
$this->markTestSkipped('scssphp is not compatible with this PHP version.');
}
$this->realDocRoot = $_SERVER['DOCUMENT_ROOT'];
$_SERVER['DOCUMENT_ROOT'] = self::$document_root;
}
/**
* @link https://github.com/mrclay/minify/issues/500
*/
public function testTimestamp()
{
$baseDir = self::$test_files;
$mainLess = "$baseDir/main.scss";
$includedLess = "$baseDir/_included.scss";
// touch timestamp with 1s difference
touch($mainLess);
sleep(1);
touch($includedLess);
$mtime1 = filemtime($mainLess);
$mtime2 = filemtime($includedLess);
$max = max($mtime1, $mtime2);
$options = array(
'groupsConfigFile' => "$baseDir/htmlHelper_groupsConfig.php",
);
$res = Minify_HTML_Helper::getUri('scss', $options);
$this->assertEquals("/min/g=scss&amp;{$max}", $res);
}
}

View File

@@ -47,4 +47,23 @@ class TestCase extends PHPUnit_Framework_TestCase
$this->assertSame($data, $displayed, "$id display"); $this->assertSame($data, $displayed, "$id display");
$this->assertEquals($data, $cache->fetch($id), "$id fetch"); $this->assertEquals($data, $cache->fetch($id), "$id fetch");
} }
/**
* Read data file, assert that it exists and is not empty.
* As a side effect calls trim() to fight against different Editors that insert or strip final newline.
*
* @param string $filename
* @return string
*/
protected function getDataFile($filename)
{
$path = self::$test_files . '/' . $filename;
$this->assertFileExists($path);
$contents = file_get_contents($path);
$this->assertNotEmpty($contents);
$contents = trim($contents);
$this->assertNotEmpty($contents);
return $contents;
}
} }

View File

@@ -0,0 +1,8 @@
/* lesstest2.scss */
a.included {
color: $primary-color;
font-size: 13px;
text-decoration: none;
}

View File

@@ -8,6 +8,7 @@
@import url("/css/foo.css"); /* abs, should not alter */ @import url("/css/foo.css"); /* abs, should not alter */
@import url(/css2/foo.css); /* abs, should not alter */ @import url(/css2/foo.css); /* abs, should not alter */
@import url(foo:bar); /* scheme, should not alter */ @import url(foo:bar); /* scheme, should not alter */
@import url(); /* empty, should not alter */
foo {clip-path:url(#c1)} /* inline clip path, should not alter */ foo {clip-path:url(#c1)} /* inline clip path, should not alter */
foo {clip-path:url(/_test_files/css_uriRewriter/foo.svg#c1)} foo {clip-path:url(/_test_files/css_uriRewriter/foo.svg#c1)}
foo {mask: url(#c1)} /* should not alter */ foo {mask: url(#c1)} /* should not alter */
@@ -17,5 +18,6 @@ foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter
foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */ foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */
foo {background:url(foo:bar);} /* scheme, should not alter */ foo {background:url(foo:bar);} /* scheme, should not alter */
foo {background:url("/_test_files/css_uriRewriter/foo bar.jpg");} foo {background:url("/_test_files/css_uriRewriter/foo bar.jpg");}
foo {background:url("");} /* empty, should not alter */
@import url('/_test_files/css_uriRewriter/foo bar.css'); @import url('/_test_files/css_uriRewriter/foo bar.css');
@import "/_test_files/css_uriRewriter/foo bar.css"; @import "/_test_files/css_uriRewriter/foo bar.css";

View File

@@ -8,6 +8,7 @@
@import url("/css/foo.css"); /* abs, should not alter */ @import url("/css/foo.css"); /* abs, should not alter */
@import url(/css2/foo.css); /* abs, should not alter */ @import url(/css2/foo.css); /* abs, should not alter */
@import url(foo:bar); /* scheme, should not alter */ @import url(foo:bar); /* scheme, should not alter */
@import url(); /* empty, should not alter */
foo {clip-path:url(#c1)} /* inline clip path, should not alter */ foo {clip-path:url(#c1)} /* inline clip path, should not alter */
foo {clip-path:url(http://cnd.com/A/B/foo.svg#c1)} foo {clip-path:url(http://cnd.com/A/B/foo.svg#c1)}
foo {mask: url(#c1)} /* should not alter */ foo {mask: url(#c1)} /* should not alter */
@@ -17,5 +18,6 @@ foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter
foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */ foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */
foo {background:url(foo:bar);} /* scheme, should not alter */ foo {background:url(foo:bar);} /* scheme, should not alter */
foo {background:url("http://cnd.com/A/B/foo bar.jpg");} foo {background:url("http://cnd.com/A/B/foo bar.jpg");}
foo {background:url("");} /* empty, should not alter */
@import url('http://cnd.com/A/B/foo bar.css'); @import url('http://cnd.com/A/B/foo bar.css');
@import "http://cnd.com/A/B/foo bar.css"; @import "http://cnd.com/A/B/foo bar.css";

View File

@@ -8,6 +8,7 @@
@import url("/css/foo.css"); /* abs, should not alter */ @import url("/css/foo.css"); /* abs, should not alter */
@import url(/css2/foo.css); /* abs, should not alter */ @import url(/css2/foo.css); /* abs, should not alter */
@import url(foo:bar); /* scheme, should not alter */ @import url(foo:bar); /* scheme, should not alter */
@import url(); /* empty, should not alter */
foo {clip-path:url(#c1)} /* inline clip path, should not alter */ foo {clip-path:url(#c1)} /* inline clip path, should not alter */
foo {clip-path:url(//cnd.com/A/B/foo.svg#c1)} foo {clip-path:url(//cnd.com/A/B/foo.svg#c1)}
foo {mask: url(#c1)} /* should not alter */ foo {mask: url(#c1)} /* should not alter */
@@ -17,5 +18,6 @@ foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter
foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */ foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */
foo {background:url(foo:bar);} /* scheme, should not alter */ foo {background:url(foo:bar);} /* scheme, should not alter */
foo {background:url("//cnd.com/A/B/foo bar.jpg");} foo {background:url("//cnd.com/A/B/foo bar.jpg");}
foo {background:url("");} /* empty, should not alter */
@import url('//cnd.com/A/B/foo bar.css'); @import url('//cnd.com/A/B/foo bar.css');
@import "//cnd.com/A/B/foo bar.css"; @import "//cnd.com/A/B/foo bar.css";

View File

@@ -8,6 +8,7 @@
@import url("/css/foo.css"); /* abs, should not alter */ @import url("/css/foo.css"); /* abs, should not alter */
@import url(/css2/foo.css); /* abs, should not alter */ @import url(/css2/foo.css); /* abs, should not alter */
@import url(foo:bar); /* scheme, should not alter */ @import url(foo:bar); /* scheme, should not alter */
@import url(); /* empty, should not alter */
foo {clip-path:url(#c1)} /* inline clip path, should not alter */ foo {clip-path:url(#c1)} /* inline clip path, should not alter */
foo {clip-path:url(foo.svg#c1)} foo {clip-path:url(foo.svg#c1)}
foo {mask: url( #c1 )} /* should not alter */ foo {mask: url( #c1 )} /* should not alter */
@@ -17,5 +18,6 @@ foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter
foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */ foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */
foo {background:url(foo:bar);} /* scheme, should not alter */ foo {background:url(foo:bar);} /* scheme, should not alter */
foo {background:url("foo bar.jpg");} foo {background:url("foo bar.jpg");}
foo {background:url("");} /* empty, should not alter */
@import url('foo bar.css'); @import url('foo bar.css');
@import "foo bar.css"; @import "foo bar.css";

View File

@@ -9,4 +9,8 @@ return array(
'less' => array( 'less' => array(
'//_test_files/main.less', '//_test_files/main.less',
), ),
'scss' => array(
'//_test_files/main.scss',
),
); );

View File

@@ -0,0 +1,7 @@
(function() {
/**
* @type {string}
*/
var $array = jQuery.find('#div');
print($array.find('a'));
})();

View File

@@ -0,0 +1 @@
(function(){var a=jQuery.find("#div");print(a.find("a"))})();

View File

@@ -0,0 +1,3 @@
var $jscomp={scope:{},findInternal:function(a,c,b){a instanceof String&&(a=String(a));for(var d=a.length,e=0;e<d;e++){var f=a[e];if(c.call(b,f,e,a))return{i:e,v:f}}return{i:-1,v:void 0}}};$jscomp.defineProperty="function"==typeof Object.defineProperties?Object.defineProperty:function(a,c,b){if(b.get||b.set)throw new TypeError("ES3 does not support getters and setters.");a!=Array.prototype&&a!=Object.prototype&&(a[c]=b.value)};
$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};$jscomp.global=$jscomp.getGlobal(this);$jscomp.polyfill=function(a,c,b,d){if(c){b=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in b||(b[e]={});b=b[e]}a=a[a.length-1];d=b[a];c=c(d);c!=d&&null!=c&&$jscomp.defineProperty(b,a,{configurable:!0,writable:!0,value:c})}};
$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,b){return $jscomp.findInternal(this,a,b).v}},"es6-impl","es3");(function(){var a=jQuery.find("#div");print(a.find("a"))})();

View File

@@ -0,0 +1,66 @@
/*! is.js
(c) 2001 Douglas Crockford
2001 June 3
*/
// is
// The -is- object is used to identify the browser. Every browser edition
// identifies itself, but there is no standard way of doing it, and some of
// the identification is deceptive. This is because the authors of web
// browsers are liars. For example, Microsoft's IE browsers claim to be
// Mozilla 4. Netscape 6 claims to be version 5.
var is = {
ie: navigator.appName == 'Microsoft Internet Explorer',
java: navigator.javaEnabled(),
ns: navigator.appName == 'Netscape',
ua: navigator.userAgent.toLowerCase(),
version: parseFloat(navigator.appVersion.substr(21)) ||
parseFloat(navigator.appVersion),
win: navigator.platform == 'Win32'
}
/*!*
* preserve this comment, too
*/
is.mac = is.ua.indexOf('mac') >= 0;
if (is.ua.indexOf('opera') >= 0) {
is.ie = is.ns = false;
is.opera = true;
}
if (is.ua.indexOf('gecko') >= 0) {
is.ie = is.ns = false;
is.gecko = true;
}
/*@cc_on
/*@if (@_win32)
if (is.ie && is.win)
document.write("PASS: IE/win honored conditional comment.<br>");
@else @*/
if (is.ie && is.win)
document.write("FAIL: IE/win did not honor multi-line conditional comment.<br>");
else
document.write("PASS: Non-IE/win browser ignores multi-line conditional comment.<br>");
/*@end
@*/
var recognizesCondComm = true;
//@cc_on/*
recognizesCondComm = false;
//@cc_on*/
if ((is.ie && is.win) == recognizesCondComm)
document.write("PASS: IE/win honored single-line conditional comment.<br>");
else
document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.<br>");
// hello
//@cc_on/*
// world
//@cc_on*/
//@cc_on/*
'hello';
/*!* preserved */
/*!* preserved */

View File

@@ -1,20 +1,20 @@
/* before.js */ /* basic.in.js */
/* 1 */ /*! is.js /* 1 */ /*! is.js
/* 2 *| /* 2 *|
/* 3 *| (c) 2001 Douglas Crockford /* 3 *| (c) 2001 Douglas Crockford
/* 4 *| 2001 June 3 /* 4 *| 2001 June 3
/* 5 *| */ /* 5 *| */
/* 6 */ /* 6 */
/* 7 */ // is /* 7 */ // is
/* 8 */ /* 8 */
/* 9 */ // The -is- object is used to identify the browser. Every browser edition /* 9 */ // The -is- object is used to identify the browser. Every browser edition
/* 10 */ // identifies itself, but there is no standard way of doing it, and some of /* 10 */ // identifies itself, but there is no standard way of doing it, and some of
/* 11 */ // the identification is deceptive. This is because the authors of web /* 11 */ // the identification is deceptive. This is because the authors of web
/* 12 */ // browsers are liars. For example, Microsoft's IE browsers claim to be /* 12 */ // browsers are liars. For example, Microsoft's IE browsers claim to be
/* 13 */ // Mozilla 4. Netscape 6 claims to be version 5. /* 13 */ // Mozilla 4. Netscape 6 claims to be version 5.
/* 14 */ /* 14 */
/* 15 */ var is = { /* 15 */ var is = {
/* 16 */ ie: navigator.appName == 'Microsoft Internet Explorer', /* 16 */ ie: navigator.appName == 'Microsoft Internet Explorer',
/* 17 */ java: navigator.javaEnabled(), /* 17 */ java: navigator.javaEnabled(),
@@ -36,7 +36,7 @@
/* 33 */ is.ie = is.ns = false; /* 33 */ is.ie = is.ns = false;
/* 34 */ is.gecko = true; /* 34 */ is.gecko = true;
/* 35 */ } /* 35 */ }
/* 36 */ /* 36 */
/* 37 */ /*@cc_on /* 37 */ /*@cc_on
/* 38 *| /*@if (@_win32) /* 38 *| /*@if (@_win32)
/* 39 *| if (is.ie && is.win) /* 39 *| if (is.ie && is.win)
@@ -44,24 +44,24 @@
/* 41 *| @else @*/ /* 41 *| @else @*/
/* 42 */ if (is.ie && is.win) /* 42 */ if (is.ie && is.win)
/* 43 */ document.write("FAIL: IE/win did not honor multi-line conditional comment.<br>"); /* 43 */ document.write("FAIL: IE/win did not honor multi-line conditional comment.<br>");
/* 44 */ else /* 44 */ else
/* 45 */ document.write("PASS: Non-IE/win browser ignores multi-line conditional comment.<br>"); /* 45 */ document.write("PASS: Non-IE/win browser ignores multi-line conditional comment.<br>");
/* 46 */ /*@end /* 46 */ /*@end
/* 47 *| @*/ /* 47 *| @*/
/* 48 */ /* 48 */
/* 49 */ var recognizesCondComm = true; /* 49 */ var recognizesCondComm = true;
/* 50 */ //@cc_on/* /* 50 */ //@cc_on/*
/* before.js */ /* basic.in.js */
/* 51 */ recognizesCondComm = false; /* 51 */ recognizesCondComm = false;
/* 52 */ //@cc_on*/ /* 52 */ //@cc_on*/
/* 53 */ /* 53 */
/* 54 */ if ((is.ie && is.win) == recognizesCondComm) /* 54 */ if ((is.ie && is.win) == recognizesCondComm)
/* 55 */ document.write("PASS: IE/win honored single-line conditional comment.<br>"); /* 55 */ document.write("PASS: IE/win honored single-line conditional comment.<br>");
/* 56 */ else /* 56 */ else
/* 57 */ document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.<br>"); /* 57 */ document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.<br>");
/* 58 */ /* 58 */
/* 59 */ // hello /* 59 */ // hello
/* 60 */ //@cc_on/* /* 60 */ //@cc_on/*
/* 61 */ // world /* 61 */ // world

View File

@@ -1,10 +1,14 @@
// sections from Prototype 1.6.1 // sections from Prototype 1.6.1
var xpath = ".//*[local-name()='ul' or local-name()='UL']" + var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
"//*[local-name()='li' or local-name()='LI']"; "//*[local-name()='li' or local-name()='LI']";
this.matcher = ['.//*']; this.matcher = ['.//*'];
xpath = { xpath = {
descendant: "//*", descendant: "//*",
child: "/*", child: "/*",
f: 0 f: 0
}; };
document._getElementsByXPath('.//*' + cond, element); document._getElementsByXPath('.//*' + cond, element);
// from angular 1.4.8
var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;

View File

@@ -0,0 +1,18 @@
/* misc.in.js */
/* 1 */ // sections from Prototype 1.6.1
/* 2 */ var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
/* 3 */ "//*[local-name()='li' or local-name()='LI']";
/* 4 */ this.matcher = ['.//*'];
/* 5 */ xpath = {
/* 6 */ descendant: "//*",
/* 7 */ child: "/*",
/* 8 */ f: 0
/* 9 */ };
/* 10 */ document._getElementsByXPath('.//*' + cond, element);
/* 11 */
/* 12 */ // from angular 1.4.8
/* 13 */ var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
/* 14 */
/* 15 */

View File

@@ -0,0 +1,13 @@
foo; /* http://example.com */
bar;
foo; /*
http://example.com */
bar;
foo = "http://example.com"; /* hello */
bar;
foo = "http://example.com"; /*
hello */
bar;

View File

@@ -0,0 +1,17 @@
/* url.in.js */
/* 1 */ foo; /* http://example.com */
/* 2 */ bar;
/* 3 */
/* 4 */ foo; /*
/* 5 *| http://example.com */
/* 6 */ bar;
/* 7 */
/* 8 */ foo = "http://example.com"; /* hello */
/* 9 */ bar;
/* 10 */
/* 11 */ foo = "http://example.com"; /*
/* 12 *| hello */
/* 13 */ bar;
/* 14 */

View File

@@ -0,0 +1,29 @@
/*! preserving comment */
// Variable
$primary-color: hotpink;
// Mixin
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
}
.my-element {
color: $primary-color;
width: 100%;
overflow: hidden;
}
.my-other-element {
@include border-radius(6px);
}
/* import include -> */
@import "_included";
/* <- import included */
/*
a normal comment.
*/