Initial commit
This commit is contained in:
470
typo3conf/ext/scriptmerger/Resources/JShrink/Minifier.php
Normal file
470
typo3conf/ext/scriptmerger/Resources/JShrink/Minifier.php
Normal file
@@ -0,0 +1,470 @@
|
||||
<?php
|
||||
/**
|
||||
* JShrink
|
||||
*
|
||||
* Copyright (c) 2009-2012, Robert Hafner <tedivm@tedivm.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Robert Hafner nor the names of his
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package JShrink
|
||||
* @author Robert Hafner <tedivm@tedivm.com>
|
||||
* @copyright 2009-2012 Robert Hafner <tedivm@tedivm.com>
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
* @link https://github.com/tedivm/JShrink
|
||||
* @version Release: 0.5.1
|
||||
*/
|
||||
|
||||
namespace JShrink;
|
||||
|
||||
/**
|
||||
* Minifier
|
||||
*
|
||||
* Usage - Minifier::minify($js);
|
||||
* Usage - Minifier::minify($js, $options);
|
||||
* Usage - Minifier::minify($js, array('flaggedComments' => false));
|
||||
*
|
||||
* @package JShrink
|
||||
* @author Robert Hafner <tedivm@tedivm.com>
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
*/
|
||||
class Minifier
|
||||
{
|
||||
/**
|
||||
* The input javascript to be minified.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $input;
|
||||
|
||||
/**
|
||||
* The location of the character (in the input string) that is next to be
|
||||
* processed.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $index = 0;
|
||||
|
||||
/**
|
||||
* The first of the characters currently being looked at.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $a = '';
|
||||
|
||||
|
||||
/**
|
||||
* The next character being looked at (after a);
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $b = '';
|
||||
|
||||
/**
|
||||
* This character is only active when certain look ahead actions take place.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $c;
|
||||
|
||||
/**
|
||||
* Contains the options for the current minification process.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* 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 array
|
||||
*/
|
||||
static protected $defaultOptions = array('flaggedComments' => true);
|
||||
|
||||
/**
|
||||
* Contains a copy of the JShrink object used to run minification. This is
|
||||
* only used internally, and is only stored for performance reasons. There
|
||||
* is no internal data shared between minification requests.
|
||||
*/
|
||||
static protected $jshrink;
|
||||
|
||||
/**
|
||||
* Minifier::minify takes a string containing javascript and removes
|
||||
* unneeded characters in order to shrink the code without altering it's
|
||||
* functionality.
|
||||
*/
|
||||
static public function minify($js, $options = array())
|
||||
{
|
||||
try{
|
||||
ob_start();
|
||||
$currentOptions = array_merge(self::$defaultOptions, $options);
|
||||
|
||||
if(!isset(self::$jshrink))
|
||||
self::$jshrink = new Minifier();
|
||||
|
||||
self::$jshrink->breakdownScript($js, $currentOptions);
|
||||
return ob_get_clean();
|
||||
|
||||
}catch(Exception $e){
|
||||
if(isset(self::$jshrink))
|
||||
self::$jshrink->clean();
|
||||
|
||||
ob_end_clean();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a javascript string and outputs only the required characters,
|
||||
* stripping out all unneeded characters.
|
||||
*
|
||||
* @param string $js The raw javascript to be minified
|
||||
* @param array $currentOptions Various runtime options in an associative array
|
||||
*/
|
||||
protected function breakdownScript($js, $currentOptions)
|
||||
{
|
||||
// reset work attributes in case this isn't the first run.
|
||||
$this->clean();
|
||||
|
||||
$this->options = $currentOptions;
|
||||
|
||||
$js = str_replace("\r\n", "\n", $js);
|
||||
$this->input = str_replace("\r", "\n", $js);
|
||||
|
||||
|
||||
$this->a = $this->getReal();
|
||||
|
||||
// the only time the length can be higher than 1 is if a conditional
|
||||
// comment needs to be displayed and the only time that can happen for
|
||||
// $a is on the very first run
|
||||
while(strlen($this->a) > 1)
|
||||
{
|
||||
echo $this->a;
|
||||
$this->a = $this->getReal();
|
||||
}
|
||||
|
||||
$this->b = $this->getReal();
|
||||
|
||||
while($this->a !== false && !is_null($this->a) && $this->a !== '')
|
||||
{
|
||||
|
||||
// now we give $b the same check for conditional comments we gave $a
|
||||
// before we began looping
|
||||
if(strlen($this->b) > 1)
|
||||
{
|
||||
echo $this->a . $this->b;
|
||||
$this->a = $this->getReal();
|
||||
$this->b = $this->getReal();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch($this->a)
|
||||
{
|
||||
// new lines
|
||||
case "\n":
|
||||
// if the next line is something that can't stand alone
|
||||
// preserve the newline
|
||||
if(strpos('(-+{[@', $this->b) !== false)
|
||||
{
|
||||
echo $this->a;
|
||||
$this->saveString();
|
||||
break;
|
||||
}
|
||||
|
||||
// if its a space we move down to the string test below
|
||||
if($this->b === ' ')
|
||||
break;
|
||||
|
||||
// otherwise we treat the newline like a space
|
||||
|
||||
case ' ':
|
||||
if(self::isAlphaNumeric($this->b))
|
||||
echo $this->a;
|
||||
|
||||
$this->saveString();
|
||||
break;
|
||||
|
||||
default:
|
||||
switch($this->b)
|
||||
{
|
||||
case "\n":
|
||||
if(strpos('}])+-"\'', $this->a) !== false)
|
||||
{
|
||||
echo $this->a;
|
||||
$this->saveString();
|
||||
break;
|
||||
}else{
|
||||
if(self::isAlphaNumeric($this->a))
|
||||
{
|
||||
echo $this->a;
|
||||
$this->saveString();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
if(!self::isAlphaNumeric($this->a))
|
||||
break;
|
||||
|
||||
default:
|
||||
// check for some regex that breaks stuff
|
||||
if($this->a == '/' && ($this->b == '\'' || $this->b == '"'))
|
||||
{
|
||||
$this->saveRegex();
|
||||
continue;
|
||||
}
|
||||
|
||||
echo $this->a;
|
||||
$this->saveString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// do reg check of doom
|
||||
$this->b = $this->getReal();
|
||||
|
||||
if(($this->b == '/' && strpos('(,=:[!&|?', $this->a) !== false))
|
||||
$this->saveRegex();
|
||||
}
|
||||
$this->clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next string for processing based off of the current index.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getChar()
|
||||
{
|
||||
if(isset($this->c))
|
||||
{
|
||||
$char = $this->c;
|
||||
unset($this->c);
|
||||
}else{
|
||||
$tchar = substr($this->input, $this->index, 1);
|
||||
if(isset($tchar) && $tchar !== false)
|
||||
{
|
||||
$char = $tchar;
|
||||
$this->index++;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if($char !== "\n" && ord($char) < 32)
|
||||
return ' ';
|
||||
|
||||
return $char;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function gets the next "real" character. It is essentially a wrapper
|
||||
* around the getChar function that skips comments. This has significant
|
||||
* performance benefits as the skipping is done using native functions (ie,
|
||||
* c code) rather than in script php.
|
||||
*
|
||||
* @return string Next 'real' character to be processed.
|
||||
*/
|
||||
protected function getReal()
|
||||
{
|
||||
$startIndex = $this->index;
|
||||
$char = $this->getChar();
|
||||
|
||||
if($char == '/')
|
||||
{
|
||||
$this->c = $this->getChar();
|
||||
|
||||
if($this->c == '/')
|
||||
{
|
||||
$thirdCommentString = substr($this->input, $this->index, 1);
|
||||
|
||||
// kill rest of line
|
||||
$char = $this->getNext("\n");
|
||||
|
||||
if($thirdCommentString == '@')
|
||||
{
|
||||
$endPoint = ($this->index) - $startIndex;
|
||||
unset($this->c);
|
||||
$char = "\n" . substr($this->input, $startIndex, $endPoint);
|
||||
}else{
|
||||
$char = $this->getChar();
|
||||
$char = $this->getChar();
|
||||
}
|
||||
|
||||
}elseif($this->c == '*'){
|
||||
|
||||
$this->getChar(); // current C
|
||||
$thirdCommentString = $this->getChar();
|
||||
|
||||
if($thirdCommentString == '@')
|
||||
{
|
||||
// conditional comment
|
||||
|
||||
// we're gonna back up a bit and and send the comment back,
|
||||
// where the first char will be echoed and the rest will be
|
||||
// treated like a string
|
||||
$this->index = $this->index-2;
|
||||
return '/';
|
||||
|
||||
}elseif($this->getNext('*/')){
|
||||
// kill everything up to the next */
|
||||
|
||||
$this->getChar(); // get *
|
||||
$this->getChar(); // get /
|
||||
|
||||
$char = $this->getChar(); // get next real character
|
||||
|
||||
// if YUI-style comments are enabled we reinsert it into the stream
|
||||
if($this->options['flaggedComments'] && $thirdCommentString == '!')
|
||||
{
|
||||
$endPoint = ($this->index - 1) - $startIndex;
|
||||
echo "\n" . substr($this->input, $startIndex, $endPoint) . "\n";
|
||||
}
|
||||
|
||||
}else{
|
||||
$char = false;
|
||||
}
|
||||
|
||||
if($char === false)
|
||||
throw new \RuntimeException('Stray comment. ' . $this->index);
|
||||
|
||||
// if we're here c is part of the comment and therefore tossed
|
||||
if(isset($this->c))
|
||||
unset($this->c);
|
||||
}
|
||||
}
|
||||
return $char;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes the index ahead to the next instance of the supplied string. If it
|
||||
* is found the first character of the string is returned.
|
||||
*
|
||||
* @return string|false Returns the first character of the string or false.
|
||||
*/
|
||||
protected function getNext($string)
|
||||
{
|
||||
$pos = strpos($this->input, $string, $this->index);
|
||||
|
||||
if($pos === false)
|
||||
return false;
|
||||
|
||||
$this->index = $pos;
|
||||
return substr($this->input, $this->index, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a javascript string is detected this function crawls for the end of
|
||||
* it and saves the whole string.
|
||||
*
|
||||
*/
|
||||
protected function saveString()
|
||||
{
|
||||
$this->a = $this->b;
|
||||
if($this->a == "'" || $this->a == '"') // is the character a quote
|
||||
{
|
||||
// save literal string
|
||||
$stringType = $this->a;
|
||||
|
||||
while(1)
|
||||
{
|
||||
echo $this->a;
|
||||
$this->a = $this->getChar();
|
||||
|
||||
switch($this->a)
|
||||
{
|
||||
case $stringType:
|
||||
break 2;
|
||||
|
||||
case "\n":
|
||||
throw new \RuntimeException('Unclosed string. ' . $this->index);
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
echo $this->a;
|
||||
$this->a = $this->getChar();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When a regular expression is detected this funcion crawls for the end of
|
||||
* it and saves the whole regex.
|
||||
*/
|
||||
protected function saveRegex()
|
||||
{
|
||||
echo $this->a . $this->b;
|
||||
|
||||
while(($this->a = $this->getChar()) !== false)
|
||||
{
|
||||
if($this->a == '/')
|
||||
break;
|
||||
|
||||
if($this->a == '\\')
|
||||
{
|
||||
echo $this->a;
|
||||
$this->a = $this->getChar();
|
||||
}
|
||||
|
||||
if($this->a == "\n")
|
||||
throw new \RuntimeException('Stray regex pattern. ' . $this->index);
|
||||
|
||||
echo $this->a;
|
||||
}
|
||||
$this->b = $this->getReal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets attributes that do not need to be stored between requests so that
|
||||
* the next request is ready to go.
|
||||
*/
|
||||
protected function clean()
|
||||
{
|
||||
unset($this->input);
|
||||
$this->index = 0;
|
||||
$this->a = $this->b = '';
|
||||
unset($this->c);
|
||||
unset($this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a character is alphanumeric.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static protected function isAlphaNumeric($char)
|
||||
{
|
||||
return preg_match('/^[\w\$]$/', $char) === 1 || $char == '/';
|
||||
}
|
||||
|
||||
}
|
||||
99
typo3conf/ext/scriptmerger/Resources/Minify/CSS.php
Normal file
99
typo3conf/ext/scriptmerger/Resources/Minify/CSS.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Minify_CSS
|
||||
* @package Minify
|
||||
*/
|
||||
|
||||
/**
|
||||
* Minify CSS
|
||||
*
|
||||
* This class uses Minify_CSS_Compressor and Minify_CSS_UriRewriter to
|
||||
* minify CSS and rewrite relative URIs.
|
||||
*
|
||||
* @package Minify
|
||||
* @author Stephen Clay <steve@mrclay.org>
|
||||
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
||||
*/
|
||||
class Minify_CSS {
|
||||
|
||||
/**
|
||||
* Minify a CSS string
|
||||
*
|
||||
* @param string $css
|
||||
*
|
||||
* @param array $options available options:
|
||||
*
|
||||
* 'preserveComments': (default true) multi-line comments that begin
|
||||
* with "/*!" will be preserved with newlines before and after to
|
||||
* enhance readability.
|
||||
*
|
||||
* 'removeCharsets': (default true) remove all @charset at-rules
|
||||
*
|
||||
* 'prependRelativePath': (default null) if given, this string will be
|
||||
* prepended to all relative URIs in import/url declarations
|
||||
*
|
||||
* 'currentDir': (default null) if given, this is assumed to be the
|
||||
* directory of the current CSS file. Using this, minify will rewrite
|
||||
* all relative URIs in import/url declarations to correctly point to
|
||||
* the desired files. For this to work, the files *must* exist and be
|
||||
* visible by the PHP process.
|
||||
*
|
||||
* 'symlinks': (default = array()) If the CSS file is stored in
|
||||
* a symlink-ed directory, provide an array of link paths to
|
||||
* target paths, where the link paths are within the document root. Because
|
||||
* paths need to be normalized for this to work, use "//" to substitute
|
||||
* the doc root in the link paths (the array keys). E.g.:
|
||||
* <code>
|
||||
* array('//symlink' => '/real/target/path') // unix
|
||||
* array('//static' => 'D:\\staticStorage') // Windows
|
||||
* </code>
|
||||
*
|
||||
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
|
||||
* see Minify_CSS_UriRewriter::rewrite
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function minify($css, $options = array())
|
||||
{
|
||||
$options = array_merge(array(
|
||||
'removeCharsets' => true,
|
||||
'preserveComments' => true,
|
||||
'currentDir' => null,
|
||||
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
|
||||
'prependRelativePath' => null,
|
||||
'symlinks' => array(),
|
||||
), $options);
|
||||
|
||||
if ($options['removeCharsets']) {
|
||||
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
|
||||
}
|
||||
require_once 'CSS/Compressor.php';
|
||||
if (! $options['preserveComments']) {
|
||||
$css = Minify_CSS_Compressor::process($css, $options);
|
||||
} else {
|
||||
require_once 'CommentPreserver.php';
|
||||
$css = Minify_CommentPreserver::process(
|
||||
$css
|
||||
,array('Minify_CSS_Compressor', 'process')
|
||||
,array($options)
|
||||
);
|
||||
}
|
||||
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
|
||||
return $css;
|
||||
}
|
||||
require_once 'CSS/UriRewriter.php';
|
||||
if ($options['currentDir']) {
|
||||
return Minify_CSS_UriRewriter::rewrite(
|
||||
$css
|
||||
,$options['currentDir']
|
||||
,$options['docRoot']
|
||||
,$options['symlinks']
|
||||
);
|
||||
} else {
|
||||
return Minify_CSS_UriRewriter::prepend(
|
||||
$css
|
||||
,$options['prependRelativePath']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
249
typo3conf/ext/scriptmerger/Resources/Minify/CSS/Compressor.php
Normal file
249
typo3conf/ext/scriptmerger/Resources/Minify/CSS/Compressor.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Minify_CSS_Compressor
|
||||
* @package Minify
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compress CSS
|
||||
*
|
||||
* This is a heavy regex-based removal of whitespace, unnecessary
|
||||
* comments and tokens, and some CSS value minimization, where practical.
|
||||
* Many steps have been taken to avoid breaking comment-based hacks,
|
||||
* including the ie5/mac filter (and its inversion), but expect tricky
|
||||
* hacks involving comment tokens in 'content' value strings to break
|
||||
* minimization badly. A test suite is available.
|
||||
*
|
||||
* @package Minify
|
||||
* @author Stephen Clay <steve@mrclay.org>
|
||||
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
||||
*/
|
||||
class Minify_CSS_Compressor {
|
||||
|
||||
/**
|
||||
* Minify a CSS string
|
||||
*
|
||||
* @param string $css
|
||||
*
|
||||
* @param array $options (currently ignored)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function process($css, $options = array())
|
||||
{
|
||||
$obj = new Minify_CSS_Compressor($options);
|
||||
return $obj->_process($css);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array options
|
||||
*/
|
||||
protected $_options = null;
|
||||
|
||||
/**
|
||||
* @var bool Are we "in" a hack?
|
||||
*
|
||||
* I.e. are some browsers targeted until the next comment?
|
||||
*/
|
||||
protected $_inHack = false;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $options (currently ignored)
|
||||
*/
|
||||
private function __construct($options) {
|
||||
$this->_options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minify a CSS string
|
||||
*
|
||||
* @param string $css
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _process($css)
|
||||
{
|
||||
$css = str_replace("\r\n", "\n", $css);
|
||||
|
||||
// preserve empty comment after '>'
|
||||
// http://www.webdevout.net/css-hacks#in_css-selectors
|
||||
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
|
||||
|
||||
// preserve empty comment between property and value
|
||||
// http://css-discuss.incutio.com/?page=BoxModelHack
|
||||
$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
|
||||
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
|
||||
,array($this, '_commentCB'), $css);
|
||||
|
||||
// remove ws around { } and last semicolon in declaration block
|
||||
$css = preg_replace('/\\s*{\\s*/', '{', $css);
|
||||
$css = preg_replace('/;?\\s*}\\s*/', '}', $css);
|
||||
|
||||
// remove ws surrounding semicolons
|
||||
$css = preg_replace('/\\s*;\\s*/', ';', $css);
|
||||
|
||||
// remove ws around urls
|
||||
$css = preg_replace('/
|
||||
url\\( # url(
|
||||
\\s*
|
||||
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
|
||||
\\s*
|
||||
\\) # )
|
||||
/x', 'url($1)', $css);
|
||||
|
||||
// remove ws between rules and colons
|
||||
$css = preg_replace('/
|
||||
\\s*
|
||||
([{;]) # 1 = beginning of block or rule separator
|
||||
\\s*
|
||||
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
|
||||
\\s*
|
||||
:
|
||||
\\s*
|
||||
(\\b|[#\'"-]) # 3 = first character of a value
|
||||
/x', '$1$2:$3', $css);
|
||||
|
||||
// remove ws in selectors
|
||||
$css = preg_replace_callback('/
|
||||
(?: # non-capture
|
||||
\\s*
|
||||
[^~>+,\\s]+ # selector part
|
||||
\\s*
|
||||
[,>+~] # combinators
|
||||
)+
|
||||
\\s*
|
||||
[^~>+,\\s]+ # selector part
|
||||
{ # open declaration block
|
||||
/x'
|
||||
,array($this, '_selectorsCB'), $css);
|
||||
|
||||
// minimize hex colors
|
||||
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
|
||||
, '$1#$2$3$4$5', $css);
|
||||
|
||||
// remove spaces between font families
|
||||
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
|
||||
,array($this, '_fontFamilyCB'), $css);
|
||||
|
||||
$css = preg_replace('/@import\\s+url/', '@import url', $css);
|
||||
|
||||
// replace any ws involving newlines with a single newline
|
||||
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
|
||||
|
||||
// separate common descendent selectors w/ newlines (to limit line lengths)
|
||||
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
|
||||
|
||||
// Use newline after 1st numeric value (to limit line lengths).
|
||||
$css = preg_replace('/
|
||||
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
|
||||
\\s+
|
||||
/x'
|
||||
,"$1\n", $css);
|
||||
|
||||
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
|
||||
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
|
||||
|
||||
return trim($css);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace what looks like a set of selectors
|
||||
*
|
||||
* @param array $m regex matches
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _selectorsCB($m)
|
||||
{
|
||||
// remove ws around the combinators
|
||||
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a comment and return a replacement
|
||||
*
|
||||
* @param array $m regex matches
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _commentCB($m)
|
||||
{
|
||||
$hasSurroundingWs = (trim($m[0]) !== $m[1]);
|
||||
$m = $m[1];
|
||||
// $m is the comment content w/o the surrounding tokens,
|
||||
// but the return value will replace the entire comment.
|
||||
if ($m === 'keep') {
|
||||
return '/**/';
|
||||
}
|
||||
if ($m === '" "') {
|
||||
// component of http://tantek.com/CSS/Examples/midpass.html
|
||||
return '/*" "*/';
|
||||
}
|
||||
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
|
||||
// component of http://tantek.com/CSS/Examples/midpass.html
|
||||
return '/*";}}/* */';
|
||||
}
|
||||
if ($this->_inHack) {
|
||||
// inversion: feeding only to one browser
|
||||
if (preg_match('@
|
||||
^/ # comment started like /*/
|
||||
\\s*
|
||||
(\\S[\\s\\S]+?) # has at least some non-ws content
|
||||
\\s*
|
||||
/\\* # ends like /*/ or /**/
|
||||
@x', $m, $n)) {
|
||||
// end hack mode after this comment, but preserve the hack and comment content
|
||||
$this->_inHack = false;
|
||||
return "/*/{$n[1]}/**/";
|
||||
}
|
||||
}
|
||||
if (substr($m, -1) === '\\') { // comment ends like \*/
|
||||
// begin hack mode and preserve hack
|
||||
$this->_inHack = true;
|
||||
return '/*\\*/';
|
||||
}
|
||||
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
|
||||
// begin hack mode and preserve hack
|
||||
$this->_inHack = true;
|
||||
return '/*/*/';
|
||||
}
|
||||
if ($this->_inHack) {
|
||||
// a regular comment ends hack mode but should be preserved
|
||||
$this->_inHack = false;
|
||||
return '/**/';
|
||||
}
|
||||
// Issue 107: if there's any surrounding whitespace, it may be important, so
|
||||
// replace the comment with a single space
|
||||
return $hasSurroundingWs // remove all other comments
|
||||
? ' '
|
||||
: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a font-family listing and return a replacement
|
||||
*
|
||||
* @param array $m regex matches
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _fontFamilyCB($m)
|
||||
{
|
||||
// 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);
|
||||
$out = 'font-family:';
|
||||
while (null !== ($piece = array_shift($pieces))) {
|
||||
if ($piece[0] !== '"' && $piece[0] !== "'") {
|
||||
$piece = preg_replace('/\\s+/', ' ', $piece);
|
||||
$piece = preg_replace('/\\s?,\\s?/', ',', $piece);
|
||||
}
|
||||
$out .= $piece;
|
||||
}
|
||||
return $out . $m[2];
|
||||
}
|
||||
}
|
||||
290
typo3conf/ext/scriptmerger/Resources/Minify/CSS/UriRewriter.php
Normal file
290
typo3conf/ext/scriptmerger/Resources/Minify/CSS/UriRewriter.php
Normal file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Minify_CSS_UriRewriter
|
||||
* @package Minify
|
||||
*/
|
||||
|
||||
/**
|
||||
* Rewrite file-relative URIs as root-relative in CSS files
|
||||
*
|
||||
* @package Minify
|
||||
* @author Stephen Clay <steve@mrclay.org>
|
||||
*/
|
||||
class Minify_CSS_UriRewriter {
|
||||
|
||||
/**
|
||||
* rewrite() and rewriteRelative() append debugging information here
|
||||
* @var string
|
||||
*/
|
||||
public static $debugText = '';
|
||||
|
||||
/**
|
||||
* In CSS content, rewrite file relative URIs as root relative
|
||||
*
|
||||
* @param string $css
|
||||
*
|
||||
* @param string $currentDir The directory of the current CSS file.
|
||||
*
|
||||
* @param string $docRoot The document root of the web site in which
|
||||
* the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
|
||||
*
|
||||
* @param array $symlinks (default = array()) If the CSS file is stored in
|
||||
* a symlink-ed directory, provide an array of link paths to
|
||||
* target paths, where the link paths are within the document root. Because
|
||||
* paths need to be normalized for this to work, use "//" to substitute
|
||||
* the doc root in the link paths (the array keys). E.g.:
|
||||
* <code>
|
||||
* array('//symlink' => '/real/target/path') // unix
|
||||
* array('//static' => 'D:\\staticStorage') // Windows
|
||||
* </code>
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array())
|
||||
{
|
||||
self::$_docRoot = self::_realpath(
|
||||
$docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
|
||||
);
|
||||
self::$_currentDir = self::_realpath($currentDir);
|
||||
self::$_symlinks = array();
|
||||
|
||||
// normalize symlinks
|
||||
foreach ($symlinks as $link => $target) {
|
||||
$link = ($link === '//')
|
||||
? self::$_docRoot
|
||||
: str_replace('//', self::$_docRoot . '/', $link);
|
||||
$link = strtr($link, '/', DIRECTORY_SEPARATOR);
|
||||
self::$_symlinks[$link] = self::_realpath($target);
|
||||
}
|
||||
|
||||
self::$debugText .= "docRoot : " . self::$_docRoot . "\n"
|
||||
. "currentDir : " . self::$_currentDir . "\n";
|
||||
if (self::$_symlinks) {
|
||||
self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
|
||||
}
|
||||
self::$debugText .= "\n";
|
||||
|
||||
$css = self::_trimUrls($css);
|
||||
|
||||
// rewrite
|
||||
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* In CSS content, prepend a path to relative URIs
|
||||
*
|
||||
* @param string $css
|
||||
*
|
||||
* @param string $path The path to prepend.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function prepend($css, $path)
|
||||
{
|
||||
self::$_prependPath = $path;
|
||||
|
||||
$css = self::_trimUrls($css);
|
||||
|
||||
// append
|
||||
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
|
||||
self::$_prependPath = null;
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a root relative URI from a file relative URI
|
||||
*
|
||||
* <code>
|
||||
* Minify_CSS_UriRewriter::rewriteRelative(
|
||||
* '../img/hello.gif'
|
||||
* , '/home/user/www/css' // path of CSS file
|
||||
* , '/home/user/www' // doc root
|
||||
* );
|
||||
* // returns '/img/hello.gif'
|
||||
*
|
||||
* // example where static files are stored in a symlinked directory
|
||||
* Minify_CSS_UriRewriter::rewriteRelative(
|
||||
* 'hello.gif'
|
||||
* , '/var/staticFiles/theme'
|
||||
* , '/home/user/www'
|
||||
* , array('/home/user/www/static' => '/var/staticFiles')
|
||||
* );
|
||||
* // returns '/static/theme/hello.gif'
|
||||
* </code>
|
||||
*
|
||||
* @param string $uri file relative URI
|
||||
*
|
||||
* @param string $realCurrentDir realpath of the current file's directory.
|
||||
*
|
||||
* @param string $realDocRoot realpath of the site document root.
|
||||
*
|
||||
* @param array $symlinks (default = array()) If the file is stored in
|
||||
* a symlink-ed directory, provide an array of link paths to
|
||||
* real target paths, where the link paths "appear" to be within the document
|
||||
* root. E.g.:
|
||||
* <code>
|
||||
* array('/home/foo/www/not/real/path' => '/real/target/path') // unix
|
||||
* array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
|
||||
* </code>
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
|
||||
{
|
||||
// prepend path with current dir separator (OS-independent)
|
||||
$path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)
|
||||
. DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
|
||||
|
||||
self::$debugText .= "file-relative URI : {$uri}\n"
|
||||
. "path prepended : {$path}\n";
|
||||
|
||||
// "unresolve" a symlink back to doc root
|
||||
foreach ($symlinks as $link => $target) {
|
||||
if (0 === strpos($path, $target)) {
|
||||
// replace $target with $link
|
||||
$path = $link . substr($path, strlen($target));
|
||||
|
||||
self::$debugText .= "symlink unresolved : {$path}\n";
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
// strip doc root
|
||||
$path = substr($path, strlen($realDocRoot));
|
||||
|
||||
self::$debugText .= "docroot stripped : {$path}\n";
|
||||
|
||||
// fix to root-relative URI
|
||||
|
||||
$uri = strtr($path, '/\\', '//');
|
||||
|
||||
$uri = self::removeDots($uri);
|
||||
|
||||
self::$debugText .= "traversals removed : {$uri}\n\n";
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove instances of "./" and "../" where possible from a root-relative URI
|
||||
* @param string $uri
|
||||
* @return string
|
||||
*/
|
||||
public static function removeDots($uri)
|
||||
{
|
||||
$uri = str_replace('/./', '/', $uri);
|
||||
// inspired by patch from Oleg Cherniy
|
||||
do {
|
||||
$uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
|
||||
} while ($changed);
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines which class to call as part of callbacks, change this
|
||||
* if you extend Minify_CSS_UriRewriter
|
||||
* @var string
|
||||
*/
|
||||
protected static $className = 'Minify_CSS_UriRewriter';
|
||||
|
||||
/**
|
||||
* Get realpath with any trailing slash removed. If realpath() fails,
|
||||
* just remove the trailing slash.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return mixed path with no trailing slash
|
||||
*/
|
||||
protected static function _realpath($path)
|
||||
{
|
||||
$realPath = realpath($path);
|
||||
if ($realPath !== false) {
|
||||
$path = $realPath;
|
||||
}
|
||||
return rtrim($path, '/\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string directory of this stylesheet
|
||||
*/
|
||||
private static $_currentDir = '';
|
||||
|
||||
/**
|
||||
* @var string DOC_ROOT
|
||||
*/
|
||||
private static $_docRoot = '';
|
||||
|
||||
/**
|
||||
* @var array directory replacements to map symlink targets back to their
|
||||
* source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
|
||||
*/
|
||||
private static $_symlinks = array();
|
||||
|
||||
/**
|
||||
* @var string path to prepend
|
||||
*/
|
||||
private static $_prependPath = null;
|
||||
|
||||
private static function _trimUrls($css)
|
||||
{
|
||||
return preg_replace('/
|
||||
url\\( # url(
|
||||
\\s*
|
||||
([^\\)]+?) # 1 = URI (assuming does not contain ")")
|
||||
\\s*
|
||||
\\) # )
|
||||
/x', 'url($1)', $css);
|
||||
}
|
||||
|
||||
private static function _processUriCB($m)
|
||||
{
|
||||
// $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
||||
$isImport = ($m[0][0] === '@');
|
||||
// determine URI and the quote character (if any)
|
||||
if ($isImport) {
|
||||
$quoteChar = $m[1];
|
||||
$uri = $m[2];
|
||||
} else {
|
||||
// $m[1] is either quoted or not
|
||||
$quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
|
||||
? $m[1][0]
|
||||
: '';
|
||||
$uri = ($quoteChar === '')
|
||||
? $m[1]
|
||||
: substr($m[1], 1, strlen($m[1]) - 2);
|
||||
}
|
||||
// analyze URI
|
||||
if ('/' !== $uri[0] // root-relative
|
||||
&& false === strpos($uri, '//') // protocol (non-data)
|
||||
&& 0 !== strpos($uri, 'data:') // data protocol
|
||||
) {
|
||||
// URI is file-relative: rewrite depending on options
|
||||
if (self::$_prependPath === null) {
|
||||
$uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
|
||||
} else {
|
||||
$uri = self::$_prependPath . $uri;
|
||||
if ($uri[0] === '/') {
|
||||
$root = '';
|
||||
$rootRelative = $uri;
|
||||
$uri = $root . self::removeDots($rootRelative);
|
||||
} elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) {
|
||||
$root = $m[1];
|
||||
$rootRelative = substr($uri, strlen($root));
|
||||
$uri = $root . self::removeDots($rootRelative);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $isImport
|
||||
? "@import {$quoteChar}{$uri}{$quoteChar}"
|
||||
: "url({$quoteChar}{$uri}{$quoteChar})";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Minify_CommentPreserver
|
||||
* @package Minify
|
||||
*/
|
||||
|
||||
/**
|
||||
* Process a string in pieces preserving C-style comments that begin with "/*!"
|
||||
*
|
||||
* @package Minify
|
||||
* @author Stephen Clay <steve@mrclay.org>
|
||||
*/
|
||||
class Minify_CommentPreserver {
|
||||
|
||||
/**
|
||||
* String to be prepended to each preserved comment
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $prepend = "\n";
|
||||
|
||||
/**
|
||||
* String to be appended to each preserved comment
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $append = "\n";
|
||||
|
||||
/**
|
||||
* Process a string outside of C-style comments that begin with "/*!"
|
||||
*
|
||||
* On each non-empty string outside these comments, the given processor
|
||||
* function will be called. The comments will be surrounded by
|
||||
* Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append.
|
||||
*
|
||||
* @param string $content
|
||||
* @param callback $processor function
|
||||
* @param array $args array of extra arguments to pass to the processor
|
||||
* function (default = array())
|
||||
* @return string
|
||||
*/
|
||||
public static function process($content, $processor, $args = array())
|
||||
{
|
||||
$ret = '';
|
||||
while (true) {
|
||||
list($beforeComment, $comment, $afterComment) = self::_nextComment($content);
|
||||
if ('' !== $beforeComment) {
|
||||
$callArgs = $args;
|
||||
array_unshift($callArgs, $beforeComment);
|
||||
$ret .= call_user_func_array($processor, $callArgs);
|
||||
}
|
||||
if (false === $comment) {
|
||||
break;
|
||||
}
|
||||
$ret .= $comment;
|
||||
$content = $afterComment;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract comments that YUI Compressor preserves.
|
||||
*
|
||||
* @param string $in input
|
||||
*
|
||||
* @return array 3 elements are returned. If a YUI comment is found, the
|
||||
* 2nd element is the comment and the 1st and 3rd are the surrounding
|
||||
* strings. If no comment is found, the entire string is returned as the
|
||||
* 1st element and the other two are false.
|
||||
*/
|
||||
private static function _nextComment($in)
|
||||
{
|
||||
if (
|
||||
false === ($start = strpos($in, '/*!'))
|
||||
|| false === ($end = strpos($in, '*/', $start + 3))
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
189
typo3conf/ext/scriptmerger/Resources/Minify/ImportProcessor.php
Normal file
189
typo3conf/ext/scriptmerger/Resources/Minify/ImportProcessor.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Minify_ImportProcessor
|
||||
* @package Minify
|
||||
*/
|
||||
|
||||
/**
|
||||
* Linearize a CSS/JS file by including content specified by CSS import
|
||||
* declarations. In CSS files, relative URIs are fixed.
|
||||
*
|
||||
* @imports will be processed regardless of where they appear in the source
|
||||
* files; i.e. @imports commented out or in string content will still be
|
||||
* processed!
|
||||
*
|
||||
* This has a unit test but should be considered "experimental".
|
||||
*
|
||||
* @package Minify
|
||||
* @author Stephen Clay <steve@mrclay.org>
|
||||
*/
|
||||
class Minify_ImportProcessor {
|
||||
|
||||
public static $filesIncluded = array();
|
||||
|
||||
public static function process($file)
|
||||
{
|
||||
self::$filesIncluded = array();
|
||||
self::$_isCss = (strtolower(substr($file, -4)) === '.css');
|
||||
$obj = new Minify_ImportProcessor(dirname($file));
|
||||
return $obj->_getContent($file);
|
||||
}
|
||||
|
||||
// allows callback funcs to know the current directory
|
||||
private $_currentDir = NULL;
|
||||
|
||||
// allows _importCB to write the fetched content back to the obj
|
||||
private $_importedContent = '';
|
||||
|
||||
private static $_isCss = NULL;
|
||||
|
||||
private function __construct($currentDir)
|
||||
{
|
||||
$this->_currentDir = $currentDir;
|
||||
}
|
||||
|
||||
// ##################### BEGIN TYPO3 modification
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $extensionConfiguration = array();
|
||||
|
||||
// ##################### END TYPO3 modification
|
||||
|
||||
private function _getContent($file)
|
||||
{
|
||||
$file = realpath($file);
|
||||
if (! $file
|
||||
|| in_array($file, self::$filesIncluded)
|
||||
|| FALSE === ($content = @file_get_contents($file))
|
||||
) {
|
||||
// file missing, already included, or failed read
|
||||
return '';
|
||||
}
|
||||
self::$filesIncluded[] = $file;
|
||||
$this->_currentDir = dirname($file);
|
||||
|
||||
// ##################### BEGIN TYPO3 modification
|
||||
if (strpos($this->_currentDir, realpath(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT'))) === FALSE) {
|
||||
$realPathToTYPO3 = str_replace('typo3/', '', realpath(PATH_typo3));
|
||||
$this->_currentDir = str_replace($realPathToTYPO3, '', $this->_currentDir);
|
||||
$this->_currentDir = realpath(PATH_site) . '/typo3' . $this->_currentDir;
|
||||
}
|
||||
// ##################### END TYPO3 modification
|
||||
|
||||
// remove UTF-8 BOM if present
|
||||
if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) {
|
||||
$content = substr($content, 3);
|
||||
}
|
||||
// ensure uniform EOLs
|
||||
$content = str_replace("\r\n", "\n", $content);
|
||||
|
||||
// process @imports
|
||||
$content = preg_replace_callback(
|
||||
'/
|
||||
@import\\s+
|
||||
(?:url\\(\\s*)? # maybe url(
|
||||
[\'"]? # maybe quote
|
||||
(.*?) # 1 = URI
|
||||
[\'"]? # maybe end quote
|
||||
(?:\\s*\\))? # maybe )
|
||||
([a-zA-Z,\\s]*)? # 2 = media list
|
||||
; # end token
|
||||
/x'
|
||||
,array($this, '_importCB')
|
||||
,$content
|
||||
);
|
||||
|
||||
if (self::$_isCss) {
|
||||
// rewrite remaining relative URIs
|
||||
$content = preg_replace_callback(
|
||||
'/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
||||
,array($this, '_urlCB')
|
||||
,$content
|
||||
);
|
||||
}
|
||||
|
||||
return $this->_importedContent . $content;
|
||||
}
|
||||
|
||||
private function _importCB($m)
|
||||
{
|
||||
$url = $m[1];
|
||||
$mediaList = preg_replace('/\\s+/', '', $m[2]);
|
||||
|
||||
if (strpos($url, '://') > 0) {
|
||||
// protocol, leave in place for CSS, comment for JS
|
||||
return self::$_isCss
|
||||
? $m[0]
|
||||
: "/* Minify_ImportProcessor will not include remote content */";
|
||||
}
|
||||
if ('/' === $url[0]) {
|
||||
// protocol-relative or root path
|
||||
$url = ltrim($url, '/');
|
||||
$file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR
|
||||
. strtr($url, '/', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// relative to current path
|
||||
$file = $this->_currentDir . DIRECTORY_SEPARATOR
|
||||
. strtr($url, '/', DIRECTORY_SEPARATOR);
|
||||
}
|
||||
$obj = new Minify_ImportProcessor(dirname($file));
|
||||
$content = $obj->_getContent($file);
|
||||
if ('' === $content) {
|
||||
// failed. leave in place for CSS, comment for JS
|
||||
return self::$_isCss
|
||||
? $m[0]
|
||||
: "/* Minify_ImportProcessor could not fetch '{$file}' */";
|
||||
}
|
||||
return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList))
|
||||
? $content
|
||||
: "@media {$mediaList} {\n{$content}\n}\n";
|
||||
}
|
||||
|
||||
private function _urlCB($m)
|
||||
{
|
||||
// $m[1] is either quoted or not
|
||||
$quote = ($m[1][0] === "'" || $m[1][0] === '"')
|
||||
? $m[1][0]
|
||||
: '';
|
||||
$url = ($quote === '')
|
||||
? $m[1]
|
||||
: substr($m[1], 1, strlen($m[1]) - 2);
|
||||
if ('/' !== $url[0]) {
|
||||
if (strpos($url, '//') > 0) {
|
||||
// probably starts with protocol, do not alter
|
||||
// ##################### BEGIN TYPO3 modification
|
||||
} elseif (strpos($url, 'data:image') !== FALSE) {
|
||||
// probably starts with an inline image, do not alter
|
||||
// ##################### END TYPO3 modification
|
||||
} else {
|
||||
// prepend path with current dir separator (OS-independent)
|
||||
$path = $this->_currentDir
|
||||
. DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR);
|
||||
// strip doc root
|
||||
$path = substr($path, strlen(realpath($_SERVER['DOCUMENT_ROOT'])));
|
||||
// fix to absolute URL
|
||||
$url = strtr($path, '/\\', '//');
|
||||
// remove /./ and /../ where possible
|
||||
$url = str_replace('/./', '/', $url);
|
||||
// inspired by patch from Oleg Cherniy
|
||||
do {
|
||||
$url = preg_replace('@/[^/]+/\\.\\./@', '/', $url, 1, $changed);
|
||||
} while ($changed);
|
||||
}
|
||||
}
|
||||
|
||||
// ##################### BEGIN TYPO3 modification
|
||||
|
||||
if (trim(self::$extensionConfiguration['css.']['postUrlProcessing.']['pattern']) !== '') {
|
||||
$pattern = self::$extensionConfiguration['css.']['postUrlProcessing.']['pattern'];
|
||||
$replacement = self::$extensionConfiguration['css.']['postUrlProcessing.']['replacement'];
|
||||
$url = preg_replace($pattern, $replacement, $url);
|
||||
}
|
||||
|
||||
// ##################### END TYPO3 modification
|
||||
|
||||
return "url({$quote}{$url}{$quote})";
|
||||
}
|
||||
}
|
||||
386
typo3conf/ext/scriptmerger/Resources/jsmin.php
Normal file
386
typo3conf/ext/scriptmerger/Resources/jsmin.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
/**
|
||||
* jsmin.php - PHP implementation of Douglas Crockford's JSMin.
|
||||
*
|
||||
* This is pretty much a direct port of jsmin.c to PHP with just a few
|
||||
* PHP-specific performance tweaks. Also, whereas jsmin.c reads from stdin and
|
||||
* outputs to stdout, this library accepts a string as input and returns another
|
||||
* string as output.
|
||||
*
|
||||
* PHP 5 or higher is required.
|
||||
*
|
||||
* Permission is hereby granted to use this version of the library under the
|
||||
* same terms as jsmin.c, which has the following license:
|
||||
*
|
||||
* --
|
||||
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* The Software shall be used for Good, not Evil.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* --
|
||||
*
|
||||
* @package JSMin
|
||||
* @author Ryan Grove <ryan@wonko.com>
|
||||
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
|
||||
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
|
||||
* @copyright 2012 Adam Goforth <aag@adamgoforth.com> (Updates)
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version 1.1.2 (2012-05-01)
|
||||
* @link https://github.com/rgrove/jsmin-php
|
||||
*/
|
||||
|
||||
class JSMin {
|
||||
const ORD_LF = 10;
|
||||
const ORD_SPACE = 32;
|
||||
const ACTION_KEEP_A = 1;
|
||||
const ACTION_DELETE_A = 2;
|
||||
const ACTION_DELETE_A_B = 3;
|
||||
|
||||
protected $a = '';
|
||||
protected $b = '';
|
||||
protected $input = '';
|
||||
protected $inputIndex = 0;
|
||||
protected $inputLength = 0;
|
||||
protected $lookAhead = null;
|
||||
protected $output = '';
|
||||
|
||||
// -- Public Static Methods --------------------------------------------------
|
||||
|
||||
/**
|
||||
* Minify Javascript
|
||||
*
|
||||
* @uses __construct()
|
||||
* @uses min()
|
||||
* @param string $js Javascript to be minified
|
||||
* @return string
|
||||
*/
|
||||
public static function minify($js) {
|
||||
$jsmin = new JSMin($js);
|
||||
return $jsmin->min();
|
||||
}
|
||||
|
||||
// -- Public Instance Methods ------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $input Javascript to be minified
|
||||
*/
|
||||
public function __construct($input) {
|
||||
$this->input = str_replace("\r\n", "\n", $input);
|
||||
$this->inputLength = strlen($this->input);
|
||||
}
|
||||
|
||||
// -- Protected Instance Methods ---------------------------------------------
|
||||
|
||||
/**
|
||||
* Action -- do something! What to do is determined by the $command argument.
|
||||
*
|
||||
* action treats a string as a single character. Wow!
|
||||
* action recognizes a regular expression if it is preceded by ( or , or =.
|
||||
*
|
||||
* @uses next()
|
||||
* @uses get()
|
||||
* @throws JSMinException If parser errors are found:
|
||||
* - Unterminated string literal
|
||||
* - Unterminated regular expression set in regex literal
|
||||
* - Unterminated regular expression literal
|
||||
* @param int $command One of class constants:
|
||||
* ACTION_KEEP_A Output A. Copy B to A. Get the next B.
|
||||
* ACTION_DELETE_A Copy B to A. Get the next B. (Delete A).
|
||||
* ACTION_DELETE_A_B Get the next B. (Delete B).
|
||||
*/
|
||||
protected function action($command) {
|
||||
switch($command) {
|
||||
case self::ACTION_KEEP_A:
|
||||
$this->output .= $this->a;
|
||||
|
||||
case self::ACTION_DELETE_A:
|
||||
$this->a = $this->b;
|
||||
|
||||
if ($this->a === "'" || $this->a === '"') {
|
||||
for (;;) {
|
||||
$this->output .= $this->a;
|
||||
$this->a = $this->get();
|
||||
|
||||
if ($this->a === $this->b) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ord($this->a) <= self::ORD_LF) {
|
||||
throw new JSMinException('Unterminated string literal.');
|
||||
}
|
||||
|
||||
if ($this->a === '\\') {
|
||||
$this->output .= $this->a;
|
||||
$this->a = $this->get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case self::ACTION_DELETE_A_B:
|
||||
$this->b = $this->next();
|
||||
|
||||
if ($this->b === '/' && (
|
||||
$this->a === '(' || $this->a === ',' || $this->a === '=' ||
|
||||
$this->a === ':' || $this->a === '[' || $this->a === '!' ||
|
||||
$this->a === '&' || $this->a === '|' || $this->a === '?' ||
|
||||
$this->a === '{' || $this->a === '}' || $this->a === ';' ||
|
||||
$this->a === "\n" )) {
|
||||
|
||||
$this->output .= $this->a . $this->b;
|
||||
|
||||
for (;;) {
|
||||
$this->a = $this->get();
|
||||
|
||||
if ($this->a === '[') {
|
||||
/*
|
||||
inside a regex [...] set, which MAY contain a '/' itself. Example: mootools Form.Validator near line 460:
|
||||
return Form.Validator.getValidator('IsEmpty').test(element) || (/^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]\.?){0,63}[a-z0-9!#$%&'*+/=?^_`{|}~-]@(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\])$/i).test(element.get('value'));
|
||||
*/
|
||||
for (;;) {
|
||||
$this->output .= $this->a;
|
||||
$this->a = $this->get();
|
||||
|
||||
if ($this->a === ']') {
|
||||
break;
|
||||
} elseif ($this->a === '\\') {
|
||||
$this->output .= $this->a;
|
||||
$this->a = $this->get();
|
||||
} elseif (ord($this->a) <= self::ORD_LF) {
|
||||
throw new JSMinException('Unterminated regular expression set in regex literal.');
|
||||
}
|
||||
}
|
||||
} elseif ($this->a === '/') {
|
||||
break;
|
||||
} elseif ($this->a === '\\') {
|
||||
$this->output .= $this->a;
|
||||
$this->a = $this->get();
|
||||
} elseif (ord($this->a) <= self::ORD_LF) {
|
||||
throw new JSMinException('Unterminated regular expression literal.');
|
||||
}
|
||||
|
||||
$this->output .= $this->a;
|
||||
}
|
||||
|
||||
$this->b = $this->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next char. Convert ctrl char to space.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function get() {
|
||||
$c = $this->lookAhead;
|
||||
$this->lookAhead = null;
|
||||
|
||||
if ($c === null) {
|
||||
if ($this->inputIndex < $this->inputLength) {
|
||||
$c = substr($this->input, $this->inputIndex, 1);
|
||||
$this->inputIndex += 1;
|
||||
} else {
|
||||
$c = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($c === "\r") {
|
||||
return "\n";
|
||||
}
|
||||
|
||||
if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
|
||||
return $c;
|
||||
}
|
||||
|
||||
return ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is $c a letter, digit, underscore, dollar sign, or non-ASCII character.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isAlphaNum($c) {
|
||||
return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform minification, return result
|
||||
*
|
||||
* @uses action()
|
||||
* @uses isAlphaNum()
|
||||
* @uses get()
|
||||
* @uses peek()
|
||||
* @return string
|
||||
*/
|
||||
protected function min() {
|
||||
if (0 == strncmp($this->peek(), "\xef", 1)) {
|
||||
$this->get();
|
||||
$this->get();
|
||||
$this->get();
|
||||
}
|
||||
|
||||
$this->a = "\n";
|
||||
$this->action(self::ACTION_DELETE_A_B);
|
||||
|
||||
while ($this->a !== null) {
|
||||
switch ($this->a) {
|
||||
case ' ':
|
||||
if ($this->isAlphaNum($this->b)) {
|
||||
$this->action(self::ACTION_KEEP_A);
|
||||
} else {
|
||||
$this->action(self::ACTION_DELETE_A);
|
||||
}
|
||||
break;
|
||||
|
||||
case "\n":
|
||||
switch ($this->b) {
|
||||
case '{':
|
||||
case '[':
|
||||
case '(':
|
||||
case '+':
|
||||
case '-':
|
||||
case '!':
|
||||
case '~':
|
||||
$this->action(self::ACTION_KEEP_A);
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
$this->action(self::ACTION_DELETE_A_B);
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($this->isAlphaNum($this->b)) {
|
||||
$this->action(self::ACTION_KEEP_A);
|
||||
}
|
||||
else {
|
||||
$this->action(self::ACTION_DELETE_A);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
switch ($this->b) {
|
||||
case ' ':
|
||||
if ($this->isAlphaNum($this->a)) {
|
||||
$this->action(self::ACTION_KEEP_A);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->action(self::ACTION_DELETE_A_B);
|
||||
break;
|
||||
|
||||
case "\n":
|
||||
switch ($this->a) {
|
||||
case '}':
|
||||
case ']':
|
||||
case ')':
|
||||
case '+':
|
||||
case '-':
|
||||
case '"':
|
||||
case "'":
|
||||
$this->action(self::ACTION_KEEP_A);
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($this->isAlphaNum($this->a)) {
|
||||
$this->action(self::ACTION_KEEP_A);
|
||||
}
|
||||
else {
|
||||
$this->action(self::ACTION_DELETE_A_B);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->action(self::ACTION_KEEP_A);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next character, skipping over comments. peek() is used to see
|
||||
* if a '/' is followed by a '/' or '*'.
|
||||
*
|
||||
* @uses get()
|
||||
* @uses peek()
|
||||
* @throws JSMinException On unterminated comment.
|
||||
* @return string
|
||||
*/
|
||||
protected function next() {
|
||||
$c = $this->get();
|
||||
|
||||
if ($c === '/') {
|
||||
switch($this->peek()) {
|
||||
case '/':
|
||||
for (;;) {
|
||||
$c = $this->get();
|
||||
|
||||
if (ord($c) <= self::ORD_LF) {
|
||||
return $c;
|
||||
}
|
||||
}
|
||||
|
||||
case '*':
|
||||
$this->get();
|
||||
|
||||
for (;;) {
|
||||
switch($this->get()) {
|
||||
case '*':
|
||||
if ($this->peek() === '/') {
|
||||
$this->get();
|
||||
return ' ';
|
||||
}
|
||||
break;
|
||||
|
||||
case null:
|
||||
throw new JSMinException('Unterminated comment.');
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return $c;
|
||||
}
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next char. If is ctrl character, translate to a space or newline.
|
||||
*
|
||||
* @uses get()
|
||||
* @return string|null
|
||||
*/
|
||||
protected function peek() {
|
||||
$this->lookAhead = $this->get();
|
||||
return $this->lookAhead;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Exceptions ---------------------------------------------------------------
|
||||
class JSMinException extends Exception {}
|
||||
?>
|
||||
2086
typo3conf/ext/scriptmerger/Resources/jsminplus.php
Normal file
2086
typo3conf/ext/scriptmerger/Resources/jsminplus.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user