1
0
mirror of https://github.com/vichan-devel/vichan.git synced 2024-11-25 07:50:23 +01:00

Updated minify, jQuery, MixItUp and Tooltipster

This commit is contained in:
Jano Slota 2014-05-05 22:55:36 +02:00 committed by czaks
parent 4c31dec50e
commit a2cd7de2dc
22 changed files with 467 additions and 330 deletions

View File

@ -69,6 +69,7 @@ class JSMin {
protected $lookAhead = null; protected $lookAhead = null;
protected $output = ''; protected $output = '';
protected $lastByteOut = ''; protected $lastByteOut = '';
protected $keptComment = '';
/** /**
* Minify Javascript. * Minify Javascript.
@ -117,7 +118,7 @@ class JSMin {
$command = self::ACTION_KEEP_A; // default $command = self::ACTION_KEEP_A; // default
if ($this->a === ' ') { if ($this->a === ' ') {
if (($this->lastByteOut === '+' || $this->lastByteOut === '-') if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
&& ($this->b === $this->lastByteOut)) { && ($this->b === $this->lastByteOut)) {
// Don't delete this space. If we do, the addition/subtraction // Don't delete this space. If we do, the addition/subtraction
// could be parsed as a post-increment // could be parsed as a post-increment
} elseif (! $this->isAlphaNum($this->b)) { } elseif (! $this->isAlphaNum($this->b)) {
@ -126,10 +127,11 @@ class JSMin {
} elseif ($this->a === "\n") { } elseif ($this->a === "\n") {
if ($this->b === ' ') { if ($this->b === ' ') {
$command = self::ACTION_DELETE_A_B; $command = self::ACTION_DELETE_A_B;
// in case of mbstring.func_overload & 2, must check for null b,
// otherwise mb_strpos will give WARNING // in case of mbstring.func_overload & 2, must check for null b,
// otherwise mb_strpos will give WARNING
} elseif ($this->b === null } elseif ($this->b === null
|| (false === strpos('{[(+-', $this->b) || (false === strpos('{[(+-!~', $this->b)
&& ! $this->isAlphaNum($this->b))) { && ! $this->isAlphaNum($this->b))) {
$command = self::ACTION_DELETE_A; $command = self::ACTION_DELETE_A;
} }
@ -160,6 +162,7 @@ class JSMin {
*/ */
protected function action($command) protected function action($command)
{ {
// make sure we don't compress "a + ++b" to "a+++b", etc.
if ($command === self::ACTION_DELETE_A_B if ($command === self::ACTION_DELETE_A_B
&& $this->b === ' ' && $this->b === ' '
&& ($this->a === '+' || $this->a === '-')) { && ($this->a === '+' || $this->a === '-')) {
@ -170,28 +173,36 @@ class JSMin {
$command = self::ACTION_KEEP_A; $command = self::ACTION_KEEP_A;
} }
} }
switch ($command) { switch ($command) {
case self::ACTION_KEEP_A: case self::ACTION_KEEP_A: // 1
$this->output .= $this->a; $this->output .= $this->a;
if ($this->keptComment) {
$this->output = rtrim($this->output, "\n");
$this->output .= $this->keptComment;
$this->keptComment = '';
}
$this->lastByteOut = $this->a; $this->lastByteOut = $this->a;
// fallthrough // fallthrough intentional
case self::ACTION_DELETE_A: case self::ACTION_DELETE_A: // 2
$this->a = $this->b; $this->a = $this->b;
if ($this->a === "'" || $this->a === '"') { // string literal if ($this->a === "'" || $this->a === '"') { // string literal
$str = $this->a; // in case needed for exception $str = $this->a; // in case needed for exception
while (true) { for(;;) {
$this->output .= $this->a; $this->output .= $this->a;
$this->lastByteOut = $this->a; $this->lastByteOut = $this->a;
$this->a = $this->get(); $this->a = $this->get();
if ($this->a === $this->b) { // end quote if ($this->a === $this->b) { // end quote
break; break;
} }
if (ord($this->a) <= self::ORD_LF) { if ($this->isEOF($this->a)) {
$byte = $this->inputIndex - 1;
throw new JSMin_UnterminatedStringException( throw new JSMin_UnterminatedStringException(
"JSMin: Unterminated String at byte " "JSMin: Unterminated String at byte {$byte}: {$str}");
. $this->inputIndex . ": {$str}");
} }
$str .= $this->a; $str .= $this->a;
if ($this->a === '\\') { if ($this->a === '\\') {
@ -203,25 +214,47 @@ class JSMin {
} }
} }
} }
// fallthrough
case self::ACTION_DELETE_A_B: // fallthrough intentional
case self::ACTION_DELETE_A_B: // 3
$this->b = $this->next(); $this->b = $this->next();
if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal if ($this->b === '/' && $this->isRegexpLiteral()) {
$this->output .= $this->a . $this->b; $this->output .= $this->a . $this->b;
$pattern = '/'; // in case needed for exception $pattern = '/'; // keep entire pattern in case we need to report it in the exception
while (true) { for(;;) {
$this->a = $this->get(); $this->a = $this->get();
$pattern .= $this->a; $pattern .= $this->a;
if ($this->a === '[') {
for(;;) {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === ']') {
break;
}
if ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
}
if ($this->isEOF($this->a)) {
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated set in RegExp at byte "
. $this->inputIndex .": {$pattern}");
}
}
}
if ($this->a === '/') { // end pattern if ($this->a === '/') { // end pattern
break; // while (true) break; // while (true)
} elseif ($this->a === '\\') { } elseif ($this->a === '\\') {
$this->output .= $this->a; $this->output .= $this->a;
$this->a = $this->get(); $this->a = $this->get();
$pattern .= $this->a; $pattern .= $this->a;
} elseif (ord($this->a) <= self::ORD_LF) { } elseif ($this->isEOF($this->a)) {
$byte = $this->inputIndex - 1;
throw new JSMin_UnterminatedRegExpException( throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated RegExp at byte " "JSMin: Unterminated RegExp at byte {$byte}: {$pattern}");
. $this->inputIndex .": {$pattern}");
} }
$this->output .= $this->a; $this->output .= $this->a;
$this->lastByteOut = $this->a; $this->lastByteOut = $this->a;
@ -237,31 +270,43 @@ class JSMin {
*/ */
protected function isRegexpLiteral() protected function isRegexpLiteral()
{ {
if (false !== strpos("\n{;(,=:[!&|?", $this->a)) { // we aren't dividing if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
// we obviously aren't dividing
return true; return true;
} }
if (' ' === $this->a) {
$length = strlen($this->output); // we have to check for a preceding keyword, and we don't need to pattern
if ($length < 2) { // weird edge case // match over the whole output.
return true; $recentOutput = substr($this->output, -10);
// check if return/typeof directly precede a pattern without a space
foreach (array('return', 'typeof') as $keyword) {
if ($this->a !== substr($keyword, -1)) {
// certainly wasn't keyword
continue;
} }
// you can't divide a keyword if (preg_match("~(^|[\\s\\S])" . substr($keyword, 0, -1) . "$~", $recentOutput, $m)) {
if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) { if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
if ($this->output === $m[0]) { // odd but could happen
return true;
}
// make sure it's a keyword, not end of an identifier
$charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
if (! $this->isAlphaNum($charBeforeKeyword)) {
return true; return true;
} }
} }
} }
// check all keywords
if ($this->a === ' ' || $this->a === "\n") {
if (preg_match('~(^|[\\s\\S])(?:case|else|in|return|typeof)$~', $recentOutput, $m)) {
if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
return true;
}
}
}
return false; return false;
} }
/** /**
* Get next char. Convert ctrl char to space. * Return the next character from stdin. Watch out for lookahead. If the character is a control character,
* translate it to a space or linefeed.
* *
* @return string * @return string
*/ */
@ -270,24 +315,36 @@ class JSMin {
$c = $this->lookAhead; $c = $this->lookAhead;
$this->lookAhead = null; $this->lookAhead = null;
if ($c === null) { if ($c === null) {
// getc(stdin)
if ($this->inputIndex < $this->inputLength) { if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex]; $c = $this->input[$this->inputIndex];
$this->inputIndex += 1; $this->inputIndex += 1;
} else { } else {
return null; $c = null;
} }
} }
if ($c === "\r" || $c === "\n") { if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
return $c;
}
if ($c === "\r") {
return "\n"; return "\n";
} }
if (ord($c) < self::ORD_SPACE) { // control char return ' ';
return ' ';
}
return $c;
} }
/** /**
* Get next char. If is ctrl character, translate to a space or newline. * Does $a indicate end of input?
*
* @param string $a
* @return bool
*/
protected function isEOF($a)
{
return ord($a) <= self::ORD_LF;
}
/**
* Get next char (without getting it). If is ctrl character, translate to a space or newline.
* *
* @return string * @return string
*/ */
@ -298,7 +355,7 @@ class JSMin {
} }
/** /**
* Is $c a letter, digit, underscore, dollar sign, escape, or non-ASCII? * Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
* *
* @param string $c * @param string $c
* *
@ -306,77 +363,84 @@ class JSMin {
*/ */
protected function isAlphaNum($c) protected function isAlphaNum($c)
{ {
return (preg_match('/^[0-9a-zA-Z_\\$\\\\]$/', $c) || ord($c) > 126); return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
} }
/** /**
* @return string * Consume a single line comment from input (possibly retaining it)
*/ */
protected function singleLineComment() protected function consumeSingleLineComment()
{ {
$comment = ''; $comment = '';
while (true) { while (true) {
$get = $this->get(); $get = $this->get();
$comment .= $get; $comment .= $get;
if (ord($get) <= self::ORD_LF) { // EOL reached if (ord($get) <= self::ORD_LF) { // end of line reached
// if IE conditional comment // if IE conditional comment
if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) { if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
return "/{$comment}"; $this->keptComment .= "/{$comment}";
} }
return $get; return;
} }
} }
} }
/** /**
* @return string * Consume a multiple line comment from input (possibly retaining it)
*
* @throws JSMin_UnterminatedCommentException * @throws JSMin_UnterminatedCommentException
*/ */
protected function multipleLineComment() protected function consumeMultipleLineComment()
{ {
$this->get(); $this->get();
$comment = ''; $comment = '';
while (true) { for(;;) {
$get = $this->get(); $get = $this->get();
if ($get === '*') { if ($get === '*') {
if ($this->peek() === '/') { // end of comment reached if ($this->peek() === '/') { // end of comment reached
$this->get(); $this->get();
// if comment preserved by YUI Compressor
if (0 === strpos($comment, '!')) { if (0 === strpos($comment, '!')) {
return "\n/*!" . substr($comment, 1) . "*/\n"; // preserved by YUI Compressor
if (!$this->keptComment) {
// don't prepend a newline if two comments right after one another
$this->keptComment = "\n";
}
$this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
} else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
// IE conditional
$this->keptComment .= "/*{$comment}*/";
} }
// if IE conditional comment return;
if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
return "/*{$comment}*/";
}
return ' ';
} }
} elseif ($get === null) { } elseif ($get === null) {
throw new JSMin_UnterminatedCommentException( throw new JSMin_UnterminatedCommentException(
"JSMin: Unterminated comment at byte " "JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
. $this->inputIndex . ": /*{$comment}");
} }
$comment .= $get; $comment .= $get;
} }
} }
/** /**
* Get the next character, skipping over comments. * Get the next character, skipping over comments. Some comments may be preserved.
* Some comments may be preserved.
* *
* @return string * @return string
*/ */
protected function next() protected function next()
{ {
$get = $this->get(); $get = $this->get();
if ($get !== '/') { if ($get === '/') {
return $get; switch ($this->peek()) {
} case '/':
switch ($this->peek()) { $this->consumeSingleLineComment();
case '/': return $this->singleLineComment(); $get = "\n";
case '*': return $this->multipleLineComment(); break;
default: return $get; case '*':
$this->consumeMultipleLineComment();
$get = ' ';
break;
}
} }
return $get;
} }
} }

View File

@ -4,11 +4,6 @@
* @package Minify * @package Minify
*/ */
/**
* Minify_Source
*/
require_once 'Minify/Source.php';
/** /**
* Minify - Combines, minifies, and caches JavaScript and CSS files on demand. * Minify - Combines, minifies, and caches JavaScript and CSS files on demand.
* *
@ -29,7 +24,7 @@ require_once 'Minify/Source.php';
*/ */
class Minify { class Minify {
const VERSION = '2.1.5'; const VERSION = '2.2.0';
const TYPE_CSS = 'text/css'; const TYPE_CSS = 'text/css';
const TYPE_HTML = 'text/html'; const TYPE_HTML = 'text/html';
// there is some debate over the ideal JS Content-Type, but this is the // there is some debate over the ideal JS Content-Type, but this is the
@ -85,7 +80,6 @@ class Minify {
public static function setCache($cache = '', $fileLocking = true) public static function setCache($cache = '', $fileLocking = true)
{ {
if (is_string($cache)) { if (is_string($cache)) {
require_once 'Minify/Cache/File.php';
self::$_cache = new Minify_Cache_File($cache, $fileLocking); self::$_cache = new Minify_Cache_File($cache, $fileLocking);
} else { } else {
self::$_cache = $cache; self::$_cache = $cache;
@ -161,9 +155,11 @@ class Minify {
* *
* @param array $options controller/serve options * @param array $options controller/serve options
* *
* @return mixed null, or, if the 'quiet' option is set to true, an array * @return null|array if the 'quiet' option is set to true, an array
* with keys "success" (bool), "statusCode" (int), "content" (string), and * with keys "success" (bool), "statusCode" (int), "content" (string), and
* "headers" (array). * "headers" (array).
*
* @throws Exception
*/ */
public static function serve($controller, $options = array()) public static function serve($controller, $options = array())
{ {
@ -174,10 +170,6 @@ class Minify {
if (is_string($controller)) { if (is_string($controller)) {
// make $controller into object // make $controller into object
$class = 'Minify_Controller_' . $controller; $class = 'Minify_Controller_' . $controller;
if (! class_exists($class, false)) {
require_once "Minify/Controller/"
. str_replace('_', '/', $controller) . ".php";
}
$controller = new $class(); $controller = new $class();
/* @var Minify_Controller_Base $controller */ /* @var Minify_Controller_Base $controller */
} }
@ -219,7 +211,6 @@ class Minify {
$contentEncoding = self::$_options['encodeMethod']; $contentEncoding = self::$_options['encodeMethod'];
} else { } else {
// sniff request header // sniff request header
require_once 'HTTP/Encoder.php';
// 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.
@ -231,7 +222,6 @@ class Minify {
} }
// check client cache // check client cache
require_once 'HTTP/ConditionalGet.php';
$cgOptions = array( $cgOptions = array(
'lastModifiedTime' => self::$_options['lastModifiedTime'] 'lastModifiedTime' => self::$_options['lastModifiedTime']
,'isPublic' => self::$_options['isPublic'] ,'isPublic' => self::$_options['isPublic']
@ -300,7 +290,7 @@ class Minify {
throw $e; throw $e;
} }
self::$_cache->store($cacheId, $content); self::$_cache->store($cacheId, $content);
if (function_exists('gzencode')) { if (function_exists('gzencode') && self::$_options['encodeMethod']) {
self::$_cache->store($cacheId . '.gz', gzencode($content, self::$_options['encodeLevel'])); self::$_cache->store($cacheId . '.gz', gzencode($content, self::$_options['encodeLevel']));
} }
} }
@ -451,7 +441,7 @@ class Minify {
/** /**
* Set up sources to use Minify_Lines * Set up sources to use Minify_Lines
* *
* @param array $sources Minify_Source instances * @param Minify_Source[] $sources Minify_Source instances
*/ */
protected static function _setupDebug($sources) protected static function _setupDebug($sources)
{ {
@ -468,6 +458,8 @@ class Minify {
* Combines sources and minifies the result. * Combines sources and minifies the result.
* *
* @return string * @return string
*
* @throws Exception
*/ */
protected static function _combineMinify() protected static function _combineMinify()
{ {
@ -526,7 +518,6 @@ class Minify {
$imploded = implode($implodeSeparator, $groupToProcessTogether); $imploded = implode($implodeSeparator, $groupToProcessTogether);
$groupToProcessTogether = array(); $groupToProcessTogether = array();
if ($lastMinifier) { if ($lastMinifier) {
self::$_controller->loadMinifier($lastMinifier);
try { try {
$content[] = call_user_func($lastMinifier, $imploded, $lastOptions); $content[] = call_user_func($lastMinifier, $imploded, $lastOptions);
} catch (Exception $e) { } catch (Exception $e) {
@ -574,7 +565,7 @@ class Minify {
{ {
$name = preg_replace('/[^a-zA-Z0-9\\.=_,]/', '', self::$_controller->selectionId); $name = preg_replace('/[^a-zA-Z0-9\\.=_,]/', '', self::$_controller->selectionId);
$name = preg_replace('/\\.+/', '.', $name); $name = preg_replace('/\\.+/', '.', $name);
$name = substr($name, 0, 200 - 34 - strlen($prefix)); $name = substr($name, 0, 100 - 34 - strlen($prefix));
$md5 = md5(serialize(array( $md5 = md5(serialize(array(
Minify_Source::getDigest(self::$_controller->sources) Minify_Source::getDigest(self::$_controller->sources)
,self::$_options['minifiers'] ,self::$_options['minifiers']

View File

@ -4,8 +4,6 @@
* @package Minify * @package Minify
*/ */
require_once 'Minify/Source.php';
/** /**
* Maintain a single last modification time for a group of Minify sources to * Maintain a single last modification time for a group of Minify sources to
* allow use of far off Expires headers in Minify. * allow use of far off Expires headers in Minify.

View File

@ -56,6 +56,7 @@ class Minify_CSS {
public static function minify($css, $options = array()) public static function minify($css, $options = array())
{ {
$options = array_merge(array( $options = array_merge(array(
'compress' => true,
'removeCharsets' => true, 'removeCharsets' => true,
'preserveComments' => true, 'preserveComments' => true,
'currentDir' => null, 'currentDir' => null,
@ -67,21 +68,20 @@ class Minify_CSS {
if ($options['removeCharsets']) { if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css); $css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
} }
require_once 'Minify/CSS/Compressor.php'; 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 {
require_once 'Minify/CommentPreserver.php'; $css = Minify_CommentPreserver::process(
$css = Minify_CommentPreserver::process( $css
$css ,array('Minify_CSS_Compressor', 'process')
,array('Minify_CSS_Compressor', 'process') ,array($options)
,array($options) );
); }
} }
if (! $options['currentDir'] && ! $options['prependRelativePath']) { if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css; return $css;
} }
require_once 'Minify/CSS/UriRewriter.php';
if ($options['currentDir']) { if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite( return Minify_CSS_UriRewriter::rewrite(
$css $css

View File

@ -70,7 +70,7 @@ class Minify_CSS_UriRewriter {
// rewrite // rewrite
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
,array(self::$className, '_processUriCB'), $css); ,array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/' $css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css); ,array(self::$className, '_processUriCB'), $css);
return $css; return $css;
@ -94,7 +94,7 @@ class Minify_CSS_UriRewriter {
// append // append
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
,array(self::$className, '_processUriCB'), $css); ,array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/' $css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css); ,array(self::$className, '_processUriCB'), $css);
self::$_prependPath = null; self::$_prependPath = null;
@ -282,11 +282,8 @@ class Minify_CSS_UriRewriter {
? $m[1] ? $m[1]
: substr($m[1], 1, strlen($m[1]) - 2); : substr($m[1], 1, strlen($m[1]) - 2);
} }
// analyze URI // if not root/scheme relative and not starts with scheme
if ('/' !== $uri[0] // root-relative if (!preg_match('~^(/|[a-z]+\:)~', $uri)) {
&& false === strpos($uri, '//') // protocol (non-data)
&& 0 !== strpos($uri, 'data:') // data protocol
) {
// URI is file-relative: rewrite depending on options // URI is file-relative: rewrite depending on options
if (self::$_prependPath === null) { if (self::$_prependPath === null) {
$uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks); $uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);

View File

@ -98,6 +98,9 @@ class Minify_Cache_File {
{ {
if ($this->_locking) { if ($this->_locking) {
$fp = fopen($this->_path . '/' . $id, 'rb'); $fp = fopen($this->_path . '/' . $id, 'rb');
if (!$fp) {
return false;
}
flock($fp, LOCK_SH); flock($fp, LOCK_SH);
$ret = stream_get_contents($fp); $ret = stream_get_contents($fp);
flock($fp, LOCK_UN); flock($fp, LOCK_UN);
@ -186,7 +189,6 @@ class Minify_Cache_File {
*/ */
protected function _log($msg) protected function _log($msg)
{ {
require_once 'Minify/Logger.php';
Minify_Logger::log($msg); Minify_Logger::log($msg);
} }

View File

@ -78,33 +78,6 @@ abstract class Minify_Controller_Base {
return $ret; return $ret;
} }
/**
* Load any code necessary to execute the given minifier callback.
*
* The controller is responsible for loading minification code on demand
* via this method. This built-in function will only load classes for
* static method callbacks where the class isn't already defined. It uses
* the PEAR convention, so, given array('Jimmy_Minifier', 'minCss'), this
* function will include 'Jimmy/Minifier.php'.
*
* If you need code loaded on demand and this doesn't suit you, you'll need
* to override this function in your subclass.
* @see Minify_Controller_Page::loadMinifier()
*
* @param callback $minifierCallback callback of minifier function
*
* @return null
*/
public function loadMinifier($minifierCallback)
{
if (is_array($minifierCallback)
&& is_string($minifierCallback[0])
&& !class_exists($minifierCallback[0], false)) {
require str_replace('_', '/', $minifierCallback[0]) . '.php';
}
}
/** /**
* Is a user-given file within an allowable directory, existing, * Is a user-given file within an allowable directory, existing,
* and having an extension js/css/html/txt ? * and having an extension js/css/html/txt ?
@ -244,7 +217,6 @@ abstract class Minify_Controller_Base {
* @return null * @return null
*/ */
public function log($msg) { public function log($msg) {
require_once 'Minify/Logger.php';
Minify_Logger::log($msg); Minify_Logger::log($msg);
} }
} }

View File

@ -4,8 +4,6 @@
* @package Minify * @package Minify
*/ */
require_once 'Minify/Controller/Base.php';
/** /**
* Controller class for minifying a set of files * Controller class for minifying a set of files
* *

View File

@ -4,8 +4,6 @@
* @package Minify * @package Minify
*/ */
require_once 'Minify/Controller/Base.php';
/** /**
* Controller class for serving predetermined groups of minimized sets, selected * Controller class for serving predetermined groups of minimized sets, selected
* by PATH_INFO * by PATH_INFO

View File

@ -4,8 +4,6 @@
* @package Minify * @package Minify
*/ */
require_once 'Minify/Controller/Base.php';
/** /**
* Controller class for requests to /min/index.php * Controller class for requests to /min/index.php
* *
@ -22,6 +20,13 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
* @return array Minify options * @return array Minify options
*/ */
public function setupSources($options) { public function setupSources($options) {
// PHP insecure by default: realpath() and other FS functions can't handle null bytes.
foreach (array('g', 'b', 'f') as $key) {
if (isset($_GET[$key])) {
$_GET[$key] = str_replace("\x00", '', (string)$_GET[$key]);
}
}
// filter controller options // filter controller options
$cOptions = array_merge( $cOptions = array_merge(
array( array(
@ -36,7 +41,6 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
$sources = array(); $sources = array();
$this->selectionId = ''; $this->selectionId = '';
$firstMissingResource = null; $firstMissingResource = null;
if (isset($_GET['g'])) { if (isset($_GET['g'])) {
// add group(s) // add group(s)
$this->selectionId .= 'g=' . $_GET['g']; $this->selectionId .= 'g=' . $_GET['g'];
@ -195,9 +199,12 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
protected function _getFileSource($file, $cOptions) protected function _getFileSource($file, $cOptions)
{ {
$spec['filepath'] = $file; $spec['filepath'] = $file;
if ($cOptions['noMinPattern'] if ($cOptions['noMinPattern'] && preg_match($cOptions['noMinPattern'], basename($file))) {
&& preg_match($cOptions['noMinPattern'], basename($file))) { if (preg_match('~\.css$~i', $file)) {
$spec['minifier'] = ''; $spec['minifyOptions']['compress'] = false;
} else {
$spec['minifier'] = '';
}
} }
return new Minify_Source($spec); return new Minify_Source($spec);
} }

View File

@ -4,8 +4,6 @@
* @package Minify * @package Minify
*/ */
require_once 'Minify/Controller/Base.php';
/** /**
* Controller class for serving a single HTML page * Controller class for serving a single HTML page
* *
@ -59,7 +57,6 @@ class Minify_Controller_Page extends Minify_Controller_Base {
'cssMinifier' => array('Minify_CSS', 'minify') 'cssMinifier' => array('Minify_CSS', 'minify')
,'jsMinifier' => array('JSMin', 'minify') ,'jsMinifier' => array('JSMin', 'minify')
); );
$this->_loadCssJsMinifiers = true;
unset($options['minifyAll']); unset($options['minifyAll']);
} }
$this->sources[] = new Minify_Source($sourceSpec); $this->sources[] = new Minify_Source($sourceSpec);
@ -67,21 +64,5 @@ class Minify_Controller_Page extends Minify_Controller_Base {
$options['contentType'] = Minify::TYPE_HTML; $options['contentType'] = Minify::TYPE_HTML;
return $options; return $options;
} }
protected $_loadCssJsMinifiers = false;
/**
* @see Minify_Controller_Base::loadMinifier()
*/
public function loadMinifier($minifierCallback)
{
if ($this->_loadCssJsMinifiers) {
// Minify will not call for these so we must manually load
// them when Minify/HTML.php is called for.
require_once 'Minify/CSS.php';
require_once 'JSMin.php';
}
parent::loadMinifier($minifierCallback); // load Minify/HTML.php
}
} }

View File

@ -4,8 +4,6 @@
* @package Minify * @package Minify
*/ */
require_once 'Minify/Controller/Base.php';
/** /**
* Controller class for emulating version 1 of minify.php (mostly a proof-of-concept) * Controller class for emulating version 1 of minify.php (mostly a proof-of-concept)
* *
@ -26,6 +24,11 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
* *
*/ */
public function setupSources($options) { public function setupSources($options) {
// PHP insecure by default: realpath() and other FS functions can't handle null bytes.
if (isset($_GET['files'])) {
$_GET['files'] = str_replace("\x00", '', (string)$_GET['files']);
}
self::_setupDefines(); self::_setupDefines();
if (MINIFY_USE_CACHE) { if (MINIFY_USE_CACHE) {
$cacheDir = defined('MINIFY_CACHE_DIR') $cacheDir = defined('MINIFY_CACHE_DIR')
@ -51,7 +54,6 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
) { ) {
return $options; return $options;
} }
$extension = $m[1];
$files = explode(',', $_GET['files']); $files = explode(',', $_GET['files']);
if (count($files) > MINIFY_MAX_FILES) { if (count($files) > MINIFY_MAX_FILES) {
@ -63,7 +65,6 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
. DIRECTORY_SEPARATOR; . DIRECTORY_SEPARATOR;
$prependAbsPaths = $_SERVER['DOCUMENT_ROOT']; $prependAbsPaths = $_SERVER['DOCUMENT_ROOT'];
$sources = array();
$goodFiles = array(); $goodFiles = array();
$hasBadSource = false; $hasBadSource = false;

View File

@ -17,6 +17,10 @@
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_HTML { class Minify_HTML {
/**
* @var boolean
*/
protected $_jsCleanComments = true;
/** /**
* "Minify" an HTML page * "Minify" an HTML page
@ -37,7 +41,7 @@ class Minify_HTML {
* @return string * @return string
*/ */
public static function minify($html, $options = array()) { public static function minify($html, $options = array()) {
$min = new Minify_HTML($html, $options); $min = new self($html, $options);
return $min->process(); return $min->process();
} }
@ -55,10 +59,10 @@ class Minify_HTML {
* 'jsMinifier' : (optional) callback function to process content of SCRIPT * 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored. * elements. Note: the type attribute is ignored.
* *
* 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
*
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype. * unset, minify will sniff for an XHTML doctype.
*
* @return null
*/ */
public function __construct($html, $options = array()) public function __construct($html, $options = array())
{ {
@ -72,6 +76,9 @@ class Minify_HTML {
if (isset($options['jsMinifier'])) { if (isset($options['jsMinifier'])) {
$this->_jsMinifier = $options['jsMinifier']; $this->_jsMinifier = $options['jsMinifier'];
} }
if (isset($options['jsCleanComments'])) {
$this->_jsCleanComments = (bool)$options['jsCleanComments'];
}
} }
@ -124,7 +131,7 @@ class Minify_HTML {
// remove ws around block/undisplayed elements // remove ws around block/undisplayed elements
$this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
.'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' .'|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
.'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
.'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
.'|ul)\\b[^>]*>)/i', '$1', $this->_html); .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
@ -215,7 +222,9 @@ class Minify_HTML {
$ws2 = ($m[4] === '') ? '' : ' '; $ws2 = ($m[4] === '') ? '' : ' ';
// remove HTML comments (and ending "//" if present) // remove HTML comments (and ending "//" if present)
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js); if ($this->_jsCleanComments) {
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
}
// remove CDATA section markers // remove CDATA section markers
$js = $this->_removeCdata($js); $js = $this->_removeCdata($js);

View File

@ -15,10 +15,10 @@ class Minify_HTML_Helper {
public $minAppUri = '/min'; public $minAppUri = '/min';
public $groupsConfigFile = ''; public $groupsConfigFile = '';
/* /**
* Get an HTML-escaped Minify URI for a group or set of files * Get an HTML-escaped Minify URI for a group or set of files
* *
* @param mixed $keyOrFiles a group key or array of filepaths/URIs * @param string|array $keyOrFiles a group key or array of filepaths/URIs
* @param array $opts options: * @param array $opts options:
* 'farExpires' : (default true) append a modified timestamp for cache revving * 'farExpires' : (default true) append a modified timestamp for cache revving
* 'debug' : (default false) append debug flag * 'debug' : (default false) append debug flag
@ -51,8 +51,12 @@ class Minify_HTML_Helper {
return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']); return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']);
} }
/* /**
* Get non-HTML-escaped URI to minify the specified files * Get non-HTML-escaped URI to minify the specified files
*
* @param bool $farExpires
* @param bool $debug
* @return string
*/ */
public function getRawUri($farExpires = true, $debug = false) public function getRawUri($farExpires = true, $debug = false)
{ {
@ -74,6 +78,12 @@ class Minify_HTML_Helper {
return $path; return $path;
} }
/**
* Set the files that will comprise the URI we're building
*
* @param array $files
* @param bool $checkLastModified
*/
public function setFiles($files, $checkLastModified = true) public function setFiles($files, $checkLastModified = true)
{ {
$this->_groupKey = null; $this->_groupKey = null;
@ -94,6 +104,12 @@ class Minify_HTML_Helper {
$this->_filePaths = $files; $this->_filePaths = $files;
} }
/**
* Set the group of files that will comprise the URI we're building
*
* @param string $key
* @param bool $checkLastModified
*/
public function setGroup($key, $checkLastModified = true) public function setGroup($key, $checkLastModified = true)
{ {
$this->_groupKey = $key; $this->_groupKey = $key;
@ -103,13 +119,23 @@ class Minify_HTML_Helper {
} }
if (is_file($this->groupsConfigFile)) { if (is_file($this->groupsConfigFile)) {
$gc = (require $this->groupsConfigFile); $gc = (require $this->groupsConfigFile);
if (isset($gc[$key])) { $keys = explode(',', $key);
$this->_lastModified = self::getLastModified($gc[$key]); foreach ($keys as $key) {
if (isset($gc[$key])) {
$this->_lastModified = self::getLastModified($gc[$key], $this->_lastModified);
}
} }
} }
} }
} }
/**
* Get the max(lastModified) of all files
*
* @param array|string $sources
* @param int $lastModified
* @return int
*/
public static function getLastModified($sources, $lastModified = 0) public static function getLastModified($sources, $lastModified = 0)
{ {
$max = $lastModified; $max = $lastModified;
@ -142,13 +168,19 @@ class Minify_HTML_Helper {
* @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) {
$l = count($arr); if (!isset($arr[0][$pos])) {
return '';
}
$c = $arr[0][$pos]; $c = $arr[0][$pos];
if ($c === '' || $l === 1) $l = count($arr);
if ($l === 1) {
return $c; return $c;
for ($i = 1; $i < $l; ++$i) }
if ($arr[$i][$pos] !== $c) for ($i = 1; $i < $l; ++$i) {
if ($arr[$i][$pos] !== $c) {
return ''; return '';
}
}
return $c; return $c;
} }
@ -157,11 +189,11 @@ class Minify_HTML_Helper {
* *
* @param array $paths root-relative URIs of files * @param array $paths root-relative URIs of files
* @param string $minRoot root-relative URI of the "min" application * @param string $minRoot root-relative URI of the "min" application
* @return string
*/ */
protected static function _getShortestUri($paths, $minRoot = '/min/') { protected static function _getShortestUri($paths, $minRoot = '/min/') {
$pos = 0; $pos = 0;
$base = ''; $base = '';
$c;
while (true) { while (true) {
$c = self::_getCommonCharAtPos($paths, $pos); $c = self::_getCommonCharAtPos($paths, $pos);
if ($c === '') { if ($c === '') {

View File

@ -14,13 +14,68 @@
* @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 {
const URL = 'http://closure-compiler.appspot.com/compile';
/** /**
* Minify Javascript code via HTTP request to the Closure Compiler API * @var string The option key for the maximum POST byte size
*/
const OPTION_MAX_BYTES = 'maxBytes';
/**
* @var string The option key for additional params. @see __construct
*/
const OPTION_ADDITIONAL_OPTIONS = 'additionalParams';
/**
* @var string The option key for the fallback Minifier
*/
const OPTION_FALLBACK_FUNCTION = 'fallbackFunc';
/**
* @var string The option key for the service URL
*/
const OPTION_COMPILER_URL = 'compilerUrl';
/**
* @var int The default maximum POST byte size according to https://developers.google.com/closure/compiler/docs/api-ref
*/
const DEFAULT_MAX_BYTES = 200000;
/**
* @var string[] $DEFAULT_OPTIONS The default options to pass to the compiler service
*
* @note This would be a constant if PHP allowed it
*/
private static $DEFAULT_OPTIONS = array(
'output_format' => 'text',
'compilation_level' => 'SIMPLE_OPTIMIZATIONS'
);
/**
* @var string $url URL of compiler server. defaults to Google's
*/
protected $serviceUrl = 'http://closure-compiler.appspot.com/compile';
/**
* @var int $maxBytes The maximum JS size that can be sent to the compiler server in bytes
*/
protected $maxBytes = self::DEFAULT_MAX_BYTES;
/**
* @var string[] $additionalOptions Additional options to pass to the compiler service
*/
protected $additionalOptions = array();
/**
* @var callable Function to minify JS if service fails. Default is JSMin
*/
protected $fallbackMinifier = array('JSMin', 'minify');
/**
* Minify JavaScript code via HTTP request to a Closure Compiler API
* *
* @param string $js input code * @param string $js input code
* @param array $options unused at this point * @param array $options Options passed to __construct(). @see __construct
*
* @return string * @return string
*/ */
public static function minify($js, array $options = array()) public static function minify($js, array $options = array())
@ -30,63 +85,101 @@ class Minify_JS_ClosureCompiler {
} }
/** /**
* @param array $options Options with keys available below:
* *
* @param array $options * fallbackFunc : (callable) function to minify if service unavailable. Default is JSMin.
* *
* fallbackFunc : default array($this, 'fallback'); * compilerUrl : (string) URL to closure compiler server
*
* maxBytes : (int) The maximum amount of bytes to be sent as js_code in the POST request.
* Defaults to 200000.
*
* additionalParams : (string[]) Additional parameters to pass to the compiler server. Can be anything named
* in https://developers.google.com/closure/compiler/docs/api-ref except for js_code and
* output_info
*/ */
public function __construct(array $options = array()) public function __construct(array $options = array())
{ {
$this->_fallbackFunc = isset($options['fallbackMinifier']) if (isset($options[self::OPTION_FALLBACK_FUNCTION])) {
? $options['fallbackMinifier'] $this->fallbackMinifier = $options[self::OPTION_FALLBACK_FUNCTION];
: array($this, '_fallback'); }
if (isset($options[self::OPTION_COMPILER_URL])) {
$this->serviceUrl = $options[self::OPTION_COMPILER_URL];
}
if (isset($options[self::OPTION_ADDITIONAL_OPTIONS]) && is_array($options[self::OPTION_ADDITIONAL_OPTIONS])) {
$this->additionalOptions = $options[self::OPTION_ADDITIONAL_OPTIONS];
}
if (isset($options[self::OPTION_MAX_BYTES])) {
$this->maxBytes = (int) $options[self::OPTION_MAX_BYTES];
}
} }
/**
* Call the service to perform the minification
*
* @param string $js JavaScript code
* @return string
* @throws Minify_JS_ClosureCompiler_Exception
*/
public function min($js) public function min($js)
{ {
$postBody = $this->_buildPostBody($js); $postBody = $this->buildPostBody($js);
$bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
? mb_strlen($postBody, '8bit') if ($this->maxBytes > 0) {
: strlen($postBody); $bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
if ($bytes > 200000) { ? mb_strlen($postBody, '8bit')
throw new Minify_JS_ClosureCompiler_Exception( : strlen($postBody);
'POST content larger than 200000 bytes' if ($bytes > $this->maxBytes) {
); throw new Minify_JS_ClosureCompiler_Exception(
'POST content larger than ' . $this->maxBytes . ' bytes'
);
}
} }
$response = $this->_getResponse($postBody);
$response = $this->getResponse($postBody);
if (preg_match('/^Error\(\d\d?\):/', $response)) { if (preg_match('/^Error\(\d\d?\):/', $response)) {
if (is_callable($this->_fallbackFunc)) { if (is_callable($this->fallbackMinifier)) {
// use fallback
$response = "/* Received errors from Closure Compiler API:\n$response" $response = "/* Received errors from Closure Compiler API:\n$response"
. "\n(Using fallback minifier)\n*/\n"; . "\n(Using fallback minifier)\n*/\n";
$response .= call_user_func($this->_fallbackFunc, $js); $response .= call_user_func($this->fallbackMinifier, $js);
} else { } else {
throw new Minify_JS_ClosureCompiler_Exception($response); throw new Minify_JS_ClosureCompiler_Exception($response);
} }
} }
if ($response === '') { if ($response === '') {
$errors = $this->_getResponse($this->_buildPostBody($js, true)); $errors = $this->getResponse($this->buildPostBody($js, true));
throw new Minify_JS_ClosureCompiler_Exception($errors); throw new Minify_JS_ClosureCompiler_Exception($errors);
} }
return $response; return $response;
} }
protected $_fallbackFunc = null; /**
* Get the response for a given POST body
protected function _getResponse($postBody) *
* @param string $postBody
* @return string
* @throws Minify_JS_ClosureCompiler_Exception
*/
protected function getResponse($postBody)
{ {
$allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen')); $allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
if ($allowUrlFopen) { if ($allowUrlFopen) {
$contents = file_get_contents(self::URL, false, stream_context_create(array( $contents = file_get_contents($this->serviceUrl, false, stream_context_create(array(
'http' => array( 'http' => array(
'method' => 'POST', 'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded', 'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
'content' => $postBody, 'content' => $postBody,
'max_redirects' => 0, 'max_redirects' => 0,
'timeout' => 15, 'timeout' => 15,
) )
))); )));
} elseif (defined('CURLOPT_POST')) { } elseif (defined('CURLOPT_POST')) {
$ch = curl_init(self::URL); $ch = curl_init($this->serviceUrl);
curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
@ -100,33 +193,37 @@ class Minify_JS_ClosureCompiler {
"Could not make HTTP request: allow_url_open is false and cURL not available" "Could not make HTTP request: allow_url_open is false and cURL not available"
); );
} }
if (false === $contents) { if (false === $contents) {
throw new Minify_JS_ClosureCompiler_Exception( throw new Minify_JS_ClosureCompiler_Exception(
"No HTTP response from server" "No HTTP response from server"
); );
} }
return trim($contents); return trim($contents);
} }
protected function _buildPostBody($js, $returnErrors = false)
{
return http_build_query(array(
'js_code' => $js,
'output_info' => ($returnErrors ? 'errors' : 'compiled_code'),
'output_format' => 'text',
'compilation_level' => 'SIMPLE_OPTIMIZATIONS'
), null, '&');
}
/** /**
* Default fallback function if CC API fails * Build a POST request body
* @param string $js *
* @param string $js JavaScript code
* @param bool $returnErrors
* @return string * @return string
*/ */
protected function _fallback($js) protected function buildPostBody($js, $returnErrors = false)
{ {
require_once 'JSMin.php'; return http_build_query(
return JSMin::minify($js); array_merge(
self::$DEFAULT_OPTIONS,
$this->additionalOptions,
array(
'js_code' => $js,
'output_info' => ($returnErrors ? 'errors' : 'compiled_code')
)
),
null,
'&'
);
} }
} }

View File

@ -55,7 +55,11 @@ class Minify_Lines {
$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)) {
array_push($newLines, '', "/* {$id} */", ''); if ($inComment) {
array_push($newLines, '', "/* {$id} *|", '');
} else {
array_push($newLines, '', "/* {$id} */", '');
}
} }
++$i; ++$i;
$newLines[] = self::_addNote($line, $i, $inComment, $padTo); $newLines[] = self::_addNote($line, $i, $inComment, $padTo);
@ -65,7 +69,6 @@ class Minify_Lines {
// check for desired URI rewriting // check for desired URI rewriting
if (isset($options['currentDir'])) { if (isset($options['currentDir'])) {
require_once 'Minify/CSS/UriRewriter.php';
Minify_CSS_UriRewriter::$debugText = ''; Minify_CSS_UriRewriter::$debugText = '';
$content = Minify_CSS_UriRewriter::rewrite( $content = Minify_CSS_UriRewriter::rewrite(
$content $content
@ -93,6 +96,9 @@ class Minify_Lines {
*/ */
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 $search = $inComment
? '*/' ? '*/'

View File

@ -13,7 +13,7 @@
* Java environment. * Java environment.
* *
* <code> * <code>
* Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.3.5.jar'; * Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.4.6.jar';
* Minify_YUICompressor::$tempDir = '/tmp'; * Minify_YUICompressor::$tempDir = '/tmp';
* $code = Minify_YUICompressor::minifyJs( * $code = Minify_YUICompressor::minifyJs(
* $code * $code
@ -21,6 +21,9 @@
* ); * );
* </code> * </code>
* *
* Note: In case you run out stack (default is 512k), you may increase stack size in $options:
* array('stack-size' => '2048k')
*
* @todo unit tests, $options docs * @todo unit tests, $options docs
* *
* @package Minify * @package Minify
@ -87,7 +90,7 @@ class Minify_YUICompressor {
{ {
self::_prepare(); self::_prepare();
if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) { if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) {
throw new Exception('Minify_YUICompressor : could not create temp file.'); 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);
@ -108,10 +111,15 @@ class Minify_YUICompressor {
,'nomunge' => false ,'nomunge' => false
,'preserve-semi' => false ,'preserve-semi' => false
,'disable-optimizations' => false ,'disable-optimizations' => false
,'stack-size' => ''
) )
,$userOptions ,$userOptions
); );
$cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile) $cmd = self::$javaExecutable
. (!empty($o['stack-size'])
? ' -Xss' . $o['stack-size']
: '')
. ' -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']}"
@ -134,8 +142,8 @@ class Minify_YUICompressor {
if (! is_file(self::$jarFile)) { if (! is_file(self::$jarFile)) {
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.'); throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.');
} }
if (! is_executable(self::$jarFile)) { if (! is_readable(self::$jarFile)) {
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not executable.'); throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not readable.');
} }
if (! is_dir(self::$tempDir)) { if (! is_dir(self::$tempDir)) {
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.'); throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.');

View File

@ -2,6 +2,9 @@
namespace MrClay; namespace MrClay;
use MrClay\Cli\Arg;
use InvalidArgumentException;
/** /**
* Forms a front controller for a console app, handling and validating arguments (options) * Forms a front controller for a console app, handling and validating arguments (options)
* *
@ -51,7 +54,7 @@ class Cli {
public $isHelpRequest = false; public $isHelpRequest = false;
/** /**
* @var array of Cli\Arg * @var Arg[]
*/ */
protected $_args = array(); protected $_args = array();
@ -80,8 +83,8 @@ class Cli {
} }
/** /**
* @param Cli\Arg|string $letter * @param Arg|string $letter
* @return Cli\Arg * @return Arg
*/ */
public function addOptionalArg($letter) public function addOptionalArg($letter)
{ {
@ -89,8 +92,8 @@ class Cli {
} }
/** /**
* @param Cli\Arg|string $letter * @param Arg|string $letter
* @return Cli\Arg * @return Arg
*/ */
public function addRequiredArg($letter) public function addRequiredArg($letter)
{ {
@ -100,17 +103,17 @@ class Cli {
/** /**
* @param string $letter * @param string $letter
* @param bool $required * @param bool $required
* @param Cli\Arg|null $arg * @param Arg|null $arg
* @return Cli\Arg * @return Arg
* @throws \InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function addArgument($letter, $required, Cli\Arg $arg = null) public function addArgument($letter, $required, Arg $arg = null)
{ {
if (! preg_match('/^[a-zA-Z]$/', $letter)) { if (! preg_match('/^[a-zA-Z]$/', $letter)) {
throw new \InvalidArgumentException('$letter must be in [a-zA-z]'); throw new InvalidArgumentException('$letter must be in [a-zA-Z]');
} }
if (! $arg) { if (! $arg) {
$arg = new Cli\Arg($required); $arg = new Arg($required);
} }
$this->_args[$letter] = $arg; $this->_args[$letter] = $arg;
return $arg; return $arg;
@ -118,7 +121,7 @@ class Cli {
/** /**
* @param string $letter * @param string $letter
* @return Cli\Arg|null * @return Arg|null
*/ */
public function getArgument($letter) public function getArgument($letter)
{ {
@ -143,7 +146,7 @@ class Cli {
$lettersUsed = ''; $lettersUsed = '';
foreach ($this->_args as $letter => $arg) { foreach ($this->_args as $letter => $arg) {
/* @var Cli\Arg $arg */ /* @var Arg $arg */
$options .= $letter; $options .= $letter;
$lettersUsed .= $letter; $lettersUsed .= $letter;
@ -159,7 +162,7 @@ class Cli {
$this->debug['getopt_return'] = $o; $this->debug['getopt_return'] = $o;
foreach ($this->_args as $letter => $arg) { foreach ($this->_args as $letter => $arg) {
/* @var Cli\Arg $arg */ /* @var Arg $arg */
$this->values[$letter] = false; $this->values[$letter] = false;
if (isset($o[$letter])) { if (isset($o[$letter])) {
if (is_bool($o[$letter])) { if (is_bool($o[$letter])) {
@ -295,7 +298,7 @@ class Cli {
{ {
$r = "\n"; $r = "\n";
foreach ($this->_args as $letter => $arg) { foreach ($this->_args as $letter => $arg) {
/* @var Cli\Arg $arg */ /* @var Arg $arg */
$desc = $arg->getDescription(); $desc = $arg->getDescription();
$flag = " -$letter "; $flag = " -$letter ";
if ($arg->mayHaveValue) { if ($arg->mayHaveValue) {

View File

@ -2,6 +2,8 @@
namespace MrClay\Cli; namespace MrClay\Cli;
use BadMethodCallException;
/** /**
* An argument for a CLI app. This specifies the argument, what values it expects and * An argument for a CLI app. This specifies the argument, what values it expects and
* how it's treated during validation. * how it's treated during validation.
@ -150,7 +152,7 @@ class Arg {
* @param string $name * @param string $name
* @param array $args * @param array $args
* @return Arg * @return Arg
* @throws \BadMethodCallException * @throws BadMethodCallException
*/ */
public function __call($name, array $args = array()) public function __call($name, array $args = array())
{ {
@ -160,7 +162,7 @@ class Arg {
$this->spec['mustHaveValue'] = true; $this->spec['mustHaveValue'] = true;
} }
} else { } else {
throw new \BadMethodCallException('Method does not exist'); throw new BadMethodCallException('Method does not exist');
} }
return $this; return $this;
} }

8
js/jquery.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long