[FEATURE] Speedup backend and allow compression in frontend
authorKai Vogel <kai.vogel@speedprogs.de>
Sat, 25 Jun 2011 08:58:05 +0000 (10:58 +0200)
committerSteffen Gebert <steffen.gebert@typo3.org>
Thu, 28 Jul 2011 06:40:30 +0000 (08:40 +0200)
This patch improves the determination of relative paths of include
files, concatenates the jsFiles and jsFooterFiles in backend by
default to speedup the backend and provides a default compressor
for the frontend.

Change-Id: I569d285cc12c7eec808094903d5c8ee2e749f9ec
Resolves: #27694
Reviewed-on: http://review.typo3.org/2924
Reviewed-by: Stefan Neufeind
Tested-by: Stefan Neufeind
Reviewed-by: Steffen Gebert
Tested-by: Steffen Gebert
Reviewed-by: Georg Ringer
NEWS.txt
t3lib/class.t3lib_compressor.php
t3lib/class.t3lib_pagerenderer.php
typo3/sysext/cms/tslib/class.tslib_fe.php
typo3/sysext/cms/tslib/class.tslib_pagegen.php

index 616eb41..402072a 100644 (file)
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -41,7 +41,7 @@ technical details see ChangeLog included in the typo3_src package.
 General
 -------------------------------------------------------------------------------
 
 General
 -------------------------------------------------------------------------------
 
-...
+-...
 
 Removed functionality
 -------------------------------------------------------------------------------
 
 Removed functionality
 -------------------------------------------------------------------------------
@@ -110,7 +110,11 @@ RTEhtmlarea
 Other improvements to the Backend
 -------------------------------------------------------------------------------
 
 Other improvements to the Backend
 -------------------------------------------------------------------------------
 
-...
+* Backend speedup with merged JavaScript files
+
+All jsLibs, jsFiles and jsFooterFiles are now concatenated by default into
+single files depending on type and section (header / footer). This reduces
+the webserver requests approximately by one third.
 
 -------------------------------------------------------------------------------
 Skin / Backend UI
 
 -------------------------------------------------------------------------------
 Skin / Backend UI
@@ -163,7 +167,20 @@ Extension Development
 TypoScript / Frontend
 -------------------------------------------------------------------------------
 
 TypoScript / Frontend
 -------------------------------------------------------------------------------
 
-...
+* Compression and concatenation of CSS and JavaScript files
+
+New builtin minifcation routines can now be used with config.minifyCSS and
+minifyJS to reduce the file size of all files configured in page.includeCSS
+and includeJS. Single files can be excluded from compression using the new
+option disableCompression. Additionally, files can be concatenated into a
+single file using config.concatenateCss and concatenateJs. Please note that
+there is only limited support for @charset, @import and @namespace statements
+in concatenated CSS files. Furthermore, if minifyCSS/JS is enabled,
+$TYPO3_CONF_VARS[FE][compressionLevel] now also affects CSS and JS files in
+frontend and applies GZIP compression. This requires the same options as for
+[BE][compressionLevel] to be set in .htaccess.
+
+
 
 Database API / DBAL
 -------------------------------------------------------------------------------
 
 Database API / DBAL
 -------------------------------------------------------------------------------
index 666c717..a00435d 100644 (file)
@@ -2,7 +2,8 @@
 /***************************************************************
  *  Copyright notice
  *
 /***************************************************************
  *  Copyright notice
  *
- *  (c) 2010-2011 Steffen Gebert (steffen@steffen-gebert.de)
+ *  (c) 2010-2011 Steffen Gebert <steffen@steffen-gebert.de>
+ *  (c) 2011 Kai Vogel <kai.vogel@speedprogs.de>
  *  All rights reserved
  *
  *  This script is part of the TYPO3 project. The TYPO3 project is
  *  All rights reserved
  *
  *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -37,6 +38,12 @@ class t3lib_Compressor {
 
        protected $targetDirectory = 'typo3temp/compressor/';
 
 
        protected $targetDirectory = 'typo3temp/compressor/';
 
+       protected $relativePath = '';
+
+       protected $rootPath = '';
+
+       protected $backPath = '';
+
                // gzipped versions are only created if $TYPO3_CONF_VARS[TYPO3_MODE]['compressionLevel'] is set
        protected $createGzipped = FALSE;
                // default compression level is -1
                // gzipped versions are only created if $TYPO3_CONF_VARS[TYPO3_MODE]['compressionLevel'] is set
        protected $createGzipped = FALSE;
                // default compression level is -1
@@ -54,7 +61,6 @@ class t3lib_Compressor {
         * Constructor
         */
        public function __construct() {
         * Constructor
         */
        public function __construct() {
-
                        // we check for existance of our targetDirectory
                if (!is_dir(PATH_site . $this->targetDirectory)) {
                        t3lib_div::mkdir(PATH_site . $this->targetDirectory);
                        // we check for existance of our targetDirectory
                if (!is_dir(PATH_site . $this->targetDirectory)) {
                        t3lib_div::mkdir(PATH_site . $this->targetDirectory);
@@ -79,44 +85,126 @@ class t3lib_Compressor {
                                $this->gzipCompressionLevel = intval($compressionLevel);
                        }
                }
                                $this->gzipCompressionLevel = intval($compressionLevel);
                        }
                }
+
+               $this->setInitialPaths();
        }
 
        /**
        }
 
        /**
-        * Concatenates the cssFiles
+        * Sets initial values for paths.
         *
         *
-        * Options:
-        *   baseDirectories            If set, only include files below one of the base directories
+        * @return void
+        */
+       public function setInitialPaths() {
+               $this->setInitialRelativePath();
+               $this->setInitialRootPath();
+               $this->setInitialBackPath();
+       }
+
+       /**
+        * Sets relative back path
+        *
+        * @return void
+        */
+       protected function setInitialBackPath() {
+               $backPath = (TYPO3_MODE === 'BE' ? $GLOBALS['BACK_PATH'] : '');
+               $this->setBackPath($backPath);
+       }
+
+       /**
+        * Sets absolute path to working directory
+        *
+        * @return void
+        */
+       protected function setInitialRootPath() {
+               $rootPath = (TYPO3_MODE === 'BE' ? PATH_typo3 : PATH_site);
+               $this->setRootPath($rootPath);
+       }
+
+       /**
+        * Sets relative path to PATH_site
+        *
+        * @return void
+        */
+       protected function setInitialRelativePath() {
+               $relativePath = (TYPO3_MODE === 'BE' ? $GLOBALS['BACK_PATH'] . '../' : '');
+               $this->setRelativePath($relativePath);
+       }
+
+       /**
+        * Sets relative path to PATH_site
+        *
+        * @param string $relativePath Relative path to site root
+        * @return void
+        */
+       public function setRelativePath($relativePath) {
+               if (is_string($relativePath)) {
+                       $this->relativePath = $relativePath;
+               }
+       }
+
+       /**
+        * Sets absolute path to working directory
+        *
+        * @param string $rootPath Absolute path
+        * @return void
+        */
+       public function setRootPath($rootPath) {
+               if (is_string($rootPath)) {
+                       $this->rootPath = $rootPath;
+               }
+       }
+
+       /**
+        * Sets relative back path
         *
         *
-        * @param       array   $cssFiles               CSS files to process
-        * @param       array   $options                Additional options
-        * @return      array   CSS files
+        * @param string $backPath Back path
+        * @return void
         */
         */
-       public function concatenateCssFiles(array $cssFiles, $options = array()) {
+       public function setBackPath($backPath) {
+               if (is_string($backPath)) {
+                       $this->backPath = $backPath;
+               }
+       }
 
 
+       /**
+        * Concatenates the Stylesheet files
+        *
+        * Options:
+        *   baseDirectories If set, only include files below one of the base directories
+        *
+        * @param array $cssFiles CSS files to process
+        * @param array $options Additional options
+        * @return array CSS files
+        */
+       public function concatenateCssFiles(array $cssFiles, array $options = array()) {
                $filesToInclude = array();
                $filesToInclude = array();
-               foreach ($cssFiles as $filename => $fileOptions) {
-                               // we remove BACK_PATH from $filename, so make it relative to TYPO3_mainDir
-                       $filenameFromMainDir = $this->getFilenameFromMainDir($filename);
+               foreach ($cssFiles as $key => $fileOptions) {
+                               // no concatenation allowed for this file, so continue
+                       if (!empty($fileOptions['excludeFromConcatenation'])) {
+                               continue;
+                       }
+                               // we remove BACK_PATH from $filename, so make it relative to root path
+                       $filenameFromMainDir = $this->getFilenameFromMainDir($fileOptions['file']);
                                // if $options['baseDirectories'] set, we only include files below these directories
                        if ((!isset($options['baseDirectories'])
                                // if $options['baseDirectories'] set, we only include files below these directories
                        if ((!isset($options['baseDirectories'])
-                                       || $this->checkBaseDirectory($filenameFromMainDir, array_merge($options['baseDirectories'], array($this->targetDirectory))))
-                                       && ($fileOptions['media'] === 'all')
+                         || $this->checkBaseDirectory($filenameFromMainDir, array_merge($options['baseDirectories'], array($this->targetDirectory))))
+                         && ($fileOptions['media'] === 'all')
                        ) {
                        ) {
-
                                $filesToInclude[] = $filenameFromMainDir;
                                        // remove the file from the incoming file array
                                $filesToInclude[] = $filenameFromMainDir;
                                        // remove the file from the incoming file array
-                               unset($cssFiles[$filename]);
+                               unset($cssFiles[$key]);
                        }
                }
 
                if (count($filesToInclude)) {
                        $targetFile = $this->createMergedCssFile($filesToInclude);
                        }
                }
 
                if (count($filesToInclude)) {
                        $targetFile = $this->createMergedCssFile($filesToInclude);
+                       $targetFileRelative = $this->relativePath . $targetFile;
                        $concatenatedOptions = array(
                        $concatenatedOptions = array(
+                               'file' => $targetFileRelative,
                                'rel' => 'stylesheet',
                                'media' => 'all',
                                'compress' => TRUE,
                        );
                                'rel' => 'stylesheet',
                                'media' => 'all',
                                'compress' => TRUE,
                        );
-                       $targetFileRelative = $GLOBALS['BACK_PATH'] . '../' . $targetFile;
                                // place the merged stylesheet on top of the stylesheets
                        $cssFiles = array_merge(array($targetFileRelative => $concatenatedOptions), $cssFiles);
                }
                                // place the merged stylesheet on top of the stylesheets
                        $cssFiles = array_merge(array($targetFileRelative => $concatenatedOptions), $cssFiles);
                }
@@ -124,68 +212,101 @@ class t3lib_Compressor {
        }
 
        /**
        }
 
        /**
-        * Finds the relative path to a file, relative to the TYPO3_mainDir.
+        * Concatenates the JavaScript files
         *
         *
-        * @param string $filename the name of the file
-        * @return string the path to the file relative to the TYPO3_mainDir
+        * @param array $jsFiles JavaScript files to process
+        * @return array JS files
         */
         */
-       private function getFilenameFromMainDir($filename) {
-                       // if the file exists in the typo3/ folder or the BACK_PATH is empty, just return the $filename
-               if (substr($filename, 0, strlen($GLOBALS['BACK_PATH'])) === $GLOBALS['BACK_PATH']) {
-                       $file = str_replace($GLOBALS['BACK_PATH'], '', $filename);
-                       if (is_file(PATH_typo3 . $file) || empty($GLOBALS['BACK_PATH'])) {
-                               return $file;
+       public function concatenateJsFiles(array $jsFiles) {
+               $filesToInclude = array();
+               foreach ($jsFiles as $key => $fileOptions) {
+                               // invalid section found or no concatenation allowed, so continue
+                       if (empty($fileOptions['section']) || !empty($fileOptions['excludeFromConcatenation'])) {
+                               continue;
                        }
                        }
+                               // we remove BACK_PATH from $filename, so make it relative to root path
+                       $filesToInclude[$fileOptions['section']][] = $this->getFilenameFromMainDir($fileOptions['file']);
+                               // remove the file from the incoming file array
+                       unset($jsFiles[$key]);
                }
 
                }
 
-                       // build the file path relatively to the PATH_site
-               $backPath = str_replace(TYPO3_mainDir, '', $GLOBALS['BACK_PATH']);
-               $file = str_replace($backPath, '', $filename);
-               if (substr($file, 0, 3) === '../') {
-                       $file = t3lib_div::resolveBackPath(PATH_typo3 . $file);
-               } else {
-                       $file = PATH_site . $file;
-               }
-
-                       // check if the file exists, and if so, return the path relative to TYPO3_mainDir
-               if (is_file($file)) {
-                       $mainDirDepth = substr_count(TYPO3_mainDir, '/');
-                       return str_repeat('../', $mainDirDepth) . str_replace(PATH_site, '', $file);
+               if (!empty($filesToInclude)) {
+                       foreach ($filesToInclude as $section => $files) {
+                               $targetFile = $this->createMergedJsFile($files);
+                               $targetFileRelative = $this->relativePath . $targetFile;
+                               $concatenatedOptions = array(
+                                       'file' => $targetFileRelative,
+                                       'type' => 'text/javascript',
+                                       'section' => $section,
+                                       'compress' => TRUE,
+                                       'forceOnTop' => FALSE,
+                                       'allWrap' => '',
+                               );
+                                       // place the merged javascript on top of the JS files
+                               $jsFiles = array_merge(array($targetFileRelative => $concatenatedOptions), $jsFiles);
+                       }
                }
                }
-
-                       // none of above conditions were met, fallback to default behaviour
-               return substr($filename, strlen($GLOBALS['BACK_PATH']));
+               return $jsFiles;
        }
 
        /**
         * Creates a merged CSS file
         *
        }
 
        /**
         * Creates a merged CSS file
         *
-        * @param       array   $filesToInclude         Files which should be merged, paths relative to TYPO3_mainDir
-        * @return      mixed   Filename of the merged file
+        * @param array $filesToInclude Files which should be merged, paths relative to root path
+        * @return mixed Filename of the merged file
         */
        protected function createMergedCssFile(array $filesToInclude) {
         */
        protected function createMergedCssFile(array $filesToInclude) {
+               return $this->createMergedFile($filesToInclude, 'css');
+       }
+
+       /**
+        * Creates a merged JS file
+        *
+        * @param array $filesToInclude Files which should be merged, paths relative to root path
+        * @return mixed Filename of the merged file
+        */
+       protected function createMergedJsFile(array $filesToInclude) {
+               return $this->createMergedFile($filesToInclude, 'js');
+       }
+
+       /**
+        * Creates a merged file with given file type
+        *
+        * @param array $filesToInclude Files which should be merged, paths relative to root path
+        * @param string $type File type
+        * @return mixed Filename of the merged file
+        */
+       protected function createMergedFile(array $filesToInclude, $type = 'css') {
+                       // Get file type
+               $type = strtolower(trim($type, '. '));
+               if (empty($type)) {
+                       throw new Exception('Error in t3lib_Compressor: No valid file type given for merged file', 1308957498);
+               }
+
                        // we add up the filenames, filemtimes and filsizes to later build a checksum over
                        // it and include it in the temporary file name
                $unique = '';
                        // we add up the filenames, filemtimes and filsizes to later build a checksum over
                        // it and include it in the temporary file name
                $unique = '';
-
                foreach ($filesToInclude as $filename) {
                foreach ($filesToInclude as $filename) {
-                       $filepath = t3lib_div::resolveBackPath(PATH_typo3 . $filename);
+                       $filepath = t3lib_div::resolveBackPath($this->rootPath . $filename);
                        $unique .= $filename . filemtime($filepath) . filesize($filepath);
                }
                        $unique .= $filename . filemtime($filepath) . filesize($filepath);
                }
-               $targetFile = $this->targetDirectory . 'merged-' . md5($unique) . '.css';
+               $targetFile = $this->targetDirectory . 'merged-' . md5($unique) . '.' . $type;
 
                        // if the file doesn't already exist, we create it
                if (!file_exists(PATH_site . $targetFile)) {
                        $concatenated = '';
                                // concatenate all the files together
                        foreach ($filesToInclude as $filename) {
 
                        // if the file doesn't already exist, we create it
                if (!file_exists(PATH_site . $targetFile)) {
                        $concatenated = '';
                                // concatenate all the files together
                        foreach ($filesToInclude as $filename) {
-                               $contents = t3lib_div::getUrl(t3lib_div::resolveBackPath(PATH_typo3 . $filename));
+                               $contents = t3lib_div::getUrl(t3lib_div::resolveBackPath($this->rootPath . $filename));
                                        // only fix paths if files aren't already in typo3temp (already processed)
                                        // only fix paths if files aren't already in typo3temp (already processed)
-                               if (!t3lib_div::isFirstPartOfStr($filename, $this->targetDirectory)) {
-                                       $concatenated .= $this->cssFixRelativeUrlPaths($contents, dirname($filename) . '/');
-                               } else {
-                                       $concatenated .= $contents;
+                               if ($type === 'css' && !t3lib_div::isFirstPartOfStr($filename, $this->targetDirectory)) {
+                                       $contents = $this->cssFixRelativeUrlPaths($contents, dirname($filename) . '/');
                                }
                                }
+                               $concatenated .= LF . $contents;
+                       }
+                               // move @charset, @import and @namespace statements to top of new file
+                       if ($type === 'css') {
+                               $concatenated = $this->cssFixStatements($concatenated);
                        }
                        t3lib_div::writeFile(PATH_site . $targetFile, $concatenated);
                }
                        }
                        t3lib_div::writeFile(PATH_site . $targetFile, $concatenated);
                }
@@ -195,17 +316,19 @@ class t3lib_Compressor {
        /**
         * Compress multiple css files
         *
        /**
         * Compress multiple css files
         *
-        * @param array $cssFiles       The files to compress (array key = filename), relative to requested page
-        * @return array                         The CSS files after compression (array key = new filename), relative to requested page
+        * @param array $cssFiles The files to compress (array key = filename), relative to requested page
+        * @return array The CSS files after compression (array key = new filename), relative to requested page
         */
        public function compressCssFiles(array $cssFiles) {
                $filesAfterCompression = array();
         */
        public function compressCssFiles(array $cssFiles) {
                $filesAfterCompression = array();
-               foreach ($cssFiles as $filename => $fileOptions) {
+               foreach ($cssFiles as $key => $fileOptions) {
                                // if compression is enabled
                        if ($fileOptions['compress']) {
                                // if compression is enabled
                        if ($fileOptions['compress']) {
-                               $filesAfterCompression[$this->compressCssFile($filename)] = $fileOptions;
-                       } else {
+                               $filename = $this->compressCssFile($fileOptions['file']);
+                               $fileOptions['file'] = $filename;
                                $filesAfterCompression[$filename] = $fileOptions;
                                $filesAfterCompression[$filename] = $fileOptions;
+                       } else {
+                               $filesAfterCompression[$key] = $fileOptions;
                        }
                }
                return $filesAfterCompression;
                        }
                }
                return $filesAfterCompression;
@@ -215,17 +338,17 @@ class t3lib_Compressor {
         * Compresses a CSS file
         *
         * Options:
         * Compresses a CSS file
         *
         * Options:
-        *   baseDirectories            If set, only include files below one of the base directories
+        *   baseDirectories If set, only include files below one of the base directories
         *
         * removes comments and whitespaces
         * Adopted from http://drupal.org/files/issues/minify_css.php__1.txt
         *
         *
         * removes comments and whitespaces
         * Adopted from http://drupal.org/files/issues/minify_css.php__1.txt
         *
-        * @param       string  $filename               Source filename, relative to requested page
-        * @return      string          Compressed filename, relative to requested page
+        * @param string $filename Source filename, relative to requested page
+        * @return string Compressed filename, relative to requested page
         */
        public function compressCssFile($filename) {
                        // generate the unique name of the file
         */
        public function compressCssFile($filename) {
                        // generate the unique name of the file
-               $filenameAbsolute = t3lib_div::resolveBackPath(PATH_typo3 . $this->getFilenameFromMainDir($filename));
+               $filenameAbsolute = t3lib_div::resolveBackPath($this->rootPath . $this->getFilenameFromMainDir($filename));
                $unique = $filenameAbsolute . filemtime($filenameAbsolute) . filesize($filenameAbsolute);
 
                $pathinfo = pathinfo($filename);
                $unique = $filenameAbsolute . filemtime($filenameAbsolute) . filesize($filenameAbsolute);
 
                $pathinfo = pathinfo($filename);
@@ -260,14 +383,14 @@ class t3lib_Compressor {
                        $contents = preg_replace('/[ \t]*+\n\s*+/S', "\n", $contents); // Consolidate multi-lines space.
                        $contents = preg_replace('/(?<!\s)\s*+$/S', "\n", $contents); // Ensure file ends in newline.
                                // we have to fix relative paths, if we aren't working on a file in our target directory
                        $contents = preg_replace('/[ \t]*+\n\s*+/S', "\n", $contents); // Consolidate multi-lines space.
                        $contents = preg_replace('/(?<!\s)\s*+$/S', "\n", $contents); // Ensure file ends in newline.
                                // we have to fix relative paths, if we aren't working on a file in our target directory
-                       if (!is_int(strpos($filename, $this->targetDirectory))) {
-                               $filenameRelativeToMainDir = substr($filename, strlen($GLOBALS['BACK_PATH']));
+                       if (strpos($filename, $this->targetDirectory) === FALSE) {
+                               $filenameRelativeToMainDir = substr($filename, strlen($this->backPath));
                                $contents = $this->cssFixRelativeUrlPaths($contents, dirname($filenameRelativeToMainDir) . '/');
                        }
                        $this->writeFileAndCompressed($targetFile, $contents);
                }
 
                                $contents = $this->cssFixRelativeUrlPaths($contents, dirname($filenameRelativeToMainDir) . '/');
                        }
                        $this->writeFileAndCompressed($targetFile, $contents);
                }
 
-               return $GLOBALS['BACK_PATH'] . '../' . $this->returnFileReference($targetFile);
+               return $this->relativePath . $this->returnFileReference($targetFile);
        }
 
        /**
        }
 
        /**
@@ -307,20 +430,17 @@ class t3lib_Compressor {
        /**
         * Compress multiple javascript files
         *
        /**
         * Compress multiple javascript files
         *
-        * @param       array   $jsFiles                The files to compress (array key = filename), relative to requested page
-        * @return      array           The js files after compression (array key = new filename), relative to requested page
+        * @param array $jsFiles The files to compress (array key = filename), relative to requested page
+        * @return array The js files after compression (array key = new filename), relative to requested page
         */
        public function compressJsFiles(array $jsFiles) {
                $filesAfterCompression = array();
         */
        public function compressJsFiles(array $jsFiles) {
                $filesAfterCompression = array();
-               foreach ($jsFiles as $filename => $fileOptions) {
-                               // we remove BACK_PATH from $filename, so make it relative to TYPO3_mainDir
-                       $filenameFromMainDir = $this->getFilenameFromMainDir($filename);
+               foreach ($jsFiles as $key => $fileOptions) {
                                // if compression is enabled
                        if ($fileOptions['compress']) {
                                // if compression is enabled
                        if ($fileOptions['compress']) {
-                               $filesAfterCompression[$this->compressJsFile($filename)] = $fileOptions;
-                       } else {
-                               $filesAfterCompression[$filename] = $fileOptions;
+                               $fileOptions['file'] = $this->compressJsFile($fileOptions['file']);
                        }
                        }
+                       $filesAfterCompression[$key] = $fileOptions;
                }
                return $filesAfterCompression;
        }
                }
                return $filesAfterCompression;
        }
@@ -328,15 +448,12 @@ class t3lib_Compressor {
        /**
         * Compresses a javascript file
         *
        /**
         * Compresses a javascript file
         *
-        * Options:
-        *   baseDirectories            If set, only include files below one of the base directories
-        *
-        * @param       string  $filename               Source filename, relative to requested page
-        * @return      string          Filename of the compressed file, relative to requested page
+        * @param string $filename Source filename, relative to requested page
+        * @return string Filename of the compressed file, relative to requested page
         */
        public function compressJsFile($filename) {
                        // generate the unique name of the file
         */
        public function compressJsFile($filename) {
                        // generate the unique name of the file
-               $filenameAbsolute = t3lib_div::resolveBackPath(PATH_typo3 . $this->getFilenameFromMainDir($filename));
+               $filenameAbsolute = t3lib_div::resolveBackPath($this->rootPath . $this->getFilenameFromMainDir($filename));
                $unique = $filenameAbsolute . filemtime($filenameAbsolute) . filesize($filenameAbsolute);
 
                $pathinfo = pathinfo($filename);
                $unique = $filenameAbsolute . filemtime($filenameAbsolute) . filesize($filenameAbsolute);
 
                $pathinfo = pathinfo($filename);
@@ -346,18 +463,58 @@ class t3lib_Compressor {
                        $contents = t3lib_div::getUrl($filenameAbsolute);
                        $this->writeFileAndCompressed($targetFile, $contents);
                }
                        $contents = t3lib_div::getUrl($filenameAbsolute);
                        $this->writeFileAndCompressed($targetFile, $contents);
                }
-               return $GLOBALS['BACK_PATH'] . '../' . $this->returnFileReference($targetFile);
+               return $this->relativePath . $this->returnFileReference($targetFile);
+       }
+
+       /**
+        * Finds the relative path to a file, relative to the root path.
+        *
+        * @param string $filename the name of the file
+        * @return string the path to the file relative to the root path
+        */
+       protected function getFilenameFromMainDir($filename) {
+                       // if BACK_PATH is empty return $filename
+               if (empty($this->backPath)) {
+                       return $filename;
+               }
+
+                       // if the file exists in the root path, just return the $filename
+               if (strpos($filename, $this->backPath) === 0) {
+                       $file = str_replace($this->backPath, '', $filename);
+                       if (is_file($this->rootPath . $file)) {
+                               return $file;
+                       }
+               }
+
+                       // build the file path relatively to the PATH_site
+               $backPath = str_replace(TYPO3_mainDir, '', $this->backPath);
+               $file = str_replace($backPath, '', $filename);
+               if (substr($file, 0, 3) === '../') {
+                       $file = t3lib_div::resolveBackPath(PATH_typo3 . $file);
+               } else {
+                       $file = PATH_site . $file;
+               }
+
+                       // check if the file exists, and if so, return the path relative to TYPO3_mainDir
+               if (is_file($file)) {
+                       $mainDirDepth = substr_count(TYPO3_mainDir, '/');
+                       return str_repeat('../', $mainDirDepth) . str_replace(PATH_site, '', $file);
+               }
+
+                       // none of above conditions were met, fallback to default behaviour
+               return substr($filename, strlen($this->backPath));
        }
 
        /**
        }
 
        /**
-        * Decides whether a CSS file comes from one of the baseDirectories
+        * Decides whether a file comes from one of the baseDirectories
         *
         *
-        * @param       string  $filename               Filename
-        * @return      boolean         File belongs to a skin or not
+        * @param string $filename Filename
+        * @param array $baseDirectories Base directories
+        * @return boolean File belongs to a base directory or not
         */
        protected function checkBaseDirectory($filename, array $baseDirectories) {
                foreach ($baseDirectories as $baseDirectory) {
         */
        protected function checkBaseDirectory($filename, array $baseDirectories) {
                foreach ($baseDirectories as $baseDirectory) {
-                               // check, if $filename starts with $skinStylesheetDirectory
+                               // check, if $filename starts with base directory
                        if (t3lib_div::isFirstPartOfStr($filename, $baseDirectory)) {
                                return TRUE;
                        }
                        if (t3lib_div::isFirstPartOfStr($filename, $baseDirectory)) {
                                return TRUE;
                        }
@@ -368,33 +525,99 @@ class t3lib_Compressor {
        /**
         * Fixes the relative paths inside of url() references in CSS files
         *
        /**
         * Fixes the relative paths inside of url() references in CSS files
         *
-        * @param       string  $contents               Data to process
-        * @param       string  $oldDir                 Directory of the originial file, relative to TYPO3_mainDir
-        * @return      string  Processed data
+        * @param string $contents Data to process
+        * @param string $oldDir Directory of the original file, relative to TYPO3_mainDir
+        * @return string Processed data
         */
        protected function cssFixRelativeUrlPaths($contents, $oldDir) {
         */
        protected function cssFixRelativeUrlPaths($contents, $oldDir) {
+               $mainDir = (TYPO3_MODE === 'BE' ? TYPO3_mainDir : '');
+               $newDir = '../../' . $mainDir . $oldDir;
+
+                       // Replace "url()" paths
+               if (stripos($contents, 'url') !== FALSE) {
+                       $regex = '/url(\(\s*["\']?([^"\']+)["\']?\s*\))/iU';
+                       $contents = $this->findAndReplaceUrlPathsByRegex($contents, $regex, $newDir, '(\'|\')');
+               }
+
+                       // Replace "@import" paths
+               if (stripos($contents, '@import') !== FALSE) {
+                       $regex = '/@import\s*(["\']?([^"\']+)["\']?)/i';
+                       $contents = $this->findAndReplaceUrlPathsByRegex($contents, $regex, $newDir, '"|"');
+               }
+
+               return $contents;
+       }
+
+       /**
+        * Finds and replaces all URLs by using a given regex
+        *
+        * @param string $contents Data to process
+        * @param string $regex Regex used to find URLs in content
+        * @param string $newDir Path to prepend to the original file
+        * @param string $wrap Wrap around replaced values
+        * @return string Processed data
+        */
+       protected function findAndReplaceUrlPathsByRegex($contents, $regex, $newDir, $wrap = '|') {
                $matches = array();
                $matches = array();
+               $replacements = array();
+               $wrap = explode('|', $wrap);
 
 
-               preg_match_all('/url(\(\s*["\']?([^"\']+)["\']?\s*\))/iU', $contents, $matches);
+               preg_match_all($regex, $contents, $matches);
                foreach ($matches[2] as $matchCount => $match) {
                                // remove '," or white-spaces around
                foreach ($matches[2] as $matchCount => $match) {
                                // remove '," or white-spaces around
-                       $match = preg_replace('/[\"\'\s]/', '', $match);
+                       $match = trim($match, '\'" ');
 
 
-                               // we must not rewrite paths containing ":", e.g. data URIs (see RFC 2397)
-                       if (strpos($match, ':') === FALSE) {
-                               $newPath = t3lib_div::resolveBackPath('../../' . TYPO3_mainDir . $oldDir . $match);
-                               $contents = str_replace($matches[1][$matchCount], '(\'' . $newPath . '\')', $contents);
+                               // we must not rewrite paths containing ":" or "url(", e.g. data URIs (see RFC 2397)
+                       if (strpos($match, ':') === FALSE && !preg_match('/url\s*\(/i', $match)) {
+                               $newPath = t3lib_div::resolveBackPath($newDir . $match);
+                               $replacements[$matches[1][$matchCount]] = $wrap[0] . $newPath . $wrap[1];
                        }
                }
                        }
                }
+
+                       // replace URL paths in content
+               if (!empty($replacements)) {
+                       $contents = str_replace(array_keys($replacements), array_values($replacements), $contents);
+               }
+
+               return $contents;
+       }
+
+       /**
+        * Moves @charset, @import and @namespace statements to the top of
+        * the content, because they must occur before all other CSS rules
+        *
+        * @param string $contents Data to process
+        * @return string Processed data
+        */
+       protected function cssFixStatements($contents) {
+               $matches = array();
+               $comment = LF . '/* moved by compressor */' . LF;
+
+                       // nothing to do, so just return contents
+               if (stripos($contents, '@charset') === FALSE && stripos($contents, '@import') === FALSE
+                       && stripos($contents, '@namespace') === FALSE) {
+
+                       return $contents;
+               }
+
+               $regex = '/@(charset|import|namespace)\s*(url)?\s*\(?\s*["\']?[^"\']+["\']?\s*\)?.*;/i';
+               preg_match_all($regex, $contents, $matches);
+               if (!empty($matches[0])) {
+                               // remove existing statements
+                       $contents = str_replace($matches[0], '', $contents);
+                               // add statements to the top of contents in the order they occur in original file
+                       $contents = $comment . implode($comment, $matches[0]) . LF . $contents;
+               }
+
                return $contents;
        }
 
        /**
         * Writes $contents into file $filename together with a gzipped version into $filename.gz
         *
                return $contents;
        }
 
        /**
         * Writes $contents into file $filename together with a gzipped version into $filename.gz
         *
-        * @param       string  $filename               Target filename
-        * @param       strings $contents               File contents
-        * @return      void
+        * @param string $filename Target filename
+        * @param string $contents File contents
+        * @return void
         */
        protected function writeFileAndCompressed($filename, $contents) {
                        // write uncompressed file
         */
        protected function writeFileAndCompressed($filename, $contents) {
                        // write uncompressed file
@@ -410,8 +633,8 @@ class t3lib_Compressor {
         * Decides whether a client can deal with gzipped content or not and returns the according file name,
         * based on HTTP_ACCEPT_ENCODING
         *
         * Decides whether a client can deal with gzipped content or not and returns the according file name,
         * based on HTTP_ACCEPT_ENCODING
         *
-        * @param       string  $filename               File name
-        * @return      string          $filename suffixed with '.gzip' or not - dependent on HTTP_ACCEPT_ENCODING
+        * @param string $filename File name
+        * @return string $filename suffixed with '.gzip' or not - dependent on HTTP_ACCEPT_ENCODING
         */
        protected function returnFileReference($filename) {
                        // if the client accepts gzip and we can create gzipped files, we give him compressed versions
         */
        protected function returnFileReference($filename) {
                        // if the client accepts gzip and we can create gzipped files, we give him compressed versions
@@ -421,6 +644,7 @@ class t3lib_Compressor {
                        return $filename;
                }
        }
                        return $filename;
                }
        }
+
 }
 
 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_compressor.php'])) {
 }
 
 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_compressor.php'])) {
index eb5fa24..a328552 100644 (file)
@@ -2,7 +2,8 @@
 /***************************************************************
  *  Copyright notice
  *
 /***************************************************************
  *  Copyright notice
  *
- *  (c) 2009-2011 Steffen Kamper (info@sk-typo3.de)
+ *  (c) 2009-2011 Steffen Kamper <info@sk-typo3.de>
+ *  (c) 2011 Kai Vogel <kai.vogel@speedprogs.de>
  *  All rights reserved
  *
  *  This script is part of the TYPO3 project. The TYPO3 project is
  *  All rights reserved
  *
  *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -29,7 +30,8 @@
  * TYPO3 pageRender class (new in TYPO3 4.3.0)
  * This class render the HTML of a webpage, usable for BE and FE
  *
  * TYPO3 pageRender class (new in TYPO3 4.3.0)
  * This class render the HTML of a webpage, usable for BE and FE
  *
- * @author     Steffen Kamper <info@sk-typo3.de>
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @author Kai Vogel <kai.vogel@speedprogs.de>
  * @package TYPO3
  * @subpackage t3lib
  */
  * @package TYPO3
  * @subpackage t3lib
  */
@@ -40,6 +42,8 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        protected $removeLineBreaksFromTemplate = FALSE;
 
        protected $concatenateFiles = FALSE;
        protected $removeLineBreaksFromTemplate = FALSE;
 
        protected $concatenateFiles = FALSE;
+       protected $concatenateJavascript = FALSE;
+       protected $concatenateCss = FALSE;
 
        protected $moveJsFromHeaderToFooter = FALSE;
 
 
        protected $moveJsFromHeaderToFooter = FALSE;
 
@@ -50,12 +54,12 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        /* @var t3lib_Compressor Instance of t3lib_Compressor */
        protected $compressor;
 
        /* @var t3lib_Compressor Instance of t3lib_Compressor */
        protected $compressor;
 
-               // static array containing associative array for the included files
-       protected static $jsFiles = array();
-       protected static $jsFooterFiles = array();
-       protected static $jsLibs = array();
-       protected static $jsFooterLibs = array();
-       protected static $cssFiles = array();
+               // arrays containing associative array for the included files
+       protected $jsFiles = array();
+       protected $jsFooterFiles = array();
+       protected $jsLibs = array();
+       protected $jsFooterLibs = array();
+       protected $cssFiles = array();
 
        protected $title;
        protected $charSet;
 
        protected $title;
        protected $charSet;
@@ -456,7 +460,7 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        }
 
        /**
        }
 
        /**
-        * Enables concatenation of js/css files
+        * Enables concatenation of js and css files
         *
         * @param void
         * @return void
         *
         * @param void
         * @return void
@@ -466,7 +470,7 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        }
 
        /**
        }
 
        /**
-        * Disables concatenation of js/css files
+        * Disables concatenation of js and css files
         *
         * @param void
         * @return void
         *
         * @param void
         * @return void
@@ -476,6 +480,46 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        }
 
        /**
        }
 
        /**
+        * Enables concatenation of js files
+        *
+        * @param void
+        * @return void
+        */
+       public function enableConcatenateJavascript() {
+               $this->concatenateJavascript = TRUE;
+       }
+
+       /**
+        * Disables concatenation of js files
+        *
+        * @param void
+        * @return void
+        */
+       public function disableConcatenateJavascript() {
+               $this->concatenateJavascript = FALSE;
+       }
+
+       /**
+        * Enables concatenation of css files
+        *
+        * @param void
+        * @return void
+        */
+       public function enableConcatenateCss() {
+               $this->concatenateCss = TRUE;
+       }
+
+       /**
+        * Disables concatenation of css files
+        *
+        * @param void
+        * @return void
+        */
+       public function disableConcatenateCss() {
+               $this->concatenateCss = FALSE;
+       }
+
+       /**
         * Sets removal of all line breaks in template
         *
         * @param void
         * Sets removal of all line breaks in template
         *
         * @param void
@@ -646,7 +690,7 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        }
 
        /**
        }
 
        /**
-        * Gets concatenate of files
+        * Gets concatenate of js and css files
         *
         * @return boolean
         */
         *
         * @return boolean
         */
@@ -655,6 +699,24 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        }
 
        /**
        }
 
        /**
+        * Gets concatenate of js files
+        *
+        * @return boolean
+        */
+       public function getConcatenateJavascript() {
+               return $this->concatenateJavascript;
+       }
+
+       /**
+        * Gets concatenate of css files
+        *
+        * @return boolean
+        */
+       public function getConcatenateCss() {
+               return $this->concatenateCss;
+       }
+
+       /**
         * Gets remove of empty lines from template
         *
         * @return boolean
         * Gets remove of empty lines from template
         *
         * @return boolean
@@ -795,15 +857,16 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        /**
         * Adds JS Library. JS Library block is rendered on top of the JS files.
         *
        /**
         * Adds JS Library. JS Library block is rendered on top of the JS files.
         *
-        * @param string $name
-        * @param string $file
-        * @param string $type
+        * @param string $name Arbitrary identifier
+        * @param string $file File name
+        * @param string $type Content Type
         * @param boolean $compress             flag if library should be compressed
         * @param boolean $forceOnTop   flag if added library should be inserted at begin of this block
         * @param string $allWrap
         * @param boolean $compress             flag if library should be compressed
         * @param boolean $forceOnTop   flag if added library should be inserted at begin of this block
         * @param string $allWrap
+        * @param boolean $excludeFromConcatenation
         * @return void
         */
         * @return void
         */
-       public function addJsLibrary($name, $file, $type = 'text/javascript', $compress = FALSE, $forceOnTop = FALSE, $allWrap = '') {
+       public function addJsLibrary($name, $file, $type = 'text/javascript', $compress = FALSE, $forceOnTop = FALSE, $allWrap = '', $excludeFromConcatenation = FALSE) {
                if (!$type) {
                        $type = 'text/javascript';
                }
                if (!$type) {
                        $type = 'text/javascript';
                }
@@ -814,7 +877,8 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                                'section' => self::PART_HEADER,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
                                'section' => self::PART_HEADER,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
-                               'allWrap' => $allWrap
+                               'allWrap' => $allWrap,
+                               'excludeFromConcatenation' => $excludeFromConcatenation,
                        );
                }
 
                        );
                }
 
@@ -823,15 +887,16 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        /**
         * Adds JS Library to Footer. JS Library block is rendered on top of the Footer JS files.
         *
        /**
         * Adds JS Library to Footer. JS Library block is rendered on top of the Footer JS files.
         *
-        * @param string $name
-        * @param string $file
-        * @param string $type
+        * @param string $name Arbitrary identifier
+        * @param string $file File name
+        * @param string $type Content Type
         * @param boolean $compress     flag if library should be compressed
         * @param boolean $forceOnTop   flag if added library should be inserted at begin of this block
         * @param string $allWrap
         * @param boolean $compress     flag if library should be compressed
         * @param boolean $forceOnTop   flag if added library should be inserted at begin of this block
         * @param string $allWrap
+        * @param boolean $excludeFromConcatenation
         * @return void
         */
         * @return void
         */
-       public function addJsFooterLibrary($name, $file, $type = 'text/javascript', $compress = FALSE, $forceOnTop = FALSE, $allWrap = '') {
+       public function addJsFooterLibrary($name, $file, $type = 'text/javascript', $compress = FALSE, $forceOnTop = FALSE, $allWrap = '', $excludeFromConcatenation = FALSE) {
                if (!$type) {
                        $type = 'text/javascript';
                }
                if (!$type) {
                        $type = 'text/javascript';
                }
@@ -842,7 +907,8 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                                'section' => self::PART_FOOTER,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
                                'section' => self::PART_FOOTER,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
-                               'allWrap' => $allWrap
+                               'allWrap' => $allWrap,
+                               'excludeFromConcatenation' => $excludeFromConcatenation,
                        );
                }
 
                        );
                }
 
@@ -851,24 +917,27 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        /**
         * Adds JS file
         *
        /**
         * Adds JS file
         *
-        * @param string $file
-        * @param string $type
+        * @param string $file File name
+        * @param string $type Content Type
         * @param boolean $compress
         * @param boolean $forceOnTop
         * @param string $allWrap
         * @param boolean $compress
         * @param boolean $forceOnTop
         * @param string $allWrap
+        * @param boolean $excludeFromConcatenation
         * @return void
         */
         * @return void
         */
-       public function addJsFile($file, $type = 'text/javascript', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '') {
+       public function addJsFile($file, $type = 'text/javascript', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '', $excludeFromConcatenation = FALSE) {
                if (!$type) {
                        $type = 'text/javascript';
                }
                if (!isset($this->jsFiles[$file])) {
                        $this->jsFiles[$file] = array(
                if (!$type) {
                        $type = 'text/javascript';
                }
                if (!isset($this->jsFiles[$file])) {
                        $this->jsFiles[$file] = array(
+                               'file' => $file,
                                'type' => $type,
                                'section' => self::PART_HEADER,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
                                'type' => $type,
                                'section' => self::PART_HEADER,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
-                               'allWrap' => $allWrap
+                               'allWrap' => $allWrap,
+                               'excludeFromConcatenation' => $excludeFromConcatenation,
                        );
                }
        }
                        );
                }
        }
@@ -876,23 +945,27 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        /**
         * Adds JS file to footer
         *
        /**
         * Adds JS file to footer
         *
-        * @param string $file
-        * @param string $type
+        * @param string $file File name
+        * @param string $type Content Type
         * @param boolean $compress
         * @param boolean $forceOnTop
         * @param boolean $compress
         * @param boolean $forceOnTop
+        * @param string $allWrap
+        * @param boolean $excludeFromConcatenation
         * @return void
         */
         * @return void
         */
-       public function addJsFooterFile($file, $type = 'text/javascript', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '') {
+       public function addJsFooterFile($file, $type = 'text/javascript', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '', $excludeFromConcatenation = FALSE) {
                if (!$type) {
                        $type = 'text/javascript';
                }
                if (!isset($this->jsFiles[$file])) {
                        $this->jsFiles[$file] = array(
                if (!$type) {
                        $type = 'text/javascript';
                }
                if (!isset($this->jsFiles[$file])) {
                        $this->jsFiles[$file] = array(
+                               'file' => $file,
                                'type' => $type,
                                'section' => self::PART_FOOTER,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
                                'type' => $type,
                                'section' => self::PART_FOOTER,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
-                               'allWrap' => $allWrap
+                               'allWrap' => $allWrap,
+                               'excludeFromConcatenation' => $excludeFromConcatenation,
                        );
                }
        }
                        );
                }
        }
@@ -1101,17 +1174,21 @@ class t3lib_PageRenderer implements t3lib_Singleton {
         * @param string $title
         * @param boolean $compress
         * @param boolean $forceOnTop
         * @param string $title
         * @param boolean $compress
         * @param boolean $forceOnTop
+        * @param string $allWrap
+        * @param boolean $excludeFromConcatenation
         * @return void
         */
         * @return void
         */
-       public function addCssFile($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '') {
+       public function addCssFile($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '', $excludeFromConcatenation = FALSE) {
                if (!isset($this->cssFiles[$file])) {
                        $this->cssFiles[$file] = array(
                if (!isset($this->cssFiles[$file])) {
                        $this->cssFiles[$file] = array(
+                               'file' => $file,
                                'rel' => $rel,
                                'media' => $media,
                                'title' => $title,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
                                'rel' => $rel,
                                'media' => $media,
                                'title' => $title,
                                'compress' => $compress,
                                'forceOnTop' => $forceOnTop,
-                               'allWrap' => $allWrap
+                               'allWrap' => $allWrap,
+                               'excludeFromConcatenation' => $excludeFromConcatenation,
                        );
                }
        }
                        );
                }
        }
@@ -1422,7 +1499,7 @@ class t3lib_PageRenderer implements t3lib_Singleton {
 
                $jsLibs = $this->renderJsLibraries();
 
 
                $jsLibs = $this->renderJsLibraries();
 
-               if ($this->concatenateFiles) {
+               if ($this->concatenateFiles || $this->concatenateJavascript || $this->concatenateCss) {
                                // do the file concatenation
                        $this->doConcatenate();
                }
                                // do the file concatenation
                        $this->doConcatenate();
                }
@@ -1446,11 +1523,11 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                }
 
                if (count($this->cssFiles)) {
                }
 
                if (count($this->cssFiles)) {
-                       foreach ($this->cssFiles as $file => $properties) {
-                               $file = t3lib_div::resolveBackPath($file);
-                               $file = t3lib_div::createVersionNumberedFilename($file);
+                       foreach ($this->cssFiles as $name => $properties) {
+                               $properties['file'] = t3lib_div::resolveBackPath($properties['file']);
+                               $properties['file'] = t3lib_div::createVersionNumberedFilename($properties['file']);
                                $tag = '<link rel="' . htmlspecialchars($properties['rel']) . '" type="text/css" href="' .
                                $tag = '<link rel="' . htmlspecialchars($properties['rel']) . '" type="text/css" href="' .
-                                          htmlspecialchars($file) . '" media="' . htmlspecialchars($properties['media']) . '"' .
+                                          htmlspecialchars($properties['file']) . '" media="' . htmlspecialchars($properties['media']) . '"' .
                                           ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '') .
                                           $endingSlash . '>';
                                if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) {
                                           ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '') .
                                           $endingSlash . '>';
                                if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) {
@@ -1500,10 +1577,10 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                }
 
                if (count($this->jsFiles)) {
                }
 
                if (count($this->jsFiles)) {
-                       foreach ($this->jsFiles as $file => $properties) {
-                               $file = t3lib_div::resolveBackPath($file);
-                               $file = t3lib_div::createVersionNumberedFilename($file);
-                               $tag = '<script src="' . htmlspecialchars($file) . '" type="' . htmlspecialchars($properties['type']) . '"></script>';
+                       foreach ($this->jsFiles as $name => $properties) {
+                               $properties['file'] = t3lib_div::resolveBackPath($properties['file']);
+                               $properties['file'] = t3lib_div::createVersionNumberedFilename($properties['file']);
+                               $tag = '<script src="' . htmlspecialchars($properties['file']) . '" type="' . htmlspecialchars($properties['type']) . '"></script>';
                                if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) {
                                        $tag = str_replace('|', $tag, $properties['allWrap']);
                                }
                                if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) {
                                        $tag = str_replace('|', $tag, $properties['allWrap']);
                                }
@@ -1835,58 +1912,121 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        /*****************************************************/
 
        /**
        /*****************************************************/
 
        /**
-        * concatenate files into one file
+        * Concatenate files into one file
         * registered handler
         *
         * @return void
         */
        protected function doConcatenate() {
         * registered handler
         *
         * @return void
         */
        protected function doConcatenate() {
-                       // traverse the arrays, concatenate in one file
-                       // then remove concatenated files from array and add the concatenated file
+               $this->doConcatenateCss();
+               $this->doConcatenateJavaScript();
+       }
 
 
-               if ($this->concatenateFiles) {
-                       $params = array(
-                               'jsLibs' => &$this->jsLibs,
-                               'jsFiles' => &$this->jsFiles,
-                               'jsFooterFiles' => &$this->jsFooterFiles,
-                               'cssFiles' => &$this->cssFiles,
-                               'headerData' => &$this->headerData,
-                               'footerData' => &$this->footerData,
-                       );
+       /**
+        * Concatenate JavaScript files according to the configuration.
+        *
+        * @return void
+        */
+       protected function doConcatenateJavaScript() {
+               if ($this->concatenateFiles || $this->concatenateJavascript) {
+                       if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler'])) {
+                                       // use external concatenation routine
+                               $params = array(
+                                       'jsLibs' => &$this->jsLibs,
+                                       'jsFiles' => &$this->jsFiles,
+                                       'jsFooterFiles' => &$this->jsFooterFiles,
+                                       'headerData' => &$this->headerData,
+                                       'footerData' => &$this->footerData,
+                               );
+                               t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler'], $params, $this);
+                       } else {
+                               $this->jsLibs = $this->getCompressor()->concatenateJsFiles($this->jsLibs);
+                               $this->jsFiles = $this->getCompressor()->concatenateJsFiles($this->jsFiles);
+                               $this->jsFooterFiles = $this->getCompressor()->concatenateJsFiles($this->jsFooterFiles);
+                       }
+               }
+       }
 
 
-                       if ($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['concatenateHandler']) {
-                                       // use extern concatenate routine
-                               t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['concatenateHandler'], $params, $this);
-                       } elseif (TYPO3_MODE === 'BE') {
-                               $cssOptions = array('baseDirectories' => $GLOBALS['TBE_TEMPLATE']->getSkinStylesheetDirectories());
+       /**
+        * Concatenate CSS files according to configuration.
+        *
+        * @return void
+        */
+       protected function doConcatenateCss() {
+               if ($this->concatenateFiles || $this->concatenateCss) {
+                       if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler'])) {
+                                       // use external concatenation routine
+                               $params = array(
+                                       'cssFiles' => &$this->cssFiles,
+                                       'headerData' => &$this->headerData,
+                                       'footerData' => &$this->footerData,
+                               );
+                               t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler'], $params, $this);
+                       } else {
+                               $cssOptions = array();
+                               if (TYPO3_MODE === 'BE') {
+                                       $cssOptions = array('baseDirectories' => $GLOBALS['TBE_TEMPLATE']->getSkinStylesheetDirectories());
+                               }
                                $this->cssFiles = $this->getCompressor()->concatenateCssFiles($this->cssFiles, $cssOptions);
                        }
                }
        }
 
        /**
                                $this->cssFiles = $this->getCompressor()->concatenateCssFiles($this->cssFiles, $cssOptions);
                        }
                }
        }
 
        /**
-        * compress inline code
+        * Compresses inline code
         *
         * @return void
         */
        protected function doCompress() {
         *
         * @return void
         */
        protected function doCompress() {
+               $this->doCompressJavaScript();
+               $this->doCompressCss();
+       }
 
 
-               if ($this->compressJavascript && $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler']) {
-                               // use extern compress routine
+       /**
+        * Compresses CSS according to configuration.
+        *
+        * @return void
+        */
+       protected function doCompressCss() {
+               if ($this->compressCss) {
+                               // use external compression routine
                        $params = array(
                        $params = array(
-                               'jsInline' => &$this->jsInline,
-                               'jsFooterInline' => &$this->jsFooterInline,
-                               'jsLibs' => &$this->jsLibs,
-                               'jsFiles' => &$this->jsFiles,
-                               'jsFooterFiles' => &$this->jsFooterFiles,
+                               'cssInline' => &$this->cssInline,
+                               'cssFiles' => &$this->cssFiles,
                                'headerData' => &$this->headerData,
                                'footerData' => &$this->footerData,
                        );
                                'headerData' => &$this->headerData,
                                'footerData' => &$this->footerData,
                        );
-                       t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'], $params, $this);
-               } else {
-                               // traverse the arrays, compress files
 
 
-                       if ($this->compressJavascript) {
+                       if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'])) {
+                                       // use external concatenation routine
+                               t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'], $params, $this);
+                       } else {
+                               $this->cssFiles = $this->getCompressor()->compressCssFiles($this->cssFiles);
+                       }
+               }
+       }
+
+       /**
+        * Compresses JavaScript according to configuration.
+        *
+        * @return void
+        */
+       protected function doCompressJavaScript() {
+               if ($this->compressJavascript) {
+                       if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'])) {
+                                       // use external compression routine
+                               $params = array(
+                                       'jsInline' => &$this->jsInline,
+                                       'jsFooterInline' => &$this->jsFooterInline,
+                                       'jsLibs' => &$this->jsLibs,
+                                       'jsFiles' => &$this->jsFiles,
+                                       'jsFooterFiles' => &$this->jsFooterFiles,
+                                       'headerData' => &$this->headerData,
+                                       'footerData' => &$this->footerData,
+                               );
+                               t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'], $params, $this);
+                       } else {
+                                       // traverse the arrays, compress files
                                if (count($this->jsInline)) {
                                        foreach ($this->jsInline as $name => $properties) {
                                                if ($properties['compress']) {
                                if (count($this->jsInline)) {
                                        foreach ($this->jsInline as $name => $properties) {
                                                if ($properties['compress']) {
@@ -1898,26 +2038,9 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                                                }
                                        }
                                }
                                                }
                                        }
                                }
-                               if (TYPO3_MODE === 'BE') {
-                                       $this->jsFiles = $this->getCompressor()->compressJsFiles($this->jsFiles);
-                                       $this->jsFooterFiles = $this->getCompressor()->compressJsFiles($this->jsFooterFiles);
-                               }
-                       }
-               }
-               if ($this->compressCss) {
-                               // use extern compress routine
-                       $params = array(
-                               'cssInline' => &$this->cssInline,
-                               'cssFiles' => &$this->cssFiles,
-                               'headerData' => &$this->headerData,
-                               'footerData' => &$this->footerData,
-                       );
-
-                       if ($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler']) {
-                                       // use extern concatenate routine
-                               t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'], $params, $this);
-                       } elseif (TYPO3_MODE === 'BE') {
-                               $this->cssFiles = $this->getCompressor()->compressCssFiles($this->cssFiles);
+                               $this->jsLibs = $this->getCompressor()->compressJsFiles($this->jsLibs);
+                               $this->jsFiles = $this->getCompressor()->compressJsFiles($this->jsFiles);
+                               $this->jsFooterFiles = $this->getCompressor()->compressJsFiles($this->jsFooterFiles);
                        }
                }
        }
                        }
                }
        }
@@ -1925,7 +2048,7 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        /**
         * Returns instance of t3lib_Compressor
         *
        /**
         * Returns instance of t3lib_Compressor
         *
-        * @return      t3lib_Compressor                Instance of t3lib_Compressor
+        * @return t3lib_Compressor Instance of t3lib_Compressor
         */
        protected function getCompressor() {
                if ($this->compressor === NULL) {
         */
        protected function getCompressor() {
                if ($this->compressor === NULL) {
@@ -1939,13 +2062,17 @@ class t3lib_PageRenderer implements t3lib_Singleton {
         *
         * Adds the version number for Frontend, compresses the file for Backend
         *
         *
         * Adds the version number for Frontend, compresses the file for Backend
         *
-        * @param       string  $filename               Filename
-        * @return      string          new filename
+        * @param string $filename Filename
+        * @return string New filename
         */
        protected function processJsFile($filename) {
                switch (TYPO3_MODE) {
                        case 'FE':
         */
        protected function processJsFile($filename) {
                switch (TYPO3_MODE) {
                        case 'FE':
-                               $filename = t3lib_div::createVersionNumberedFilename($filename);
+                               if ($this->compressJavascript) {
+                                       $filename = $this->getCompressor()->compressJsFile($filename);
+                               } else {
+                                       $filename = t3lib_div::createVersionNumberedFilename($filename);
+                               }
                        break;
                        case 'BE':
                                if ($this->compressJavascript) {
                        break;
                        case 'BE':
                                if ($this->compressJavascript) {
@@ -1961,4 +2088,5 @@ class t3lib_PageRenderer implements t3lib_Singleton {
 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_pagerenderer.php'])) {
        include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_pagerenderer.php']);
 }
 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_pagerenderer.php'])) {
        include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_pagerenderer.php']);
 }
+
 ?>
\ No newline at end of file
 ?>
\ No newline at end of file
index f572060..abf84a4 100644 (file)
@@ -3958,6 +3958,7 @@ if (version == "n3") {
        function setAbsRefPrefix()      {
                if ($this->absRefPrefix)        {
                        $this->content = str_replace('"media/', '"'.t3lib_extMgm::siteRelPath('cms').'tslib/media/', $this->content);
        function setAbsRefPrefix()      {
                if ($this->absRefPrefix)        {
                        $this->content = str_replace('"media/', '"'.t3lib_extMgm::siteRelPath('cms').'tslib/media/', $this->content);
+                       $this->content = str_replace('"typo3temp/compressor/', '"' . $this->absRefPrefix . 'typo3temp/compressor/', $this->content);
                        $this->content = str_replace('"typo3conf/ext/', '"'.$this->absRefPrefix.'typo3conf/ext/', $this->content);
                        $this->content = str_replace('"' . TYPO3_mainDir . 'contrib/', '"' . $this->absRefPrefix . TYPO3_mainDir . 'contrib/', $this->content);
                        $this->content = str_replace('"' . TYPO3_mainDir . 'ext/', '"' . $this->absRefPrefix . TYPO3_mainDir . 'ext/', $this->content);
                        $this->content = str_replace('"typo3conf/ext/', '"'.$this->absRefPrefix.'typo3conf/ext/', $this->content);
                        $this->content = str_replace('"' . TYPO3_mainDir . 'contrib/', '"' . $this->absRefPrefix . TYPO3_mainDir . 'contrib/', $this->content);
                        $this->content = str_replace('"' . TYPO3_mainDir . 'ext/', '"' . $this->absRefPrefix . TYPO3_mainDir . 'ext/', $this->content);
index 001c894..0a10191 100644 (file)
@@ -538,7 +538,7 @@ See <a href="http://wiki.typo3.org/index.php/TYPO3_3.8.1" target="_blank">wiki.t
                                                        $pageRenderer->addCssInlineBlock(
                                                                'import_' . $key,
                                                                '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['media']) . ';',
                                                        $pageRenderer->addCssInlineBlock(
                                                                'import_' . $key,
                                                                '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['media']) . ';',
-                                                               $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['compress'] ? TRUE : FALSE,
+                                                               empty($GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['disableCompression']),
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
                                                                ''
                                                        );
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
                                                                ''
                                                        );
@@ -548,10 +548,11 @@ See <a href="http://wiki.typo3.org/index.php/TYPO3_3.8.1" target="_blank">wiki.t
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['alternate'] ? 'alternate stylesheet' : 'stylesheet',
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['media'] ? $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['media'] : 'all',
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['title'] ? $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['title'] : '',
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['alternate'] ? 'alternate stylesheet' : 'stylesheet',
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['media'] ? $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['media'] : 'all',
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['title'] ? $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['title'] : '',
-                                                               $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['compress'] ? TRUE : FALSE,
+                                                               empty($GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['disableCompression']),
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
                                                                $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
-                                                               $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['allWrap']);
-
+                                                               $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['allWrap'],
+                                                               $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['excludeFromConcatenation'] ? TRUE : FALSE
+                                                       );
                                                }
                                        }
                                }
                                                }
                                        }
                                }
@@ -693,9 +694,10 @@ See <a href="http://wiki.typo3.org/index.php/TYPO3_3.8.1" target="_blank">wiki.t
                                                        $key,
                                                        $ss,
                                                        $type,
                                                        $key,
                                                        $ss,
                                                        $type,
-                                                       $GLOBALS['TSFE']->pSetup['includeJSlibs.'][$key . '.']['compress'] ? TRUE : FALSE,
+                                                       empty($GLOBALS['TSFE']->pSetup['includeJSlibs.'][$key . '.']['disableCompression']),
                                                        $GLOBALS['TSFE']->pSetup['includeJSlibs.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
                                                        $GLOBALS['TSFE']->pSetup['includeJSlibs.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
-                                                       $GLOBALS['TSFE']->pSetup['includeJSlibs.'][$key . '.']['allWrap']
+                                                       $GLOBALS['TSFE']->pSetup['includeJSlibs.'][$key . '.']['allWrap'],
+                                                       $GLOBALS['TSFE']->pSetup['includeJSlibs.'][$key . '.']['excludeFromConcatenation'] ? TRUE : FALSE
                                                );
                                        }
                                }
                                                );
                                        }
                                }
@@ -715,9 +717,10 @@ See <a href="http://wiki.typo3.org/index.php/TYPO3_3.8.1" target="_blank">wiki.t
                                                        $key,
                                                        $ss,
                                                        $type,
                                                        $key,
                                                        $ss,
                                                        $type,
-                                                       $GLOBALS['TSFE']->pSetup['includeJSFooterlibs.'][$key . '.']['compress'] ? TRUE : FALSE,
+                                                       empty($GLOBALS['TSFE']->pSetup['includeJSFooterlibs.'][$key . '.']['disableCompression']),
                                                        $GLOBALS['TSFE']->pSetup['includeJSFooterlibs.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
                                                        $GLOBALS['TSFE']->pSetup['includeJSFooterlibs.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
-                                                       $GLOBALS['TSFE']->pSetup['includeJSFooterlibs.'][$key . '.']['allWrap']
+                                                       $GLOBALS['TSFE']->pSetup['includeJSFooterlibs.'][$key . '.']['allWrap'],
+                                                       $GLOBALS['TSFE']->pSetup['includeJSFooterlibs.'][$key . '.']['excludeFromConcatenation'] ? TRUE : FALSE
                                                );
                                        }
                                }
                                                );
                                        }
                                }
@@ -737,9 +740,10 @@ See <a href="http://wiki.typo3.org/index.php/TYPO3_3.8.1" target="_blank">wiki.t
                                                $pageRenderer->addJsFile(
                                                        $ss,
                                                        $type,
                                                $pageRenderer->addJsFile(
                                                        $ss,
                                                        $type,
-                                                       $GLOBALS['TSFE']->pSetup['includeJS.'][$key . '.']['compress'] ? TRUE : FALSE,
+                                                       empty($GLOBALS['TSFE']->pSetup['includeJS.'][$key . '.']['disableCompression']),
                                                        $GLOBALS['TSFE']->pSetup['includeJS.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
                                                        $GLOBALS['TSFE']->pSetup['includeJS.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
-                                                       $GLOBALS['TSFE']->pSetup['includeJS.'][$key . '.']['allWrap']
+                                                       $GLOBALS['TSFE']->pSetup['includeJS.'][$key . '.']['allWrap'],
+                                                       $GLOBALS['TSFE']->pSetup['includeJS.'][$key . '.']['excludeFromConcatenation'] ? TRUE : FALSE
                                                );
                                        }
                                }
                                                );
                                        }
                                }
@@ -758,9 +762,10 @@ See <a href="http://wiki.typo3.org/index.php/TYPO3_3.8.1" target="_blank">wiki.t
                                                $pageRenderer->addJsFooterFile(
                                                        $ss,
                                                        $type,
                                                $pageRenderer->addJsFooterFile(
                                                        $ss,
                                                        $type,
-                                                       $GLOBALS['TSFE']->pSetup['includeJSFooter.'][$key . '.']['compress'] ? TRUE : FALSE,
+                                                       empty($GLOBALS['TSFE']->pSetup['includeJSFooter.'][$key . '.']['disableCompression']),
                                                        $GLOBALS['TSFE']->pSetup['includeJSFooter.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
                                                        $GLOBALS['TSFE']->pSetup['includeJSFooter.'][$key . '.']['forceOnTop'] ? TRUE : FALSE,
-                                                       $GLOBALS['TSFE']->pSetup['includeJSFooter.'][$key . '.']['allWrap']
+                                                       $GLOBALS['TSFE']->pSetup['includeJSFooter.'][$key . '.']['allWrap'],
+                                                       $GLOBALS['TSFE']->pSetup['includeJSFooter.'][$key . '.']['excludeFromConcatenation'] ? TRUE : FALSE
                                                );
                                        }
                                }
                                                );
                                        }
                                }
@@ -993,6 +998,13 @@ See <a href="http://wiki.typo3.org/index.php/TYPO3_3.8.1" target="_blank">wiki.t
                if ($GLOBALS['TSFE']->config['config']['minifyJS']) {
                        $pageRenderer->enableCompressJavascript();
                }
                if ($GLOBALS['TSFE']->config['config']['minifyJS']) {
                        $pageRenderer->enableCompressJavascript();
                }
+               if ($GLOBALS['TSFE']->config['config']['concatenateCss']) {
+                       $pageRenderer->enableConcatenateCss();
+               }
+               if ($GLOBALS['TSFE']->config['config']['concatenateJs']) {
+                       $pageRenderer->enableConcatenateJavascript();
+               }
+                       // backward compatibility for old configuration
                if ($GLOBALS['TSFE']->config['config']['concatenateJsAndCss']) {
                        $pageRenderer->enableConcatenateFiles();
                }
                if ($GLOBALS['TSFE']->config['config']['concatenateJsAndCss']) {
                        $pageRenderer->enableConcatenateFiles();
                }